講義メモ ・文法編:列挙 から ・ゲーム開発編:p.156 列挙型を利用して野菜の種類を表す型を作ろう から 文法編:列挙 ・列挙(enum):元は一連の整数値に名前を付けたもので、いくつかの種類を示す型として用いられている ・定義書式: enum 列挙型名 { メンバー, … }; ・定義のあるクラスの外側で用いる場合は「public」を前置する  例: public season { spring, summer, fall, winter }; ・列挙のメンバには内部的に0からの整数が順に振られている  上の例では、springには0、summerには1、fallには2、winterには3 ・この整数を列挙と共に用いる場合、必要に応じて整数値を変更できる ・列挙の整数値の変更例: enum month { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }; //月名の列挙 ・列挙のメンバを用いるには「列挙型名.メンバ」とする ・列挙のメンバの持つ整数値を用いるにはint型にキャストする ・列挙を型とする変数を定義できる(つまり、ユーザ定義型の変数になる) ・この変数にはメンバを代入でき、インクリメントなどの整数演算が可能 ・この変数をDebug.Logすると、メンバ名が表示される ・列挙を型とする変数の保持する整数がメンバ数を超えても良いが、この変数をDebug.Logすると、メンバ名の代わりに整数値が表示される ・列挙を型とする変数の保持する整数が負の数になったり、該当するメンバが定義されていない場合も同様 演習 ex0810a.cs 列挙の基礎 ・上記の例の列挙seasonを定義し、列挙を型とする変数を試そう 作成例 using UnityEngine; public enum season { spring, summer, fall, winter }; public class ex0810a : MonoBehaviour { void Start() { Debug.Log(season.spring); //"spring"を表示 Debug.Log((int)season.spring); //0を表示 season now = season.summer; //列挙を型とする変数を定義し、列挙メンバを代入(初期化) Debug.Log(now); //"summer"を表示 now++; //列挙を型とする変数は整数演算が可能 Debug.Log(now); //"fall"を表示 now += 10; //列挙を型とする変数の保持する整数がメンバ数を超えても良い Debug.Log(now); //メンバがないので、代わりに整数値(12)を表示 now -= 20; //列挙を型とする変数の保持する整数がメンバ数を超えても良い Debug.Log(now); //メンバがないので、代わりに整数値(-8)を表示 } } 文法編:列挙の例 ・UnityではKeyCodeなどが列挙型として提供されており、キーボードの全キーと、ジョイスティックのボタンなどが、メンバとして定義されている  https://docs.unity3d.com/ja/2021.2/ScriptReference/KeyCode.html ・例: Debug.Log((int)KeyCode.Escape); ⇒ 27 ・C#でもConsoleColorなどが列挙型として提供されており、デバッグコンソールで表示可能な色名が、メンバとして定義されている  https://learn.microsoft.com/ja-jp/dotnet/api/system.consolecolor 文法編:Unityと列挙 ・C#の文法としては、列挙の定義はクラスと同等なので、クラス定義の外に置くことが多い。 ・利用範囲がクラス内に限定される場合は、クラスの中に記述するとよい。  この場合「public enum」の「public」は不要(記述すると外部から利用可能になる) ・Unityの場合、MonoBehaviourの派生クラスが基本なので、p.157 Vegetable.csのように、派生クラス内に「public enum」として記述する ・これで、他のUnityで動作するクラスから利用可能になる ・また、列挙を型とする変数の定義もここで行うことができる 文法編:列挙の応用 ・バラバラの整数値をグループ化して列挙にまとめる場合も多い  例: enum player { height = 100, weight = 200, speed = 10, size = 50 }; ・「= 値」による指定は途中で行ってもOK  例: enum prime { two = 2, three, five = 5, seven = 7 }; //1桁の素数 ゲーム開発編:p.156 列挙型を利用して野菜の種類を表す型を作ろう ・① Assetsにある「tomato」をクリック ・②「Open Prehub」ではなく「Open」ボタンをクリック ・複数のコンポーネントが開いていて見づらければ「Sprite Renderer」「Regitbody2D」の▼をクリックして窓を閉じると良い ・③「Add Component」「New Script」をクリック ・④「Name」に「Vegetable」と入力し「Create and Add」をクリック ・⑤「Vegetable(Script)」の右の「:」「Edit Script」 ・⑥ Visual StudioでVegetable.csのソースを入力し「ファイル」「すべて保存」 ・⑦ Unityに戻る p.158(続き) ・①「Vegetable(Script)」の下にパブリック変数「Type」ができており、値が「Tomato」になっていること、▼を押すと4種類から選択可能なことを確認 ・②「Scenes」をクリックして編集完了 ・③ Assetsにある「Gpepper」をクリックし、パブリック変数「Type」を「Gpepper」に変更 ・④ Assetsにある「Broccoli」をクリックし、パブリック変数「Type」を「Broccoli」に変更 ・⑤ Assetsにある「Potato」をクリックし、パブリック変数「Type」を「Potato」に変更 ・⑥「Scenes」をクリックして編集完了 p.159(続き) ・① Assetsにある「Broccoli」をシーン上の適当な位置にドラッグ&ドロップ ・② Assetsにある「Gpepper」をシーン上の適当な位置にドラッグ&ドロップ ・③ Assetsにある「Tomato」をシーン上の適当な位置にドラッグ&ドロップ ・④ プレイボタンを押して動作を確認 ・⑤「File」「Save」で保存 ・⑥ シーン上に配置した野菜の4つのインスタンスをクリック&Deleteで削除する p.157 Vegetable.cs using UnityEngine; public class Vegetable : MonoBehaviour { public enum Type { //Type列挙型の定義(外部から利用可) Tomato, Gpepper, Broccoli, Potato //Type列挙型のメンバー } public Type type; //Type列挙型を型とする変数typeの宣言(外部から利用可) } 文法編:オブジェクトの配列 ・整数値や文字列の配列と同様に、オブジェクトの配列を生成・利用できる ・p.160 shooter.csの7行目では、Unityが提供するGameObjectクラスを型とする配列prefabsを要素数4で生成している  public GameObject[] prefabs = new GameObject[4]; ・定義と生成の書式: クラス名[] 配列名 = new クラス名[要素数]; ・クラスの代わりに構造体なども利用可能だが、扱いが異なるので注意 ・クラスは参照型なので、生成されるのは参照であるため、必要に応じてオブジェクトを生成して与える必要がある  例:for (int i = 0; i < 要素数; i++) { 配列名[i] = new クラス名(…); } ・構造体は値型なので、オブジェクトが同時に生成される(よって非常に要素数が多い場合は負荷が大きくなる) ・クラスや構造体に含まれるメンバを配列の要素から用いるには「配列名[添字].メンバ名」とする 演習 ex0810b.cs ・下記のMonsterクラス型の配列を要素数3で生成しよう class Monster { //インナークラスMonsterの定義 public int hp; //Monsterクラスの持つデータ public int mp; //同上 public void show() { //Monsterクラスの持つメソッド Debug.Log(hp + ", " + mp); //2変数の値を出力 } } ・各要素のhp、mpを設定し、showメソッドで表示して確認しよう 作成例 using UnityEngine; public class ex0810b : MonoBehaviour{ class Monster { //インナークラスMonsterの定義 public int hp; //Monsterクラスの持つデータ public int mp; //同上 public void show() { //Monsterクラスの持つメソッド\ Debug.Log(hp + ", " + mp); //2変数の値を出力 } } void Start() { Monster[] mons = new Monster[3]; //Monster型の配列monsを要素数3で生成(参照のみ) for (int i = 0; i < mons.Length; i++) { mons[i] = new Monster(); } //オブジェクトを生成して各要素に与える mons[0].hp = 10; mons[0].mp = 20; //各要素のメンバ変数に代入 mons[1].hp = 11; mons[1].mp = 21; //〃 mons[2].hp = 12; mons[2].mp = 22; //〃 foreach (var mon in mons) { //配列monsの全要素について繰返す mon.show(); //作業変数monはMonster型なのでMonsterクラスのメソッドを呼べる } } } 訂正「※ 以下確認中」としていた件について ・講義内で「C#では「Monster[] mons = new Monster[3];」でMonsterクラス型の配列が3要素で生成される」と説明しましたが、これは誤りでした。 ・C#でもUnityでも「for (int i = 0; i < mons.Length; i++) { mons[i] = new Monster(); }」を実行して、Monster型のオブジェクトを生成して配列monsに与える必要があります。 ・なお、Monsterをクラスではなく構造体にすると「Monster[] mons = new Monster[3];」だけでMonster構造体型の配列が3要素で生成されるので、例外は発生しません。 文法編:オブジェクトの配列と継承 ・派生クラスのオブジェクトは基本クラスを型とする配列の要素にできる ・ただし、こうすると、派生クラスで追加したデータメンバやメソッドは利用を制限される  ①基本クラス型の配列の要素としては基本クラスで定義されたメンバのみ  ②派生クラス型にキャストすると、派生クラスで追加したメンバも利用可能になる ・また、基本クラス型の配列の要素となった派生クラスのオブジェクトは、派生クラスにキャストすることで、派生クラス型の変数に代入できる ・以上により、多くの派生クラスを持つ基本クラスの配列で、派生クラスオブジェクトをまとめて扱うことができる 演習 ex0810c.cs ・下記のSlimeクラス型の要素をMonster型の配列に格納して試してみよう class Slime : Monster { //Monsterを継承するクラスSlimeの定義 public string name; } 作成例 using UnityEngine; public class ex0810c : MonoBehaviour{ class Monster { //インナークラスMonsterの定義 public int hp; //Monsterクラスの持つデータ public int mp; //同上 public void show() { //Monsterクラスの持つメソッド Debug.Log(hp + ", " + mp); //2変数の値を出力 } } class Slime : Monster { //Monsterを継承するクラスSlimeの定義 public string name; } void Start() { Monster veldra = new Monster(); veldra.hp = 100; veldra.mp = 200; Monster velgri = new Monster(); velgri.hp = 101; velgri.mp = 201; Slime rimuru = new Slime(); rimuru.hp = 102; rimuru.mp = 202; rimuru.name = "リムル"; Monster[] mons = {veldra, velgri, rimuru}; //Monster型の配列monsにSlimeオブジェクトも foreach (var mon in mons) { //配列monsの全要素について繰返す mon.show(); //作業変数monはMonster型なのでMonsterクラスのメソッドを呼べる } //Debug.Log(mons[2].name); //派生クラスで追加したメンバは使えないが Debug.Log(((Slime)mons[2]).name); //キャストすれば「リムル」が得られる Slime rimuru_copy = (Slime)mons[2]; //代入も可能 Debug.Log(rimuru_copy.name); //「リムル」が得られる } } 文法編:オブジェクトの動的な生成 ・プログラムの中でクラスや構造体からオブジェクトを動的に生成する場合、基本的にはnew演算子を用いる ・もう一つの方法は、クラスや構造体を戻り値型とするメソッドを呼ぶことで、返されるオブジェクトを、クラスや構造体を型とする変数で受け取れば良い ・Unityでは、これに加えてゲームオブジェクトの複製を生成するInstantiateメソッドがMonoBehaviourクラスにあり、Unity用のクラスに継承される ・よって、p.163の説明の通り、プレハブ名、位置情報、回転情報を与えて、ゲームオブジェクトの複製と出現を指示できる ゲーム開発編:p.160 Shooterにプレハブを渡す ・ゲームオブジェクトの配列を生成して用いる ゲーム開発編:p.162 マウスボタンの状態をチェックしてインスタンスを作る ・Unityが提供するInputクラスのGetMouseButtonUpメソッドに0を指定すると、マウスの左ボタンが「押されて戻された=クリックされた」かどうかがbool型で得られる ・Instantiateメソッドでゲームオブジェクトの複製を生成する ・プレハブ名、位置情報、回転情報を与えるので、仮にprefabs[0]を与えておく ・回転情報には「回転しない」ことを意味する Quaternion.identityプロパティを与える p.162 shooter.cs(途中での改良分を含む) //p.162 shooter.cs using UnityEngine; public class shooter : MonoBehaviour { float move = 0.05f; //単精度実数型のフィールド変数moveの初期化 public GameObject[] prefabs = new GameObject[4]; //ゲームオブジェクトの4要素の配列を生成 void Update() { Vector3 pos = transform.position; //構造体Vector3のオブジェクトposをプロパティを利して生成 pos.x += move; //オブジェクトposの持つ変数xにmoveの値を足し込む if (pos.x < -2.5f || pos.x > 2.5f) { //両端に来ていたら move = -move; //増分の符号を反転することで逆向きにする } transform.position = pos; //オブジェクトposをtransformプロパティに渡すと画面に反映 if (Input.GetMouseButtonUp(0)) { Instantiate(prefabs[0], pos, Quaternion.identity); } } } 提出:p.162 shooter 次回予告: ・ゲーム開発編:p.164 野菜の種類を順番に切り替える から ・文法編:プロパティの補足、クラスと構造体、など