講義メモ ・ゲーム開発編:p.164 野菜の種類を順番に切り替える から ・文法編:プロパティの補足、クラスと構造体、など p.160 Shooter.cs 補足 ・p.160で下記を追記してゲームオブジェクトの4要素の配列を生成している  public GameObject[] prefabs = new GameObject[4]; ・しかし、GameObjectはクラスであるため、本来は、この1行のみではオブジェクトへの参照の配列(オブジェクトのない配列)しか生成されない ・しかし、p.168下から3行目に説明がある通り、Unityが提供するMonoBehaviour継承クラスにおいては、Unityのシステムによって、自動的にオブジェクトが生成される ・よって、通常のクラスの場合と、Unityが提供するMonoBehaviour継承クラスの場合で、仕様が異なることに注意を。 ゲーム開発編:p.164 野菜の種類を順番に切り替える ・Shooter.csに、現在の野菜の添字を示すint型変数curvageを追加 ・初期値は0として、tomatoを意味させる ・マウスボタンが押された時の処理でtomatoを固定で出現させているところを、この変数を用いることで、異なる野菜が出現するように変更 ・また、野菜の種類を順番に切り替えるようにしている p.164&165 Shooter.cs //p.164&165 shooter.cs using UnityEngine; public class shooter : MonoBehaviour { float move = 0.05f; //単精度実数型のフィールド変数moveの初期化 public GameObject[] prefabs = new GameObject[4]; //ゲームオブジェクトの4要素の配列を生成 int curvage = 0; //【追加】現在の野菜の添字を示す変数(初期値はtomato) 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[curvage], pos, Quaternion.identity); //【変更】現在の野菜を表示 curvage++; //【追加】現在の野菜の添字に1加算することで次の野菜へ curvage %= 4; //【追加】4で割った余りにすることで0,1,2,3の繰返しになる } } } p.165 Shooter.cs 文法の補足 ・prefabs[curvage]により、GameObject型のオブジェクトの配列から、curvage番目のオブジェクトを得ている ・「curvage++」の「++」はp.105の通りインクリメント演算子で「curvage = curvage + 1」と同じ意味になる ・なお、ここではインクリメント(1加算)を単独で行っているので後置を前置にして「++curvage」としても同じ動作になる ・「curvage %= 4」の「%=」はp.103の通り「計算もできる代入演算子(複合代入演算子)で「curvage = curvage % 4」と同じ意味になる ・これにより、curvageの値は4で割った余りである0,1,2,3のどれかになる ・よって、「curvage++」と「curvage %= 4」の組み合わせで、curvageの値は0,1,2,3,0,1,2,3,0,…と順番に変化していくことがわかる ・なお、剰余算は加減算などより処理が重たくなりやすいので、このような処理はif文や条件演算子で記述することも多い  例: curvage = (curvage >= 3) ? 0 : curvage + 1; //3なら0に、でなければ+1に。 アレンジ演習:p.165 Shooter.cs ・上記の条件演算子による記述にして動作を確認しよう 作成例 //アレンジ演習:p.164&165 shooter.cs using UnityEngine; public class shooter : MonoBehaviour { float move = 0.05f; //単精度実数型のフィールド変数moveの初期化 public GameObject[] prefabs = new GameObject[4]; //ゲームオブジェクトの4要素の配列を生成 int curvage = 0; //現在の野菜の添字を示す変数(初期値はtomato) 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[curvage], pos, Quaternion.identity); //【変更】現在の野菜を表示 //curvage++; //【削除】現在の野菜の添字に1加算することで次の野菜へ //curvage %= 4; //【削除】4で割った余りにすることで0,1,2,3の繰返しになる curvage = (curvage >= 3) ? 0 : curvage + 1; //【差替】3なら0に、でなければ+1に。 } } } p.166 プロパティが返す構造体の値は直接書き替えられない ・プロパティやメソッドは値だけではなく、オブジェクトを返すことができる ・例:  public getSlime() { //hpとmpが10であるスライムを返すメソッド   Slime w = new Slime(); //スライムクラスのオブジェクトを生成   w.hp = w.mp = 10; //このスライムのhpとmpを10にする   return w; //生成したオブジェクトを返す  } ・クラスのオブジェクトの場合、生成したオブジェクトへの参照になるので、返された側でそのオブジェクトそのものを操作できる ・ところが、構造体のオブジェクトの場合、生成したオブジェクトのコピーが返されるので、返された側でそのオブジェクトの中のメンバを操作できない ・よって、p.166のようなエラーが表示されてしまう 文法編:プロパティの補足 ・上記の「プロパティが返す構造体の値は直接書き替えられない」ことを試そう ・まず「プロパティやメソッドは値だけではなく、オブジェクトを返すことができる」を試そう ・下記のMap構造体(座標を示す)を定義した場合 struct Map { public int x; //データメンバ public int y; } ・この構造体を型とするデータや、型とするプロパティを持つMonsterクラスを定義できる class Monster { //インナークラスMonsterの定義 public int hp; //Monsterクラスの持つint型のデータ public int mp; //同上 public Map xy; //Monsterクラスの持つMap構造体の座標オブジェクト public Map XYP{ //座標オブジェクトを返すプロパティ get { return xy; } //座標オブジェクトを返す(setがないので外部書替え不可) } } 演習 ex0824a.cs ・上記を用いて、プロパティ経由で得た座標オブジェクトのデータメンバx、yを書き替えられないことを確認しよう ・表示であれば可能なことを確認しよう ・また、座標オブジェクトをプロパティで得て、Map型の変数に代入すれば:  ① Map型の変数のメンバであるデータの書き替え  ② Map型の変数を元の座標オブジェクトに書き戻す  ことが可能なことを確認しよう 作成例 using UnityEngine; public class ex0824a : MonoBehaviour { //Map構造体(座標を示す) struct Map { public int x; //データメンバ public int y; } //この構造体を型とするデータや、型とするプロパティを持つMonsterクラス class Monster { //インナークラスMonsterの定義 public int hp; //Monsterクラスの持つint型のデータ public int mp; //同上 public Map xy; //Monsterクラスの持つMap構造体の座標オブジェクト public Map XYP{ //座標オブジェクトを返すプロパティ get { return xy; } //座標オブジェクトを返す } } void Start() { Monster veldra = new Monster(); //Monsterのveldraを生成 //veldra.XYP.x = 10; //プロパティで得た座標オブジェクトのデータは書替え不可 //veldra.XYP.y = 20; //プロパティで得た座標オブジェクトのデータは書替え不可 Debug.Log(veldra.XYP.x + ", " + veldra.XYP.y); //表示は可能 Map vmap = veldra.XYP; //veldraの座標オブジェクトをプロパティで得て代入 vmap.x = 10; //受け取ったオブジェクトのコピーのデータは書替えOK vmap.y = 20; //受け取ったオブジェクトのコピーのデータは書替えOK veldra.xy = vmap; //書替え結果を書き戻すことはOK Debug.Log(veldra.XYP.x + ", " + veldra.XYP.y); //書き換わっていることを確認 } } p.167 プロパティの作り方はフィールド変数とまったく違う ・「文法編:プロパティ」や「p.145 変数のように使えるけれどちょっと違うプロパティ」で説明した通り、プロパティの作り方はメソッドに近い ・なお、プロパティのget、setはどちらか片方であれば省略可能。つまり、getとset、getのみ、setのみの3パターンで記述できる ・主に、getのみにすることで、データを読み込み専用(Read Only)にすることが多い ・特定のデータ専用のプロパティを作成した場合は、そのデータをpublicではなくprivate(あるいは無指定)にすることで、そのデータの保護に役立つ ・基本的にプロパティはクラスや構造体の外部から呼び出して用いるので、publicにする ・なお、クラスや構造体の内部ではデータとして持っていない情報(計算や処理で得られる値など)を、プロパティで返すようにすると、外部からはクラスや構造体の持つデータに見える 演習 ex0824b.cs ・以下のBMIを扱うクラスの定義に、文字列「痩せ気味」「普通」「太り気味」「未設定」のいずれかを返すプロパティmessageを追記して、動作を確認しよう ・なお、BMIが20以上30未満であれば「普通」とし、heightまたはweightが0.0であれば「未設定」を返すこと class BMI0824 { //BMIを扱うクラスの定義 private double height; //身長(内部用) private double weight; //体重(内部用) public double HM { //heightを扱うプロパティ get { return height; } //値を返すのは無制限 set { if (value > 0) { height = value; } } //値を設定するにはルールあり } public double KG { //プロパティの定義 get { return weight; } //値を返すのは無制限 set { if (value > 0) { weight = value; } } //値を設定するにはルールあり } } 作成例 using UnityEngine; public class ex0824b : MonoBehaviour { public double Height, Weight; //パブリック変数 class BMI0824 { //BMIを扱うクラスの定義 private double height; //身長(内部用) private double weight; //体重(内部用) public double HM { //heightを扱うプロパティ get { return height; } //値を返すのは無制限 set { if (value > 0) { height = value; } } //値を設定するにはルールあり } public double KG { //プロパティの定義 get { return weight; } //値を返すのは無制限 set { if (value > 0) { weight = value; } } //値を設定するにはルールあり } public string message { //判定文字列を返すプロパティ(set無し) get { if (height == 0.0 || weight == 0.0) { return "未設定"; } else { double bmi = weight / height / height; if (bmi < 20.0) { return "痩せ気味"; } else if (bmi >= 30.0) { return "太り気味"; } else { return "普通"; } } } } } void Start() { BMI0824 b = new BMI0824(); //BMIを扱うオブジェクトを生成 Debug.Log(b.message); //判定文字列を表示 b.HM = Height; //パブリック変数からプロパティ経由で身長を設定 b.KG = Weight; //パブリック変数からプロパティ経由で体重を設定 Debug.Log(b.message); //判定文字列を表示 } } p.168 class(クラス)とstruct(構造体)の違いについて ・UnityやC#を業務に活用していくと、Unityに用意されていないクラスや構造体を自分で定義したり、他者が定義したものを利用することは少なくない ・よって、class(クラス)とstruct(構造体)の違いをしっかり理解しておこう ・なお、C/C++における構造体と、C#/Unityにおける構造体は仕様が大きく異なるので注意  ⇒ C/C++における構造体はデータメンバのみを持ち、データをまとめて扱うためだけの仕組み。  ⇒ C#/Unityにおける構造体はメソッドやプロパティなども持てる「軽量のクラス」 ・class(クラス)にできてstruct(構造体)にはできないこと:  ①継承できない  ②データ(インスタンス変数)の初期化ができない(代入で初期値を与えること)  ③引数のないコンストラクタ(※後述)の定義はできない(自動的に用意される)  ④デストラクタ(※後述)の定義はできない(自動的に用意される) ・class(クラス)は参照型だが、struct(構造体)は値型なので、下記の場合に全データのコピーが発生する  ①構造体を型とする変数間の代入  ②構造体を型とする引数を持つメソッドの呼び出し  ③構造体を戻り値型とするメソッドやプロパティからの戻り ・なお、class(クラス)は参照型なので、クラスを型とする変数間の代入では、参照が代入され、2つの変数が同じオブジェクトを扱うようになる  ⇒2つ目の変数が1つ目の変数の別名になる 演習 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のままなことを確認しよう(コピーなので) 提出:演習 ex0824c.cs 次回予告 ・ゲーム開発編:p.169 Chapter 5「ゲームを仕上げよう」から ・文法編:インスタンスとコンストラクタとデストラクタ、など