C#に限らずプログラミングをする時に変数を使う事は多々あります。
むしろ使わずにプログラミングする方が難しいですね。(^_^;)
そこで、今回はUnity付属のMonoDevelopを使って簡単に変数について勉強していきます。
変数と似たようなものに、フィールドとプロパティがあり、違いってなんだ?となってきますが、そこら辺は検索で『フィールドとプロパティの違い』等と検索すれば詳しい解説をされている方が大勢おられますので、そちらにまかせましょう・・・・)))))))))))(・・)/すたた
後は、マイクロソフトの連載記事にも載っています。
前回のC#の記事は全然ゆるい記事になっていなかったので、今回からゆるい感じで記事を進めようと思います。
きちんとした内容は書籍や専門サイトをご覧ください。(-_-)
そんな事書いたら、この記事を公開する意味はなくなってしまうような気がしますが・・・・・(ーー;)
変数
それでは変数についてみていきましょう。
変数とはなんぞや
変数は一時的にデータを記憶しておく領域(箱)といったところでしょうか、変数を宣言し、intやfloat等の型に合わせた大きさの領域を確保しデータを入れておく場所を作ります。
1 2 3 | int intValue; |
最初に変数の型(int)を書き、その後に変数名(intValue)を書きます。
変数には値型と参照型があり、int(整数)やfloat(浮動小数点数)の場合はそのまま値が変数に入る値型で、stringやUnityのGameObject等はそのものの値を入れるわけではなく、値が入っている場所を示す参照値が入っているので参照型と呼ばれます。
なぜ値型だけでなく参照型があるのかと言うと、メソッドでデータの受け渡しをする時に、受け取り側でも同じ型の仮の引数を宣言する必要があり、そうするとその領域分のメモリを確保しなくてはならなくなります。
その為、受け渡すデータが大きい場合はそれだけでメモリを多く使ってしまいます。
参照型であればデータがある場所を指し示す参照を渡すだけなので、引数にデータ領域を確保しなくて済みます。
例えるならば、友人に本を10冊借りるとします。
値型の場合、友人から直接10冊の本を借りるので10冊の本が入る袋を用意し、受け取ります。
参照型の場合は友人から10冊の本は『家に置いてあるから』という本10冊のありかを教えてもらうだけ。
という感じです。
例えがひどすぎる・・・・・(^_^;)
参照型のメリットは他にもあると思いますが、なんだろ?
連続する値を使う時に便利とか?
難しい話は置いておきまして・・・、先ほどのスクリプトではint型のintValueという名前の変数を宣言しています。
変数の有効範囲
その変数が有効なのは宣言されているブロック{}内のみでブロックの外では使用できません。
クラス全体で使う値はフィールドやプロパティを使うと便利です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using UnityEngine; using System.Collections; public class PreLearn2 : MonoBehaviour { // Use this for initialization void Start () { int intValue = 1; { float floatValue = 2.2f; Debug.Log (intValue); Debug.Log (floatValue); } // 変数が宣言されていないというエラーになる //Debug.Log (floatValue); } } |
↑のようにintValueはブロックの外で宣言されているので、ブロックの中でも有効で、コンソールにデータを出力する事が出来ます。
一方floatValueはブロックの中で宣言されていますので、外側でDebug.Log(floatValue);と記述するとエラーが表示されコンパイル出来ません。
配列について
配列はintやfloat、GameObject等の一連の型で複数の値を保持出来るものです。
例えば1週間毎日の勉強時間を時間単位として記録したい場合にint型で1週間分の時間の変数を用意したとします。
1 2 3 4 5 6 7 8 9 | int time1; int time2; int time3; int time4; int time5; int time6; int time7; |
と一つ一つ変数を作成していては面倒な上に、変数名を間違えたら大変ですし、7個の記録を使って平均値を計算したいといった時に変数それぞれを足していく為に、それぞれの変数を記述する必要があるなど大変です。
そこで活躍するのが配列で
1 2 3 | int[] times = {1, 2, 3, 4, 5, 6, 7}; |
型の後に[]を付けてその後に変数名を記述します。
宣言と同時に配列の初期化をする事が出来、↑のように7つの要素を持つint型の配列が宣言出来ます。
初期化をしない場合は配列の要素を確保する為に要素数を指定する必要があります。
どこからどこまでをtimes配列の領域とするか決定しなければいけないということですね。
1 2 3 4 5 6 7 8 | int[] times; // 要素を確保していないのでエラー //times [0] = 1; // 要素を確保 times = new int[7]; times [0] = 1; |
↑の例では最初は配列の宣言のみで要素の確保をしていません。
その為、コメントにしてあるtimes[0] = 1を行うと、エラーが発生しコンパイル出来ません。
その下で7個文のint型の要素をnew int[7]で確保しtimesに入れる事でtimesの各要素に値を入れる事が出来るようになります。
配列の要素は0番目から始まるので、要素を7個確保した場合は要素番号は0から6の間になります。
配列は繰り返し文で使用するとその効力がよくわかります。
配列の場合は要素の数を限定して使う為、必要のない領域分のメモリを使います。
あらかじめ領域を確保せず逐一要素の追加や削除を行いたい場合はList等を使った方が便利です。
Listについてはまた別の機会にやりたいと思います。
string型について
string型は値型ではなく参照型になります。
その為、string型の変数をメソッド等で受け渡しをする場合は文字列データへの参照が渡される事になります。
1 2 3 4 5 6 | string stringValue = "テスト"; Debug.Log(stringValue); Debug.Log(stringValue[0]); |
↑のようにstring型の変数に『テスト』という文字列を格納し、Debug.LogでコンソールにstringValueの値を表示すると『テスト』という文字列が表示され、
stringValue[0]では『テ』という文字が表示されます。
URL等のPathを文字列型に入れる場合は階層に\記号を入れますが、この\記号を文字列型の”(ダブルクォーテーション)の中に入れるとエスケープシーケンスと認識されてしまうので、\\と連続で記述する必要があります。
リテラル文字列を使うと連続で記述しなくても済みます。
1 2 3 4 5 6 7 8 9 | // 通常Pathの記述 string path1 = "C:\\Users\\user\\Documents"; // リテラル文字列を使ったPathの記述 string path2 = @"C:\Users\user\Documents"; Debug.Log (path1); Debug.Log (path2); |
ここら辺の事は
↑を参考にしました。
変数のキャスト
スクリプトの処理によっては変数の値のキャスト(型変換)をしなければいけない事があります。
暗黙のキャスト
値が小さい数値型から大きい数値型へのキャストは暗黙的に行われ
1 2 3 4 5 6 7 8 | int intValue = 1; float floatValue; floatValue = intValue; Debug.Log (floatValue); } |
↑のような場合はint型はfloat型にキャストされ、floatValueの値を出力すると1が表示されます。
逆にfloatValueをintValue変数に入れようと思うとエラーが発生します。
また暗黙のキャストは子クラスを親クラスの変数に入れる時にも行われます。
継承の記事をまだやっていないので、子クラスを親クラスの変数に入れるのはまたその時にでもやりたいと思います。
明示的なキャスト
型変換を明示的に行いたい事もあります。
キャストをするには
1 2 3 4 | // キャスト int floatToIntValue = (int)floatValue; |
↑のように変数の前に(キャストする型)という記述を入れます。
上の例ではfloat型の値をint型にキャストしそれをfloatToIntValue変数に入れています。
参照型の変数もキャストする事が出来ますが、参照型の場合はキャストが出来ずに例外が発生する可能性があります。
そんな時はキャストが可能かどうかを調べ、例外を回避します。
まずParentObjというobjectクラスを継承して作った親クラスとChildObjというParentObjクラスを継承して作ったクラスを作成します。
1 2 3 4 5 6 7 8 | using UnityEngine; using System.Collections; public class ParentObj : object { } |
1 2 3 4 5 6 7 8 | using UnityEngine; using System.Collections; public class ChildObj : ParentObj { private int intValue; } |
ChildObjをインスタンス化し、ParentObjにキャストが可能かどうかを調べた後にキャストをしてみます。
1 2 3 4 5 6 7 8 9 | ChildObj childObj = new ChildObj(); // キャスト出来るか確認 if (childObj is ParentObj) { Debug.Log ("ParentObjにキャスト可能"); ParentObj parentObj = (ParentObj)childObj; } |
↑のようにisを使ってChildObjクラスのインスタンスであるchildObjをParentObjクラスへとキャストする事が出来るか判定し、可能であればキャストをしています。
通常であれば暗黙のキャストが働くので明示的にする必要もありませんが・・・・(^_^;)
isを使った場合は戻り値に真偽値(bool値)が返って来るのでif文で条件判定をしますが、as演算子を使用するとキャストが可能ならばキャストし、不可能ならばnull値を入れると言う事も出来ます。
Unityでキャストが必要な時は、例えばゲームオブジェクトをインスタンス化するInstantiateを使った時ですね。
GameObject.Instantiateの戻り値はObject型なのでこれをGameObject型の変数に入れたい場合はGameObject型にキャストする必要があります。
1 2 3 4 | // asを使用すると例外を発生させずにキャスト GameObject obj = Instantiate (prefab, new Vector3(0f, 0f, 0f), Quaternion.identity) as GameObject; |
↑のようにas GameObjectでGameObject型にキャストしGameObject型のobjに入れています。
暗黙の型指定
ここまでで変数の宣言と同時に値を設定する場合は、まずは型を宣言し、その後に変数名を記述し、右辺に値を設定していました。
ですが、変数の宣言と同時に初期化する場合等の、あらかじめ右辺の型が決まっている場合は型名を省略し、varという記述を追加します。
1 2 3 4 5 6 | // 右辺はint型になっているので型を省略しvarで宣言出来る var intValue = 1; // 初期化しない場合はこの変数がどんな型なのかわからない為、varだけではエラーになる。 //var floatValue; |
intValueは宣言とともに値の初期化をしている為に右辺の値はintだとわかっている為、左辺の型名の指定をなくしvarという記述で行えます。
floatValueは初期化をしていないので、宣言だけでは型名がわからない為、varの記述だけではエラーとなってしまいます。
Null許容型の値型変数の宣言
intやfloatの値型では通常Null値を設定する事が出来ません。
ですがNullを許容するように宣言をするとNull値を設定する事も出来ます。
1 2 3 4 5 6 | // Null許容のint型を宣言 int? intValue1 = null; // ジェネリクスで宣言 Nullable<int> intValue2 = 1; |
型の後に?を付けて宣言するか、Nullableのジェネリクスに型を指定して宣言するとNull許容型となります。
Null許容型にするとHasValueプロパティでその変数が値を持っているかどうかを調べる事が出来ます。
これらの事を踏まえたスクリプトを記述してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using UnityEngine; using System.Collections; using System; public class Learn2_3 : MonoBehaviour { // Use this for initialization void Start () { int intValue1 = 0; // Null許容のint型を宣言 int? intValue2 = null; // ジェネリクスで宣言 Nullable<int> intValue3 = 1; Debug.Log (intValue1); Debug.Log (intValue2); Debug.Log (intValue3); intValue3 = null; Debug.Log (intValue3); // Null許容型で値があるかどうかのチェック if (intValue3.HasValue) { Debug.Log ("Nullじゃない"); } else { Debug.Log ("Null"); } } } |
サンプルの実行
これでとりあえず変数についての勉強は終わりです。
これまでの解説を実行して確認する為に、UnityでLearn2_1スクリプトを作り実行してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | using UnityEngine; using System.Collections; public class Learn2_1 : MonoBehaviour { // インスタンス化するゲームオブジェクト public GameObject prefab; // Use this for initialization void Start () { // 変数の宣言 int intValue; float floatValue = 5.5f; double doubleValue = 3.1; string stringValue = "テスト"; // 通常Pathの記述 string path1 = "C:\\Users\\user\\Documents"; // リテラル文字列を使ったPathの記述 string path2 = @"C:\Users\user\Documents"; Debug.Log (path1); Debug.Log (path2); /* // int[] times = {1, 2, 3, 4, 5, 6, 7}; int[] times; // 要素を確保していないのでエラー //times [0] = 1; // 要素を確保 times = new int[7]; times [0] = 1; */ // 値を入れる intValue = 1; // 暗黙のキャスト doubleValue = intValue; // 明示的キャスト int floatToIntValue = (int)floatValue; ChildObj childObj = new ChildObj(); // キャスト出来るか確認 if (childObj is ParentObj) { Debug.Log ("ParentObjにキャスト可能"); ParentObj parentObj = (ParentObj)childObj; } // asを使用すると例外を発生させずにキャスト GameObject obj = Instantiate (prefab, new Vector3(0f, 0f, 0f), Quaternion.identity) as GameObject; // 暗黙の型指定を使った場合 // var obj = Instantiate (prefab, new Vector3(0f, 0f, 0f), Quaternion.identity) as GameObject; // 値を出力する Debug.Log ("int " + intValue); Debug.Log ("float " + floatValue); Debug.Log ("double " + doubleValue); Debug.Log ("string " + stringValue); Debug.Log ("stringを分割して表示 " + stringValue [0] + " : " + stringValue [1] + " : " + stringValue [2]); Debug.Log ("floatをintにキャスト " + floatToIntValue); Debug.Log ("インスタンス化したゲームオブジェクトの名前 " + obj.name); } } |
配列についてはコメント化してあります。
publicでprefabフィールドを宣言したので、このLearn2_1スクリプトをMain Cameraに設定し、インスペクタで何らかのゲームオブジェクトのプレハブを設定しておきます。
わたくしの場合はCubeを作成し、プレハブ化してそれを設定しました。
実行してみると、
↑のような結果がコンソールに表示されます。
おわりに
記事を書き始めはゆるい感じで書こうと思うんですが・・・、書いているうちにこれもあれも書かなきゃと言う事になり結局ボリューム満載になってしまいます。(^_^;)
ボリュームあるけど中身が正確か?と言われるとなんとも言えませんが、どこかの誰かの参考にはなるかもしれません。
次回こそはゆるい感じでいこう・・・・・(T_T)/~~~