32
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[超初心者向け]UnityでのC#の基礎(3)配列

Last updated at Posted at 2016-10-13

Unityは「ゲームを簡単に作れるようにする」ソフトですが、他のプログラミング経験がないままで利用するとScriptが分からなくてつまずいてしまいます。ここでは、Unityで利用するC#に絞って、Scriptの仕組みを解説します。
Scriptのエディタで出てくる簡単な解説は以下で記していますので、一度読んでからこちらを参考にして下さい。

[超初心者向け]やっと納得、Unityを初めて触ると出てくるC#の何だあれの答え

ここでは少しずつ、Scriptを解説していきます。

#連続したオブジェクトを定義する配列

同じ型のものを複数で定義して、連続して同じ処理を施したり、値などを加えて順位を出すなど、配列を用いるとまとめて処理が可能になり、Scriptを簡単にできます。

#配列の基本
リファレンスの中で、複数の連続したデータを扱う場面があります。

GameObject.GetComponents
public Component[] GetComponents(Type type);
public T[] GetComponents();

引用元:[https://docs.unity3d.com/ja/current/ScriptReference/GameObject.GetComponents.html]
(https://docs.unity3d.com/ja/current/ScriptReference/GameObject.GetComponents.html)

このGetCompornentsは、コンポーネントの型を指定して(上記のT)、それに該当する自身のGameObjectにアタッチされたコンポーネントを取得するものです。
コードのサンプルでは、以下のように書いてあります。

HingeJoint[] hingeJoints;
hingeJoints = GetComponents<HingeJoint>();

この処理を実行すると、HingeJoint型のアタッチされたコンポーネントが、配列hingeJointsに格納されます。

##配列の定義

配列は、型と[]で定義します。

型[] オブジェクト名;

int[] i;  //int型の配列
float[] f;  //float型の配列
Vector3[] v; //Vecotor3型の配列
GameObject[] gameObjects; //GameObject型の配列

##初期化と要素数
配列に含まれるオブジェクトを要素と呼び、その数を要素数として定義します。上記の場合は要素数が無いので、それを後から配列が代入されることで数が決まります。要素数の変更は、決定後にできません

定義時に初期化

int[] i={10,20,30};

これで定義される配列は、以下になります。

参照 i[0] i[1] i[2]
要素 10 20 30

参照は以下の形で指標をしていします。

int sum =i[0]+i[1]+i[2]; //3要素の合計

要素数だけ定義すると要素数だけ確定しますが要素は未定義になります。

要素数だけ初期化

定義時に要素数だけ決めておきます、あとから要素の値は変えられます。

int[] a= new int[5];

a[0]=10;
a[3]=-1;

これで定義される配列は以下になります。不定の要素の値は、内容が保証されません。

参照 a[0] a[1] a[2] a[3] a[4]
要素 10 デフォルト指定値 デフォルト指定値 -1 デフォルト指定値

int型の場合、指定値は0になります。配列の型に依存し設定される値はかわります。
規定のデフォルト値は、こちらを参照してください。

[https://msdn.microsoft.com/ja-jp/library/83fhsxwc.aspx]
(https://msdn.microsoft.com/ja-jp/library/83fhsxwc.aspx)
参照型(classなど)の配列の場合は、未定義部分にはnullが入ります。

GameObject partyGameObject[5];

a[0] = GameObject.Find("MainPlayer");
a[2] = GameObject.Find("SubPlayer");

これで定義される配列は以下になります。不定の要素の値は、内容が保証されません。

参照 a[0] a[1] a[2] a[3] a[4]
要素 "MainPlayer"のGameObject null "SubPlayer"のGameObject null null

値型と参照型の違いは、こちらを参照して下さい。
[値型と参照型(MSDN)
https://msdn.microsoft.com/ja-jp/library/cc406735.aspx]
(https://msdn.microsoft.com/ja-jp/library/cc406735.aspx)

要素数不定で後から代入

後から配列をnew演算子で作成し、代入することが可能です。

        int[] a;
        a = new int[] { 8, -2, 4 };
参照 a[0] a[1] a[2]
要素 8 -2 4

##配列の処理

配列は連続したデータ型なので、まとめた処理をする場合に便利です。以下は、リファレンスにあるMeshコードの抜粋です。

    void Update() {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Vector3[] normals = mesh.normals;
        int i = 0;
        while (i < vertices.Length) {
            vertices[i] += normals[i] * Mathf.Sin(Time.time);
            i++;
        }
        mesh.vertices = vertices;
    }

引用元:Mesh
https://docs.unity3d.com/ja/current/ScriptReference/Mesh.html

このScriptはUpdate関数で配列verticesに入っているポリゴンの頂点を1秒間で1°、三角関数のsinで動かす物です。Meshコンポーネントを有するGameObjectならなんでも有効ですので試してみて下さい。
頂点を動かしている演算は、以下の部分です。

            vertices[i] += normals[i] * Mathf.Sin(Time.time);

normalsに入っているのが法線ベクトルで、長さが1になる単位ベクトルになっています。それを三角関数sinをかけているので、±1の距離で頂点が動きます。
頂点数がもし5つであれば、以下のコードでも代用できます。

            vertices[0] += normals[0] * Mathf.Sin(Time.time);
            vertices[1] += normals[1] * Mathf.Sin(Time.time);
            vertices[2] += normals[2] * Mathf.Sin(Time.time);
            vertices[3] += normals[3] * Mathf.Sin(Time.time);
            vertices[4] += normals[4] * Mathf.Sin(Time.time);

これで用意すると頂点数が変わった場合に対応が出来ません。それを可変長に捉えて処理するために、vertices.Lengthを用いています。

        int i = 0;
        while (i < vertices.Length) {
            vertices[i] += normals[i] * Mathf.Sin(Time.time);
            i++;
        }

vertices.Lengthは配列の要素数を取得するものです。変数iは0から要素数-1まで処理を実行します。配列の指標は0から要素数-1になるので、それに合せています。

この処理をforで書き換えると、以下のようになります。

        for(int i = 0; i < vertices.Length; i++) {
            vertices[i] += normals[i] * Mathf.Sin(Time.time);
        }

一方、冒頭で紹介したGameObject.GetComponentsのリファレンスでは以下のサンプルコードが示されています。

		HingeJoint[] hingeJoints;

		hingeJoints = GetComponents<HingeJoint>( );

		foreach( HingeJoint joint in hingeJoints )
			joint.useSpring = false;

引用元:GameObject.GetComponents
https://docs.unity3d.com/ja/current/ScriptReference/GameObject.GetComponents.html

このforeachは配列型等の特定の型に用いられる制御文で、この例では取得したHingeJoint型のコンポーネント(GameObjectを他のGameObjectに連結するコンポーネント)のそれぞれのuseSpringにfalseを代入する(バネのように伸び縮みする動作を無効化する)処理をしています。
これを前述のようにfor文で置き換えると、以下の用になります。

        for(int i = 0; i < hingeJoints.Length; i++) {
		hingeJoints[i].useSpring = false;
        }

配列の要素それぞれのみの参照で閉じた形で処理を行う場合、foreachの方が簡潔になります。詳しくは以下を参照して下さい。
[配列での foreach の使用 (C# プログラミング ガイド)
https://msdn.microsoft.com/ja-jp/library/2h3zzhdw.aspx]
(https://msdn.microsoft.com/ja-jp/library/2h3zzhdw.aspx)

#配列の特徴とまとめ

  • ここで紹介した配列は1次元配列で、Unityのリファレンスに登場するものはほぼこれですが、この他に多次元化した配列もC#では使えます。
  • 親子関係のようにしたジャグ配列を用いると、配列の要素が配列になり、各要素数がバラバラにできます。
  • 配列は単純な処理には向いていますが、長さを後から変えることが再定義するしか無いため、柔軟な処理が出来ない場合が多くあります。その場合はList型が役立ちます。配列に処理を施すメソッドが多数、用意されており、非常に強力です。配列で間に合う場合はそのままで、複雑な処理をしたい、用意されたメソッドを用いたい場合はList型にすると便利です。他にも配列処理が出来る型がありますが、現状だとほぼList型で間に合います。
  • List(T) クラス (System.Collections.Generic)
  • https://msdn.microsoft.com/ja-jp/library/6sh2ey19(v=vs.110).aspx

この記事は以下でシリーズしていますので、適宜、参照してください。
[UnityでのC#の基礎]
(http://qiita.com/JunShimura/items/f87c599b3738b804f605#unity%E3%81%A7%E3%81%AEc%E3%81%AE%E5%9F%BA%E7%A4%8E)

32
30
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?