今回の記事では列挙型と定数について解説していきます。
列挙型を使うとスクリプト上で定数をテキストとして表現でき、より読みやすく設定しやすいコードを書くことができます。
前回の記事:
列挙型(enum)とは
列挙型は値型になり、System.Enum
型から派生したものになります。
列挙型を使用することで定数に名前をつけることができ、スクリプトが読みやすくなります。
列挙型は定数を簡単に書くことができるものと理解すればOKです。
定数という用語が出てきたので、ここで簡単に説明します。
定数について
これまで変数を何回も使ってきましたが、プログラミングには一度値を設定したら変更できない定数というものが存在します。定数はアプリのパラメータを表すのに最適な変数になります。
定数には以下のものがあります。使い方としては変数宣言の時に型の前にこれらのキーワードを付け加えるだけになります。
const
:メンバ宣言の時にのみに値を設定できる。値型とstringのみ使用できる。コンパイル時から固定値。
readonly
:メンバ宣言とコンストラクタ(※)内でのみ値を設定できる。実行時のみの固定値。
(※コンストラクタ(Constructor)はクラスの初期化を行うメソッドになり、少し書き方が異なります。詳しくは後ほど解説します。)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class A { //両方とも型の前に書く。二つ同時に指定することはできない。 public const int Constant = 100; public readonly float Readonly = 10; } var a = new A(); //後から値は変更できない。 a.Constant = 100; // <- NG a.Readonly = -1; // <- NG //読み込みとしてのみ利用可能。 var n = a.Constant + a.Readonly; |
const
とreadonly
の違いは定数として扱われるタイミングが異なります。const
として宣言された変数はコンパイルのタイミングから定数となりswitch
文のcase
文に指定できますが、readonly
の場合はできず、実行時の初期化のタイミングから定数になるなどの違いがあります。
また、constは値型のみしかつけることができませんが、readonlyには型に制約はありません。
列挙型の書き方
列挙型には2種類あります。よく使われるそれらの書き方についてみていきます。
- 通常のもの
- フラグ
通常の列挙型
列挙型の宣言はenum <列挙型名>{ <定数となるテキスト>, ... }
と書きます。
基本的に列挙型のメンバは定義された順に0から始まる連続した数値が設定されます。
列挙型宣言時に=演算子を使用すると好きな数値を設定することができます。その際後ろのメンバには前のメンバの値に+1した数値が設定されます。
列挙型はint型として扱われますが、他の数値型を指定することもできます。
また、ToString()
メソッドを使用することで列挙型のテキストを出力することもできます。
enum <列挙型名> : <数値型> { <定数となるテキスト>, ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
enum Fruits { Apple, // == 0 Banana, // == 1 Orange, // == 2 Grape, // == 3 } Fruits f = Fruits.Apple; Fruits f2 = Fruits.Orange; string str = f.ToString(); // str == "Apple" //数値型を指定した列挙型の宣言(ushort型として扱われる) enum Animal : ushort { Cat = 1, Dag = 100, } |
System.Flags属性をつけた列挙型
まだ紹介していないC#の機能になりますが、System.FlagsAttribute
という属性(※)を列挙型の定義の部分に書くとその列挙型はフラグとして扱われます。
(※属性(Attribute)はメンバにつけることができる、付加的な情報を表します。使用する際は末尾のAttributeは省略できます。)
フラグは一つの変数に複数の情報を持たせることができます。
なぜなら、情報の有無を0か1のビットで表現する形式だからです
ビット演算が可能なので2進数の1桁目、二桁目それぞれに違う状態を意味する値を設定できます。
例えば、ビットの1桁目で行動A、ビットの二桁目で行動B、ビットの3桁目には行動Cを表すとします。行動中であればビットを1に。行動してなければビットを0にするとします。
このとき、行動Aと行動Cのみ行っていれば、0b0101のように表現できるわけですね(0bを接頭辞で付けると二進数を意味します)。
ビット演算で行動状態の重ね合わせなどもできるので便利です。少々玄人向けの列挙型の使い方になります。
上記の特性から、メンバの定義の際もビット演算を考慮した値を設定する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//フラグの場合は手動で値を設定した方がいい。 [Flags] public enum Fruits { Apple = 0b0001, // <- 1 Banana = 0b0010, // <- 2 Orange = 0b0100, // <- 4 Grape = 0b1000, // <- 8 } //flagにはAppleとGrapeの二つの値が設定されている。 // 数値としては0b1001となっている。 // フラグの合成には|演算子を使う。 Fruits flag = Fruits.Apple | Fruits.Grape; //値の判定の仕方。フラグの確認には&演算子を使う。 bool isApple = (flag & Fruits.Apple) != 0; // <- true bool isOrange = (flag & Fruits.Orange) != 0; // <- false |
【実践】Unityで実際に列挙型を使ってみよう!
今回のサンプルコードでは設定した列挙型によってGameObjectの移動の仕方を変更させてみましょう!
サンプルコードではMoveOp列挙型でGameObjectの移動方向を定義しています。
InspectorからMoveOp型のMoveFlagsとSpeedを変更することでGameObjectを移動させることができます。MoveFlagsはGetMove()の中で使用しています。
GetMove()メソッドは移動量を求めるメソッドになり、Update()の中で呼び出しています。Update()の中で移動させているので再生中に移動方法を切り替えることができます。
MoveOp列挙型のメンバとGetMove()の処理を追加することで他方向に移動させたり、異なる処理を追加することができるので、余力のある方は試してみてください。
実行する際は適当なGameObjectを作成して、それにサンプルコードのコンポーネントをアタッチしてください。
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 |
using System; using UnityEngine; public class Sample : MonoBehaviour { [Flags] public enum MoveOp { None = 0, Up = 0x1, Down = 0x1 << 2, Left = 0x1 << 3, Right = 0x1 << 4, } public MoveOp MoveFlags = MoveOp.None; public float Speed = 1f; //1秒あたりに進む速さ void Update() { transform.position += GetMove(Speed); } Vector3 GetMove(float speed) { speed *= Time.deltaTime;// <- 1フレーム単位の速さに変換している。 var move = Vector3.zero; if(0 != (MoveFlags & MoveOp.Up)) { move.y += speed; } if(0 != (MoveFlags & MoveOp.Down)) { move.y -= speed; } if(0 != (MoveFlags & MoveOp.Left)) { move.x -= speed; } if(0 != (MoveFlags & MoveOp.Right)) { move.x += speed; } return move; } } |
MoveOp列挙型の書き方がぱっと見難しいと思いますが、それぞれの桁をシフト演算を用いてフラグとして宣言しています(シフト演算の復習はこちら)。
この書き方をすることで、各状態の重ね合わせを表現できるようになります。
例えば、右上移動は右を表す5桁目に1が入っている&上を表す2桁目に1が入っていることで表現可能です。
通常の数値の列挙型を用いる場合は、右上、左上などもそれぞれ別の定数として宣言しなくてはならず管理が大変になります。
今回のサンプルコードの書き方は”ボタン同時押し”や”状態の同時実現”などゲームで頻出する機能を実現する上で利用できるので頑張って理解してみてください。
まとめ
今回の記事では列挙型について解説してきました。簡単にまとめると以下のようになります。
- 列挙型は定数をテキストとして扱える様にするもので値型になる。
- 定数は値を設定した後は変更できない。
const
とreadonly
の2種類ある。 const
には値型とstring
のみ利用でき、コンパイル時から定数として扱われる。readonly
は好きな型に利用でき、実行時の初期化から定数として扱われる。- 全ての列挙型は
System.Enum
から派生している。 - 列挙型のメンバは自動的に数値が割り振られる。
- 列挙型のテキストはそのまま文字列に変換できたり、対応している数値に変換できる。
それでは次の記事に行ってみましょう!
次回の記事:
コメント