講義メモ 後半

ゲーム開発編: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.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を表示
    }
}

今週の話題

ゲームソフト販売本数ランキング:今週1位も「マリオカート ワールド(Switch2)」 GO!
東京都、生成AI活用「モバイルアプリコンテスト」中高生対象 GO!
Epic GamesのCEO語る「UE5最適化問題」の原因と改善策。ハイスペック基準の開発工程が落とし穴と分析 GO!

Windows 11最新アプデでSSDが壊れる不具合「Windows側の問題」とPhison調査結果公表も社員再現できず GO!
ユービーアイが『ザ・ローグ:プリンス オブ ペルシャ』を“インディー規模”と宣伝して物議醸す―「開発はインディー」vs「大企業の資金が入っている」GO!

前回のコメント

・クラスと構造体の違いが分かりました。

 何よりです。
 テキストではUnityが提供するクラスと構造体のみが説明されていますが、
 プログラマやチームの自前のクラスや構造体を用いることは少なくありませんので、
 自在に使いこなせることを目指してください。

講義メモ 後半

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.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に。
        }
    }
}

文法編:プロパティの補足

・上記の「プロパティが返す構造体の値は直接書き替えられない」ことを試そう
・まず「プロパティやメソッドは値だけではなく、オブジェクトを返すことができる」を試そう
・下記の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); //書き換わっていることを確認
    }
}

今週の話題

ゲームソフト販売本数ランキング:今週1位は再び「マリオカート ワールド(Switch2)」 GO!
【イベント告知】アカマイが「Akamai Cloud Day 2025」を9月24日に開催。特別セッションに安野貴博氏が登壇、ソニーグループや時雨堂の事例も GO!
赤字スタートのセガ、逆襲の鍵は『ソニックレーシング』と待望の『Football Manager』か【ゲーム企業の決算を読む】GO!
業績は減益、株価は高値。スクエニに迫る「物言う株主」の影【ゲーム企業の決算を読む】GO!
グリーの2025年6月期決算は売上6.8%減―新作『まどドラ』投入もゲーム事業はQoQで減収【ゲーム企業の決算を読む】GO!
【キャリアクエスト】「これ、あったら面白い」を実際に作り出せる。セガ入社3年目の若手が語るプランナーという職種、そして自身の展望とは GO!

マイクロソフト「Windows 11高負荷時にSSDを破損する不具合再現できず」と声明。一方Phisonは「Windows側の問題」と調査結果公表 GO!
DDoS・Bot攻撃との“いたちごっこ”に終止符を──AkamaiとCygamesが語る、費用対効果と自動化を両立するセキュリティ戦略【CEDEC2025レポート】GO!