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