読者です 読者をやめる 読者になる 読者になる

The Last of Us Remastered

年明けにPlaystationStoreでセールになっていて2500円くらいで買えたので
The Last of Us を買った。

The Last of Us Remastered 【CEROレーティング「Z」】 - PS4

The Last of Us Remastered 【CEROレーティング「Z」】 - PS4


進行型のゲームとして、
様々な面でクオリティが高い。

グラフィック、
アニメーション、
カットシーン、
音楽、
吹き替えだったがセリフの雰囲気まで含めて
なにからなにまで
素晴らしいので、
こんなゲームの開発もあるんだなーと。

バッテリーから伸びてる電源ケーブルなんかも
グニョングニョンしてる。
そんなところも粗さがない。

ストーリーも、プロローグでパンデミックが起こって
主人公の一人、ジョエルに悲劇が起こって、
これが可哀そうでね・・

そのあと、もう一人の主人公の少女エリーと行動をともにするようになって、
あー、まあ、どうせ先々こういう展開になるのかなーなんて予測しつつ、
最後まで、あまりだれずに進められた。
(そして、それほど予測したような安易な展開はそれほど描かれなかった)

バトルは、時々強制的なシューティングパートがあってストレスを感じることもあったけど、(下手だから)
ほとんどのケースは、
上手くステルスしていくスタイルも許容されているので楽しい。
特に弓矢は他の敵に気づかれずに使える飛び道具で、
使った矢も折れなければ回収できるので、
上手いプレイをしているという感覚があって良い。

ただ、ストーリー的に、
終盤からラストまでで、
うーん?っていう展開があり。

エリーを助ける助けるためにジョエルが人を殺しまくるという。
いや、それまでも強盗の類いは散々殺してきたんだけど、
一応人類のために活動してる組織の人たちだし、
ほぼ無防備の人なんかも殺しちゃって、
オイオイ…っていう。
ま、描写としては、そこに至るまでにも、
エリーに対する執着が変化していくのは描かれていたんだけど。
うーん、なんか最後のほうは感情移入としては微妙だったかなーっていう。
嫌な気分になって、さしてハッピーエンドでもないし、
もうちょい別の終わらせかた無かったのかねーなんて、
思ってしまった。

が、そんなことをぼんやり考えていたのだが。
1、2日ぼんやり考えていて、
ふと、あれって敢えてそうなんじゃないかって気がしてきた。
いや、敢えてなのは、敢えてなんだろうけど、
ストーリーのために、ああいうジョエルの行動になっていたのではなくて、
ゲームの、プレイヤーの気持ちを、
ジョエルから引き離すのが、
意図的だったのではなかろうか。

ジョエルとプレイヤーのリンクをブチ切って、
どこへプレイヤーを投影させるのか、
気持ちのリンク先を、
最後にエリーに持っていったのでは・・。

ラストシーンで、エリーはジョエルの話を疑って、
そしておそらく嘘を見破って、
言葉にはしないが、失望したようなそんな様子を見せる。
それと、ジョエルないわーっていう、
プレイヤー側の気持ちとが重なることを意図してるのでは。

もうちょい言うと、
ラストシーンの直前、
最後のプレイアブルシーンで、2人で山道を歩くのだけど、
このときの操作対象がエリーになっているのだよね。
それまで、2人が分かれている場面でエリーを操作することはあったけど、
2人一緒のときは、
ジョエルを操作することになっていた。
(おかげでこのシーンが始まったとき、あれ?ってなった)
これも意図的に設計されたことなのではないかな。

これ、ストーリーの流れと、
プレイ対象のスイッチというシステムとしての効果を、
重ね合わせるという、高度な、ゲームでしかできないことを表現しようと
トライしているのじゃないだろうか。
すごい。

うーん、どうだろ。
ちょっと好意的に深読みしているかもしれない。
2も作ってるようだし。(ジョエルも続投なようだし)

Zenjectに含まれてるサンプル その1

Unity DI Unity DI

kurihara-n.hatenablog.com

Zenjectについて前に書いたが、
アセットのパッケージ自体にサンプルが含まれているので、
それを見ていこうと思う。

サンプルは2種類あり、アセットをインポートしたままの状態であれば
Assets/Zenject/OptionalExtras
の下に
SampleGame1 (Beginner)
SampleGame2 (Advanced)
というフォルダがそれぞれある。

まず、SampleGame1(Beginner)から見ていく。
フォルダの中に、Asteroidsというシーンがあるので
このシーンを開く。

Runするとゲームが動きタイトル画面でクリックでゲームが始まる。
マウスで自機を動かし隕石を避けるゲーム。
f:id:kurihara-n:20170104223309p:plain

シーン構成

シーンの構成を見ていくと、
SceneContextがあり、ここにGameInstallerというInstallerスクリプトがあり、
Installersのリストに追加されている。
また、このSceneContextはScriptableObjectInstallerとして、
ゲームの設定が定義されているGameSettingsInstallerも設定してある。

Guiというオブジェクトからは、
GuiHandlerというコンポーネントがZenjectBindingsでBindされている。

Sceneというオブジェクトは、
背景と、ライト、カメラを持っている。特に触れることはない。

Shipというオブジェクトからは、
ShipというコンポーネントがBindされている。

Scriptable Object Installer

まず、
SceneContextのScriptable Object Installersに設定されているGameSettingsInstallerは、
ゲームの各種設定や、プレハブを持っているScriptableObjectになっている。
GameSettingsInstaller.csを見てみると
InstallBindings()にてそれぞれのインスタンスをBindingしている。
Container.BindInstance(****)というとこ。
これにより他の参照をしたいクラスで使うことができる。

Scriptable Object Installerのドキュメント
https://github.com/modesttree/Zenject#scriptableobject-installer

MonoInstaller

Bindingを行っているMonoInstallerであるGameInstaller.csを見ていく。
最初に、

        [Inject]
        Settings _settings = null;

という箇所があるが、
これがさっきのScriptableObjectInstallerでBindingした設定のインスタンスをとれるようにしているところ。
GameInstallerのSettingsはプレハブを持っている。

プレハブはBindFactoryでファクトリをBnidしており、よそでインスタンスを生成できるようにしている。

InstallAsteroids

            Container.Bind<ITickable>().To<AsteroidManager>().AsSingle();
            Container.Bind<IFixedTickable>().To<AsteroidManager>().AsSingle();
            Container.Bind<AsteroidManager>().AsSingle();

はAsteroidManagerをBindしている。
コメントにもあるが、

Container.BindAllInterfacesAndSelf<AsteroidManager>().To<AsteroidManager>();

と書くのとも一緒になる。

BindFactoryはScriptableObjectからプレハブを受けて、
Asteroidを生成できるようにわたしている。
WithGameObjectNameをつけると生成されるオブジェクトの名前を設定できる。
UnderTransformGroupは設定された名前のオブジェクトの子に設定していく。

InstallShip

最初に自機がクラッシュした時のSignalをバインドしている。

            Container.BindSignal<Signals.ShipCrashed>();

Signalはイベントと同じように使える。
https://github.com/modesttree/Zenject/blob/master/Documentation/CommandsAndSignals.md#signals

ちなみに、SignalのListenを残している状態でSignal自体のDisposeが走ると
Assertになってしまう。
Assertが書いてあるSignal1.csからSignal6.csというファイルに、
要らないならコメントアウトしてね、って書いてあるので気になるならコメントアウトしよう。

他にはStateFactoryと、各ステートのfactoryがBindFactoryされてる。
ステートは
ShipStateWaitingToStart
ShipStateDead
ShipStateMoving
Stateを使っているのはShipオブジェクトだが、
これはシーンに配置されていて、ZenjectBindingによってBindingされている。

InstallMisc

GameControllerをはじめ、
もろもろのクラスのバインディングを行っている。
爆発などのプレハブもBindFactoryしていて、
Ship死亡時のステートからcreateが呼ばれている。

InitExecutionOrder

処理順の制御を行っている。
これはUnityのExecution Orderと同じように使えるものと思っておけばいい。
https://github.com/modesttree/Zenject#update--initialization-order

Zenjectの導入

Unity DI

UnityのためのDependency Injection フレームワークにZenjectというものがある。
つい最近知ったものだけれども、
使えるシーンが多いと思うので現状把握している範囲でまとめておこうと思う。

Github
GitHub - modesttree/Zenject: Dependency Injection Framework for Unity3D

Asset store
https://www.assetstore.unity3d.com/jp/#!/content/17758

知ったきっかけはこちらのAmingさんの開発者ブログ
Unity3DのDIフレームワーク、Zenjectの紹介 | Aiming 開発者ブログ
via Unityまとめ

Unityを日頃使っていて、
なんとなく感じている痒いところの解決の助けになりそうに思っていて、
例えば、どうしてもシングルトンを使ってしまっているところであるとか、
管理用のゲームオブジェクトがシーンに配置されていて、
SerializeFieldに必要なオブジェクトを配置していて、それがいつの間にか外れていて
実行時にExceptionで発覚してギャフン、みたいなのを
軽減させる手段に成りえるかと感じた。

Introduction

GithubのIntroductionのおおまかな訳。

ZenjectはUnityのための、軽量なDependency Injectionフレームワークです。
(ただUnity以外で使うこともできます)
これは貴方のアプリケーションを、
とても分断されたレスポンシビリティをもつ疎結合なパーツの集合にすることができます。
Zenjectは、スケーラブルで最高にフレキシブルな方法で、
容易に書けて、再利用でき、リファクタ、テストできる多くのコンフィグレーションにより、
各パーツをのりづけ(関連付け)できます。

すごそうな雰囲気だが、
まだこれだけだと良く分からないかも。

Dependency Injection

そもそもの話として、
僕はDIコンテナというものは使ったことがなく、今回初めて触れた。
なので、Zenjectの機能の範囲でDIの領域からはみ出ている部分に触れていることもあるかもしれない。
また、DIとしてのベストプラクティスを踏み外している部分や、
やや的を外しているかも。

DIについて調べた確認したページなども書いておく。
DIコンテナの本当の使いどころ | 技術トピックス | ウルシステムズ株式会社
2005年なので随分前の記事。DIの適さない使い方などにも触れられている。
分野によっては前から普通に使われていたのだなと、
自分は不勉強だったなと思った。

c# - DIコンテナを使うメリットが分からない - スタック・オーバーフロー
StackOverflowで、回答がわかりやすいなと。

依存性の注入 - Wikipedia
Wikipedia
上記のStackOverflowにもリンクがあった。
DIという用語を作りだしたのはMartin Fowler、そうだったのか。

Inversion of Control Containers and the Dependency Injection pattern
Martin Fowlerのブログ。
検索したらちゃんと出てきた。
2004年の記事。


ある程度見ていくと、既存のDIについてはJavaphp
C#もあるけど、
Unityというゲームエンジンで動作するフレームワークとは
少し前提条件が違うようにも思うが、
パターンとして考え方を整えるために色々読んでおくのは良いかと。

また、ZenjectのReadmeにも、
DIを使う理由については行数を割いて書かれていて、
https://github.com/modesttree/Zenject#theory
https://github.com/modesttree/Zenject#misconceptions
のあたりで書かれている。

サービスを使うにあたって、
直に生成するよりかはコンストラクタなどでインターフェースを受けるほうが柔軟で、
それをさらに推し進めると、その依存性の解決は最初にやっちゃえばいいよね、
という感じだろうか。ざっくり言うと。

Pokémon Goでの事例

ZenjectはポケモンGoでも採用されている。
Unite LA 2016でのトークの動画が上がっていて、
全般的にDIを採用したアーキテクチャについて話している。
www.youtube.com

  • Unityを長く使っていて、いつもとっ散らかっちゃうけど今回はすごくきれいにできた
  • 既存のゲームデザインとは異なるため多くのイテレーションを必要とし、柔軟なアーキテクチャが必要だった
  • Testについて。
  • ゲームステートごとにInstallerがある。
  • InstallPrefabというAttributeを別途作成・使用している
  • Q&Aにて、パフォーマンスに関してはキャッシングが効いてるから大丈夫と思う、みたいなことを言っている

英語なので、少し怪しいが。

サンプル

まず Hello World Exampleを試してみる。

https://github.com/modesttree/Zenject#hello-world-example

ヒエラルキービューで右クリック 
Zenject -> Scene Context
でSceneContextオブジェクトを生成、配置する。
f:id:kurihara-n:20161227000604g:plain

Projectビューの右クリックからCreate -> Zenject -> MonoInstaller
でInstallerとなるスクリプトを作成する。
ファイルダイアログが開くので、TestInstaller.csという名前で保存する。
f:id:kurihara-n:20161227001830g:plain
この作ったTestInstaller.csに、
ZenjectのReadmeに載っているコードをコピペか、同じように記述する。

TestInstallerをシーンに配置する。(ここではSceneContextにAddComponentしちゃう)
Scene ContextのInstallerプロパティに、
TestInstallerを追加する。
f:id:kurihara-n:20161227001853g:plain

エディタ実行をすると、ログにHello Worldと出力される。
また、Ctrl + Shift + Vで、バリデーションがされて、
問題があればエラーを出してくれる。

オブジェクトの生成

いくつかシンプルなケースをいくつか試してみる。
プレハブをもとにオブジェクトを生成したい場合、
従来であればInstantiateを呼び出すことになるが、
ZenjectではFactoryをBindすることができる。

ここではSceneContextにSerializeFieldでプレハブを受けるInstallerを用意する。
f:id:kurihara-n:20161228012822p:plain

サンプル同様にInstallerをSceneContetのプロパティに設定しておく。
Installerのコードは以下のようになっている。

using UnityEngine;
using Zenject;

public class InstancingInstaller : MonoInstaller<InstancingInstaller>
{
    [SerializeField]
    GameObject cubePrefab;

    public override void InstallBindings()
    {
        Container.BindFactory<Cube, Cube.Factory>().FromPrefab(cubePrefab);
        Container.Bind<ITickable>().To<InstancingManager>().AsSingle();
        Container.Bind<InstancingManager>().AsSingle();
    }
}

FromPrefabからFactoryをBindしている。

cubePrefabはCubeコンポーネントを持っていて、そのなかでFactoryを定義している

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Zenject;

public class Cube : MonoBehaviour
{
    public class Factory : Factory<Cube>
    {
    }
    
	void Start () {
        //...
	}
	
	void Update () {
		//...
	}
}

ITickableを実装したInstancingManagerというクラスからFactoryを使う。
Instancingmanagerは以下。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Zenject;

public class InstancingManager : ITickable
{
    Cube.Factory factory;

    public InstancingManager(Cube.Factory factory)
    {
        this.factory = factory;
    }

    void ITickable.Tick()
    {
        //if(someCondition)
        //{
        //factory.Create();
        //}
    }
}

factory.Create()を呼べば、オブジェクトが生成される。

ScriptableObjectInstaller

Readmeに書いてあるものからスクリプタブルオブジェクトの利用について。
https://github.com/modesttree/Zenject#scriptable-object-installer

Scriptable Object InstallerでScriptable Objectを利用できる。
右クリックからCreate -> Zenject -> Scriptable Object Installer
ファイル保存ダイアログがでるので、任意の名前をつける。(この例だとGameSettingsInstaller)
このScriptableObjectInstallerはScriptableObjectを継承しているので、
エディタで設定したいプロパティはここに持たせる。
エディタスクリプトにより、このScriptableObjectが生成できるようになっており、
Create -> Installers -> ScriptableObjectInstaller
で作る。
これをSceneContextに追加れば使用できる。
BindはContainer.BindInstance()で行う。

Scene Bindings

これもReadmeにあるものから。
https://github.com/modesttree/Zenject#scene-bindings

すでにシーンに配置してあるオブジェクト/MonoBehaviourを、Bindingさせる方法。
Unityだとシーンに配置させるケースは多々あるので、
それをBindingして受けれるようにする方法。

オブジェクトにZenject Bindingコンポーネントをアタッチし、
Componentsに、そのオブジェクトが持っているBindしたいコンポーネントを追加。

これだけで、[Inject]であったり,ITickableのコンストラクタなりで受けることができる。
[Inject]はListで受けることもできるので、
Amingさんの開発ブログの例もそうだが、
シーンに配置したいくつかのオブジェクトにZenjectBindingを設定しておくと、
オブジェクトのリストが実行時に作られている状態になる。

これは便利。
同じようなことをやるときにオブジェクトのStartなりでマネージャに渡したりしていたのが手間が省けるし、
場合によってはGameObject.FindObjectsOfTypeなどで取得をしたりしていたものが、
このリストでとれるようになる。
まあ、動的にも生成されるようなものはPrefabから生成したさいに別途登録するような使い方にはなると思う。



いくつかのケースを書いてみた。
ITickableなどを用いていると、
Unityの標準のUpdateと別にタスクが回るようなものなので、
従来の使いかたに慣れ切っていると、
少し導入に障害を感じるかもともおもう。
ただ、色々とスッキリかけるので利点は感じる。
もっと使っていきたいと思う。

利用者が増えて日本語でも情報が増えるとうれしい。

環境設定2016

新しい職場で働くこととなったので、

前の職場から引き続き使いたいツールのメモ。 

IDEなどは仕事内容によって変わってくるので割愛。

 

Chrome: extensions

iKnow popup dictionary

great suspender

hatena bookmark

checker plus for gmail

 

ペイントツール

Painter.net

 

キャプチャ用

ScreenToGif

 

ポモドーロ用

Tomaghy

Transform.positionの代入とCS1612エラーで色々考えた

Unity

UnityでC#のプログラムを書いていて、
ひと月に一回くらいウッカリ遭遇するエラーがあって、

transform.position.x = 10.0f;

とか書いたときに、

error CS1612: Cannot modify a value type return value of `CubeBehaviour.AAA.position'. Consider storing the value in a temporary variable

という。
正しく書くのは単にpositionのx,y,zは直接代入しないで、
new Vector3したものをpositionに入れればよい。

たまに忘れたころにエラー出てきて、おっとっと。みたいな。
僕がボケてるだけかもしれないけど、
ちょいちょい引っかかるので、
これなにがどうしてこうなってんの、どうにかなんないのっていうのを考えてみた話(たいしたオチはない)

エラー自体の意味するところは以下のものとなる。
Compiler Error CS1612


プロパティを介して代入しようとしてもダメだよ、プロパティは実体はメソッドだから。
ということである。

なにがプロパティなのかというとpositionなわけで、
定義を見てみると、

namespace UnityEngine
{
public class Transform : Component, IEnumerable
{
...
public Vector3 position { get; set; }

となっている。

プロパティでエラーになるっていうのがどういうことか、
もうちょっと分かりやすくpublic変数と比べてみると、

class AAA
{
public Vector3 position { get; set; }
}

class BBB
{
public Vector3 position;
}


void Start()
{
AAA a = new AAA();
a.position.x = 1.0f; // error: CS1612

BBB b = new BBB();
b.position.x = 1.0f;
}

AAA,BBBという2つのクラスに、
外から見て同じように扱えるpositionというものがあるが、
これは上のAAAのpositionへの代入だけエラーが出る。
プロパティだから。

ここで、じゃあTransformのpositionもpublicな変数にしちゃえば良いじゃない、
とか一瞬思ってしまったわけだが、
まあ、そうはなかなかいかないというものだと思う。

内部の詳細は当然分からないけど、
Transformの内部処理、エンジン側としてはオブジェクトの情報に変更があったということは知っておかなきゃいけない。
単純に座標の更新があったというだけすればいいわけじゃなくて
dirtyフラグを立てるであるとかして、
可視判定の更新をかけるかであるとか、
最適化で動いてないものは処理をスキップするとか
物理のほうでも位置の参照をしているであるとか、もしくは衝突判定とか、
親のTransformのほうでもAABBの更新するとかどうとか。とかとか。
妄想だけど、まあ色々やることはありそう。
なので実体はメソッドであるプロパティでないと都合は悪いのだろう。

なので解決方法としては、
"自分がしっかりする"、しかない。

まあ、それでもなにか少しできることであれば
拡張メソッドを用意しておいて、
そちらを使うことを習慣にしておく、
とかがひとまず打てる手かな、、と。

using UnityEngine;
using System.Collections;

public static class TransformExtension
{
    public static void SetPosition(this Transform self, float x, float y, float z)
    {
        self.position = new Vector3(x, y, z);
    }

    public static void SetPositionX(this Transform self, float x)
    {
        self.position = new Vector3(x, self.position.y, self.position.z);
    }

    public static void SetPositionY(this Transform self, float y)
    {
        self.position = new Vector3(self.position.x, y, self.position.z);
    }

    public static void SetPositionZ(this Transform self, float z)
    {
        self.position = new Vector3(self.position.x, self.position.y, z);
    }
}

例えばこんな風に。
座標のxだけ更新したい場合とかもちょっとスッキリ書けるようになるし。
transform.SetPositionX(10.0f);
とか。
メソッド名は微妙だが。

ゲームメカニクス おもしろくするためのゲームデザイン

ゲームメカニクス おもしろくするためのゲームデザイン (Professional Game Developerシリーズ)

ゲームメカニクス おもしろくするためのゲームデザイン (Professional Game Developerシリーズ)

買ったのはずいぶん前で、一度目を通していたと思うけど、
読み直した。
前回は途中でツールの説明になったあたりから後、飛ばし読んでしまったんだと思う。

サブタイトルで割とゲームデザイン全般に対する本のような、
アクションゲームはこんなのがあってー、STGはこうでー、
のような印象を受けるんじゃないかなって思うけど、
実際には、ゲーム内のリソースと内部経済・フィードバックループでの循環について
踏み込んだ内容になっていて、
著者のフレームワーク「Machinations」の紹介、ツールの紹介と、
それをつかったパターンについて書かれてる本、かな。
内部経済を表現できる適応範囲は広いので、
実際、パックマンのようなものから、RTSのようなものまで話題に上がってて、
ゲームデザインを考えるのに、
Machinationsを使うかどうかは置いておいても、
リソース・フィードバックループを分かっておくと良さそう。

ツールはFlash製なので、あんまり使い良くはない。
公式のwikiとかで動いてなかったりするし。
Android上で動作するものがあると、合間時間でもいじれてよいかも。

公式のページはこちらにあるので、とりあえず触るのにはここから。
wikiなんかへのリンクもあるし、
ツールのソースはgithubにあってそこへのリンクもある。
Machinations: Game Feedback Diagrams

フィードバックについての話題はMarc LeBlancさんのトークと合わせて紹介されていて、
スライドはここからアクセスできる。
The collected game design rants of Marc LeBlanc

GDCのサイトに、Machinationsに関してのセッションの動画があって動画がここから見れる。
www.gdcvault.com

日本語のスライドとしてはこちらを最初に見ると分かりやすいです。
Machinationの紹介

絵でわかる人工知能

絵でわかる人工知能 明日使いたくなるキーワード68 (サイエンス・アイ新書)

絵でわかる人工知能 明日使いたくなるキーワード68 (サイエンス・アイ新書)

CEDECで売られていたので購入。
ちょっとずつ読んだり、気になる章から読んだりしていたが、
人口知能に関わる各トピックごとに書かれているので、
どこから読んでも大丈夫。

森川さんのイラストも可愛くて、
何回も読み返せるし、
一回読んで終わりという本ではなくて、
気になったときに何回も読み直すような本かなって思う。

人口知能に関する広大なトピックを
俯瞰でみて広く全体をカバーしており、
この膨大な知識に基づいてかかれているのは素晴らしい。

著者である三宅さんがゲームAIのスペシャリストなので、
ゲームAIに関わるトピックも多く含まれており、
ゲーム開発者であれば読んでいて損はないはず。