講義メモ ・ゲーム開発編:p.169 Chapter 5「ゲームを仕上げよう」から ・文法編:インスタンスとコンストラクタとデストラクタ、など 演習フォロー ex0824c.cs ・class(クラス)は参照型なので、クラスを型とする変数間の代入では、参照が代入され、  2つの変数が同じオブジェクトを扱うようになることを確認しよう ・struct(構造体)は値型なので、データのコピーが発生することを確認しよう ・Slimeクラスと、Dragon構造体を定義し、双方にpublicなint型の変数hpを記述する ・Slimeクラスのオブジェクトrimuruのhpを100にし、rimuruをSlimeクラスのオブジェクトslalinに代入 ・Dragon構造体のオブジェクトveldraのhpを200にし、veldraをDragon構造体のオブジェクトvelgreに代入 ・slalinのhpを300にすると、rimuruのhpも300になることを確認しよう(別名なので) ・velgreのhpを400にしても、veldraのhpは200のままなことを確認しよう(コピーなので) 作成例 using UnityEngine; public class ex0824c : MonoBehaviour{ class Slime { //Slimeクラス public int hp; } struct Dragon { //Dragon構造体 public int hp; } void Start() { Slime rimuru = new Slime(); rimuru.hp = 100; Slime slalin = rimuru; //slalinへrimuruの持つ参照を代入 slalin.hp = 200; //rimuruとslalinは同じオブジェクト Debug.Log(rimuru.hp); //だから、rimuru.hpが書き換わる Dragon veldra = new Dragon(); veldra.hp = 300; Dragon velgre = veldra; //velgreへveldraのコピーを代入 velgre.hp = 400; //veldraとvelgreは異なるオブジェクト Debug.Log(veldra.hp); //だから、veldra.hpは書き換わっていない } } 文法の補足: プロパティ+配列に似た仕組みのインデクサ ・クラスや構造体には、プロパティと同じ位置づけでインデクサを定義できる ・インデクサを定義してあると、そのオブジェクトに含まれるデータを配列と同様に扱える ・この配列は、オブジェクト名[インデクス] で扱えるので、見かけ上、オブジェクトの配列に見える ・配列よりも柔軟な仕掛けで(配列のインデクスは整数のみの添字だが)インデクスに実数や文字列が使用できる ・Unityが提供するクラスや、構造体にもインデクサが定義されているものがあり知ってくと便利 ・例: Vector3構造体(p.144)の場合:  Vector3 v3 = new Vector3(1.1f, 2.2f, 3.3f); //x,y,z座標を指定してオブジェクトを生成  Debug.Log(v3.x); //x座標を表示  Debug.Log(v3[0]); //インデクサを用いてx座標を表示  for(int i = 0; i < 3; i++) Debug.Log(v3[i]); //インデクサを用いてx,y,z座標を表示 演習 ex0831a.cs ・上記のVector3の例を用いて、xyz座標を一律に3倍し、その結果を表示してみよう ・なお、Vector3オブジェクトそのものをDebug.Logすると、xyz座標が列記される 作成例 using UnityEngine; public class ex0831a : MonoBehaviour{ void Start() { Vector3 v3 = new Vector3(1.1f, 2.2f, 3.3f); //x,y,z座標を指定してオブジェクトを生成 Debug.Log(v3); for(int i = 0; i < 3; i++) { v3[i] *= 3.0f; //インデクサを用いてx,y,z座標を一律で3倍する } Debug.Log(v3); } } 文法の補足: プロパティ+配列に似た仕組みのインデクサ(続き) ・自前のクラスや構造体にインデクサを定義できる ・ただし、インデクサには名前がないので「戻り値型 this[インデックス型 仮引数] {}」となる ・そして、中に、プロパティと同じ形式で get{} set{} のどちらかまたは両方を記述する」 ・「インデックス型 仮引数」を複数定義することで、多次元インデクサを記述できる ・なお、インデクサもオーバーロードが可能で、インデクス型や、インデクス数が異なるインデクサを複数記述できる ・例:Monster831クラスに下記を記述  private static string[] names = {"リムル", "ヴェルドラ", "ヴェルグリ", "シュナ"};  public string this[int i] { get { return names[i]; } //インデクスが整数型で文字列を返すインデクサ ・すると、下記のように利用できる  Monster831 mon = new Monster831();  Debug.Log(mon[1]); //"ヴェルドラ" ・また、インデクスが文字列型で整数(添字)を返すインデクサを追記してオーバーロードできる  public int this[string s] { get { return …; } //インデクスが文字列型で整数を返すインデクサ ・すると、下記のように利用できる  Debug.Log(mon["シュナ"]); //3 演習 ex0831a.cs ・上記のMonster831の例を追記して、インデクサの定義と利用を試そう ・なお、インデクスが文字列型で整数を返すインデクサでは、名前を指定すると添字を返すとする。見つからない名前の場合は-1を返すこと。 作成例 using UnityEngine; public class ex0831a : MonoBehaviour{ class Monster831 { private static string[] names = {"リムル", "ヴェルドラ", "ヴェルグリ", "シュナ"}; public string this[int i] { //インデクスが整数型で文字列を返すインデクサ① get { return names[i]; } //引数を添字として要素値の文字列を返す } public int this[string s] { //インデクスが文字列型で整数を返すインデクサ② get { for (int i = 0; i < names.Length; i++) { //全要素について繰返す if (names[i] == s) return i; //要素値の文字列と引数が一致したら添字を返す } return -1; //一致するものがない場合の処理(文法上必要) } } } void Start() { Vector3 v3 = new Vector3(1.1f, 2.2f, 3.3f); //x,y,z座標を指定してオブジェクトを生成 Debug.Log(v3); for(int i = 0; i < 3; i++) { v3[i] *= 3.0f; //インデクサを用いてx,y,z座標を一律で3倍する } Debug.Log(v3); Monster831 mon = new Monster831(); Debug.Log(mon[1]); //インデクサ①を用いて"ヴェルドラ"を表示 Debug.Log(mon["シュナ"]); //インデクサ②を用いて3を表示 } } p.168 C#にも「インスタンス」がある ・p.160で下記を追記してゲームオブジェクトの4要素の配列を生成している  public GameObject[] prefabs = new GameObject[4]; ・だが、GameObjectはクラスであるため、本来は、この1行のみではオブジェクトへの参照の配列(オブジェクトのない配列)しか生成されない ・しかし、p.168下から3行目に説明がある通り、Unityが提供するMonoBehaviour継承クラスにおいては、  Unityのシステムによって、自動的にオブジェクトが生成される ・よって、通常のクラスの場合と、Unityが提供するMonoBehaviour継承クラスの場合で、仕様が異なることに注意を。 ・通常のクラスでは「クラス名 変数名 = new クラス名(…);」でオブジェクトを生成する必要がある。 ・この「new クラス名(…)」は内部的にコンストラクタを呼び出している ・クラスを定義すると「引数のない、中身のないコンストラクタ」が自動的に用意されており,「クラス名(…)」で呼び出される ・よって「引数のない、中身のあるコンストラクタ」を自前で定義することで「new クラス名(…)」の実行時に呼び出させることが可能 p.168 C#にも「インスタンス」がある:コンストラクタ ・コンストラクタは名前と戻り値がない特殊なメソッドで「new クラス名(…)」でしか呼び出せない ・よって、主に、初期処理(データの初期化、準備処理の実行)を記述するために用いる「 ・これは、オブジェクトの生成と初期処理をくっつけることで、初期処理の実行漏れを防ぐ効果がある ・「public クラス名() {コンストラクタの内容} 」とすることで、「引数のない、中身のあるコンストラクタ」を自前で定義できる ・例:Monster831クラスに下記を追記  public Monster831() { Debub.Log("モンスターオブジェクトを生成した"); } ・すると「new Monster831()」の実行時に上記が呼び出される 演習 ex0831a.cs(続き) ・上記のMonster831の例にコンストラクタを追記して動作を確認しよう ・なお、コンストラクタ、データメンバ、メソッド、プロパティ、インデクサの記述順序は自由(チームルールによっては順序を決めている場合もある) 作成例 using UnityEngine; public class ex0831a : MonoBehaviour{ class Monster831 { public Monster831() { //引数のないコンストラクタの自前の定義 Debug.Log("モンスターオブジェクトを生成した"); } private static string[] names = {"リムル", "ヴェルドラ", "ヴェルグリ", "シュナ"}; public string this[int i] { //インデクスが整数型で文字列を返すインデクサ① get { return names[i]; } //引数を添字として要素値の文字列を返す } public int this[string s] { //インデクスが文字列型で整数を返すインデクサ② get { for (int i = 0; i < names.Length; i++) { //全要素について繰返す if (names[i] == s) return i; //要素値の文字列と引数が一致したら添字を返す } return -1; //一致するものがない場合の処理(文法上必要) } } } void Start() { Vector3 v3 = new Vector3(1.1f, 2.2f, 3.3f); //x,y,z座標を指定してオブジェクトを生成 Debug.Log(v3); for(int i = 0; i < 3; i++) { v3[i] *= 3.0f; //インデクサを用いてx,y,z座標を一律で3倍する } Debug.Log(v3); Monster831 mon = new Monster831(); //自前のコンストラクタを呼ぶ Debug.Log(mon[1]); //インデクサ①を用いて"ヴェルドラ"を表示 Debug.Log(mon["シュナ"]); //インデクサ②を用いて3を表示 } } p.168 C#にも「インスタンス」がある:引数のあるコンストラクタ ・引数のあるコンストラクタを定義できる ・引数のあるコンストラクタであれば、構造体にも定義できる  ※ 構造体にも引数のないコンストラクタが自動的に用意されるが変更できない ・テキストp.168にあるように「new クラス名(実引数, …)」とすることで、引数の数と型が一致するコンストラクタが実行される  例: Vector3 v3 = new Vector3(1.1f, 2.2f, 3.3f);  ⇒Vector3構造体のコンストラクタ(float, float, float)が呼び出される  ※Vector3構造体のコンストラクタ(float, float, float)にはデフォルト実引数が定義されているので、  引数を省略すると0.0fが指定されたとみなされる ・よって、コンストラクタもオーバーロードが可能で、引数型や、引数の数や並びが異なるコンストラクタを複数記述できる ・例:Monster831クラスに下記を記述 private string msg; public Monster831(string s) { //string型の引数を1個もつコンストラクタの自前の定義 Debug.Log(msg = s); } ・すると「new Monster831(文字列)」の実行時に上記が呼び出される p.168 C#にも「インスタンス」がある:デストラクタ ・自前のクラスや構造体のオブジェクトはC#によって管理され、まったく利用されなくなった時点(通常はプログラムの終了時)で自動的に消去される ・この仕掛けをガベージコレクションといい、プログラム側からはリクエストできるが制御は不可で、いつ実行されるかはわからないが、確実に実行される ・この消去時に実行される特殊なメソッドがデストラクタで、名前、戻り値、引数はなく、自動的に中身が空のデストラクタが用意される ・よって、確実に実行してほしい後処理(リソースの後始末など)の記述に用いることがある ・「~クラス名() {デストラクタの内容} 」とすることで、「中身のあるデストラクタ」を自前で定義できる ・例:Monster831クラスに下記を追記  ~Monster831() { Debub.Log("消去された"); } ・すると、プログラム終了時に上記が呼び出される 演習 ex0831a.cs(続き) ・上記のMonster831の例に引数付きのコンストラクタと、デストラクタを追記して動作を確認しよう ・Unityの場合、通常、プレイボタンの2度目のクリックにより、プログラムが終了する直前にオブジェクトが破棄されるので、デストラクタが動作する 作成例 using UnityEngine; public class ex0831a : MonoBehaviour{ class Monster831 { public Monster831() { //引数のないコンストラクタ①の自前の定義 Debug.Log("モンスターオブジェクトを生成した"); } private string msg; public Monster831(string s) { //string型の引数を1個もつコンストラクタ②の自前の定義 Debug.Log(msg = s); } ~Monster831() { //デストラクタ Debug.Log(msg + ":消去された"); } private static string[] names = {"リムル", "ヴェルドラ", "ヴェルグリ", "シュナ"}; public string this[int i] { //インデクスが整数型で文字列を返すインデクサ① get { return names[i]; } //引数を添字として要素値の文字列を返す } public int this[string s] { //インデクスが文字列型で整数を返すインデクサ② get { for (int i = 0; i < names.Length; i++) { //全要素について繰返す if (names[i] == s) return i; //要素値の文字列と引数が一致したら添字を返す } return -1; //一致するものがない場合の処理(文法上必要) } } } void Start() { Vector3 v3 = new Vector3(1.1f, 2.2f, 3.3f); //x,y,z座標を指定してオブジェクトを生成 Debug.Log(v3); for(int i = 0; i < 3; i++) { v3[i] *= 3.0f; //インデクサを用いてx,y,z座標を一律で3倍する } Debug.Log(v3); Monster831 mon = new Monster831("ゲーム用"); //自前のコンストラクタ②を呼ぶ Debug.Log(mon[1]); //インデクサ①を用いて"ヴェルドラ"を表示 Debug.Log(mon["シュナ"]); //インデクサ②を用いて3を表示 } } ゲーム開発編:p.169 Chapter 5「ゲームを仕上げよう」 p.171 画面上に文字を表示するにはUnity UIを使う ・Debug.Logはデバッグコンソールへの文字列の出力用なので、ゲーム画面上には表示できない ・ゲーム画面上に文字を表示するにはUnity UIを使う ・UIはユーザ・インタフェースの略で、Unityが提供する機能群(複数のパーツ) p.172 箱のゲームオブジェクトを作成する:補足 ・①[Hierarchy]の「+」をクリック ・②[2D Object]の[Sprites]の[Square]をクリック p.175 Unity UIのCanvasを配置する ・①[Hierarchy]の「+」をクリック ・警告「Keep Vertex Color …」が表示されるが無視して良い p.176 数を表示するためのTextを配置する ・①[Hierarchy]の「+」をクリック ・②[UI]の[Legacy]の[Text]をクリック  ※[Test - TextMeshPro] ではなく ・ここで「Canvas Renderer」の「Cult Transpearent Me」のチェックをオフにする(テキストの図の通り) ・テキストp.177下の図ではボックス上部の白い部分にテキストボックスを移動しているが、p.178の図の通り、ボックスの上の位置に大き目に配置する  ※うっすらと「0」が見えれば良い p.178 数を表示するためのTextを配置する(続き) ・①Text(Legacy)の[Text]の「New Text」に「0」を入力して上書きする p.178 ゲームオブジェクトを複製する ・①[Text(Legacy)]を右クリック ・④[Alignment]を左右中央揃え(左側の中央ボタン)、上下中央揃え(右側の中央ボタン)にする p.180 Collider2Dコンポーネントで衝突判定をする ・Colliderはコリジョン(衝突)から来た概念でUnityが提供する仕組み ・2D用と3D用のコンポーネントがある ・このゲームでは、落下する野菜とボックスの双方にCollider2Dコンポーネントを追加することで、衝突判定をさせる p.180 Collider2Dコンポーネントで衝突判定をする 補足 ・②[Open]ボタンをクリック p.181 Collider2Dコンポーネントで衝突判定をする(つづき)補足 ・① [Add Component][Physics2D]をクリック  ※表示されなければファイル名のボックスを空にすると良い p.181 BoxプレハブにBoxCollider2Dコンポーネントを設定する 補足 ・②[Open]ボタンをクリック p.182 BoxプレハブにBoxCollider2Dコンポーネントを設定する 続き(上) 補足 ・③[Add Component][Physics2D]をクリックして[Box Collider 2D」をクリック p.182 BoxプレハブにBoxCollider2Dコンポーネントを設定する 続き(中央) 補足 ・①[Offset]のXを[0]、Yを[0.45]にする ・②[SizeのXを[1.28]、Yを[0.4]にする 提出:演習 ex0831a.cs ・どのバージョンでもOK 次回予告: ・ゲーム開発編:p.183「野菜とBoxの衝突判定のスクリプトを書く」から ・文法編:ジェネリクス など