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

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のための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で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に関わるトピックも多く含まれており、
ゲーム開発者であれば読んでいて損はないはず。

カラーパレットを使ったSprite

f:id:kurihara-n:20160926233843g:plain

スプライトを用いたアイテムやキャラクターなど、
パレットを変えるだけで色替え出来れば、
カラーバリエーション分のカラーテクスチャを持つより、
容量節約できていいよねってことで
試してみた実装。
(ただし、ちょっと筋が悪いって思っている方法)

GitHub - kuriharaan/ColorPalette: Color palette experiment for sprites

やり方
パレットを作りたいテクスチャを選択し、
エディタスクリプトを実行すると、
テクスチャの各ピクセルを順に調べ、
カラーのリストを作り、
新しくソーステクスチャと同じサイズのアルファテクスチャ(Alpha8)を作って、
インデクスを格納していく。

描画には、アルファテクスチャをスプライトとして設定し、
パレットを参照するためのシェーダを持ったマテリアルを割り当てる。

Unity5.4からシェーダの配列パラメータの設定がスクリプトで出来るようになったらしい。
Unity - Scripting API: Material.SetColorArray
など。
スクリプトでは、このSetColorArrayを使ってテーブルを設定するようにしている。

シェーダではテクスチャのPixelからアルファ値をとり、
color 配列のインデクスとして使用するようにしている。

一応、これで望む挙動はしているが、
いくらか微妙な点がある。

・カラー情報を別途シリアライズしないといけない(今は初期値を決めうち)
・エディタでプレビューできない

逆に、よいところはスクリプトからカラーを設定すれば、
なんでもカラーを指定できるところ。

少し違ったやり方でパレットはテクスチャで持って、
スプライトのテクスチャに加えて
もう一段マテリアルに、パレットテクスチャを
指定する方法がある。
アセットストアにあるアセットのひとつに
OldSkool Palette Sprites
https://www.assetstore.unity3d.com/jp/#!/content/8649
というものが実はあって、
紹介動画を見ると、
パレットテクスチャを作る方法をとっている。
OldSkool Palette Sprites Tutorial - YouTube

こちらのほうが現実的な、運用に良い方法に思う。


追記。
仕組み上、アルファテクスチャはポイントサンプリングにする必要があるので、
そういうビジュアルで大丈夫な用途に限ったほうが良さそう。
これは紹介しているアセットも同じで。