ラベル Unity の投稿を表示しています。 すべての投稿を表示
ラベル Unity の投稿を表示しています。 すべての投稿を表示

2013年1月2日水曜日

Unity メッシュ考察

メッシュの動的修正について色々と試していたら、プリミティブのCubeを書き換えてしまった。
以下にそれを修正したコードを記載する。

立方体のメッシュの情報のうち、ポリゴンの張りかたを指定する配列 triangles を書き換えてしまった。
verticesとuvsも上書きしてしまったが、配列の長さが違っていたため、幸運にも上書きされずに済んだ。
trianglesの長さには制限がないということが分かる。
もともとの長さは36だが、実験したところ36より大きい数でも問題なく入った。

MeshFilter mesh_filter = _target.gameObject.GetComponent<MeshFilter>() as MeshFilter;

Mesh mesh = mesh_filter.sharedMesh;

mesh.name = "Cube";
mesh.triangles = new int[36] {
0,2,1,1,2,3,
4,6,5,5,6,7,
8,10,9,9,10,11,
15,13,14,15,14,12,
19,17,18,19,18,16,
23,21,22,23,22,20};

MonoBehaviour.print(mesh.vertexCount);
MonoBehaviour.print(mesh.vertices.Length);

mesh.RecalculateNormals();

2012年9月19日水曜日

Unity&C#ではインターフェースが使えない?

 次のような状況を考える。
  • Aという動作を行えるオブジェクトと、Bという動作を行えるオブジェクトがある 
  • 重複したオブジェクトもある。
  • Bの動作の可否に関わらず、Aの動作が可能なオブジェクトを1カ所に格納し、それらに適宜、動作を呼び出す
  • Bも同様に格納し動作を呼び出す
Unityではコンポーネントの型による検索機能があるので、AとBをコンポーネント(MonoBeheaviorを継承したclass)とすることで、こういった要求に応えることができる。重複する場合には両方をゲームオブジェクトにアタッチすることで、分かりやすい解決となる。

 しかし、両方の動作を行える性質を持ちながら、それぞれ派生した動作を定義したいという要求が発生した場合どうするのだろうか。
 恐らくUnityの作法としては、Aの派生クラス、Bの派生クラスを定義し、2つをアタッチするのが正解なのだが、これではスクリプトファイルが一気に2つ増えてしまう。また性質を統合しているという意味を明確に示すためにも、1つのスクリプトとしてまとめたい。

 ところが、C#では多重継承が使えない。これは厄介なダイヤモンド継承と、それを回避するための複雑な手順を省きたいという考えからなのだろう。実際上記の例をそのまま多重継承できてしまうと、MonoBehaviorをA、Bそれぞれから2つ継承することになってしまう。

 今回はこういった経緯で...
C#の仕様である、インターフェースを使用して多重継承を行う方法を模索してみた。

 さて、前提条件としてA、Bというコンポーネントが存在した。コードとしては次のようになる。

// script file : ComponentA ------------------------
public class ComponentA : MonoBehavior
{
        public void funcA()
        {
                // do something
        }
}

// script file : ComponentB ------------------------
public class ComponentB : MonoBehavior
{
        public void funcB()
        {
                // do something
        }
}

 C#では多重継承が使えないが、インターフェースは多重継承が可能だ。しかも、1つのクラスと、複数のインターフェースを継承したクラスを定義することが許されている。

 まずA、Bそれぞれに対応するインターフェースを用意する。これはファイルを分ける必要はないので、それぞれのファイルにでも書き足せばいいだろう。

// script file : ComponentA ------------------------
        :
        :
public interface InterfaceA
{
        public void callA();
}

// script file : ComponentB ------------------------
        :
        :
public interface InterfaceB
{
        public void callB();
}

 インターフェースに定義されたメソッド宣言は、テンプレートでいうインターフェースのようなもの(謎)で、これを継承したクラスは同型、同名のメソッドを定義する必要がある。

 そしてUnity&C#で多重継承を模擬したクラスは以下のようなものだ。

// script file : DeriveAB ---------------------------
public class DeriveAB : MonoBehavior, InterfaceA, InterfaceB
{
        ComponentA implA;
        ComponentB implB;

        public void callA()
        {
                implA.funcA();
        }

        public void callB()
        {
                implB.funcB();
        }

        void Start()
        {
                implA = new ComponentA();
                implB = new ComponentB();
        }
}

 果てしなく、ださい。かつ幾つか致命的なトレードオフがあることを説明しなければならない。
  • このようにComponentA、ComponentBの実体をスクリプト内で生成した場合、2つのコンポーネントはUnityの検索対象に入らない。検索したい場合、スタート関数内でgameObjectにコンポーネントとしてアタッチしてやる必要がある。
  • Unityエディタはインターフェース型をインスペクターに表示しない。従って手動で参照を設定することができない。
  • スクリプト内ではインターフェース型を扱うことができるが、検索して得られるComponentAはDeriveABの基底クラスというわけではなく、ただの実体。多態性が存在しない。したがって、DeriveABを検索し、インターフェース型に変換した参照をとらなえれば、派生した動作を行うことはできない。
絶望的である。だが、それぞれのインターフェースの生成時に、別途用意した静的クラスの配列などに参照を格納することはできる。あるオブジェクトの子供のコンポーネントを検索、といった事をするには、相応の前準備が必要となるが、全検索だけなら許容範囲内の手間だろう。

 無い物ねだりだが、Unityにインターフェース型の検索機能がつけば万々歳である。もしくは、せめてインスペクターで表示してくれれば、細かな関連性の設定に使えるのではないだろうか。

 そのようなことがなければ、セオリー通りおとなしく派生クラスを2つ作り、お互いの参照を持たせるのが得策であるようだ。

2012年9月9日日曜日

Unity 複数のレイヤーマスク

 Unityでは全てのオブジェクトに対して、必ず1つレイヤーが指定されている。Unityでゲームを作るなら必ず使用することになるレイヤーの用途は、カメラのカリングマスクとしての使用だ。
 カリングというと「間引く」といった意味なので、名称に混乱するかもしれないが、カリングマスクに指定することで、カメラはそのレイヤーのオブジェクトを描画するようになる。

 カメラのセッティングをいじったことがあれば気付かれたろうが、カリングマスクには複数のレイヤーを指定することが可能だ。そしてこのカリングマスクの情報は全て1つのint値に保存される。
 レイヤーは31個まで追加することができるが、これらも全てintであり、ビットシフトによって生成される。順に
1 << 0
1 << 1
    :
    :
1 << 31
という具合である。10進数でみれば複雑に混ざり合った値に見えるが、2進数では全て異なる桁に1が入った値であるため、これらの足しあわせは全て固有の値を取ることが分かる。したがってint値1つから、31のレイヤーのうち、選択されたレイヤー全てを割り出すことが可能なのだ。

 例えばカメラが映し出すレイヤーから、スクリプトによって動的にレイヤーを追加、除外するには、以下のようにすることができる。

void Start()
{
    // push No.8 Layer & pop No.9 Layer
    camera.cullingMask += 1 << 8 - 1 << 9;
}