【Unity】RangeAttributeの最小値・最大値をConstで指定する
最近改めてAttribute(属性)を勉強し直してます。
Inspectorに表示する系のAttributeを中心に見ており、[HideInInspector]と[SerializeField]ではどっちが優先されるのか、[System.NonSerialized]と[SerializeField]ではどうか、なんて感じで遊んでました。
この記事ではInspectorへの表示つながりで、結構使用頻度の高いRangeAttributeの動作を確認します。
環境
macOS 10.13 High Sierra
Unity2018.1.0f2
RangeAttributeについて
int型やfloat型にこのAttributeを指定すると、Inspectorウィンドウでスライダーが表示され、そこで値を設定することができます。
Range(範囲)と名前がついているように、値の上限と下限を設定することができるので、変な値を入れてしまうことが少なくなります。
右にある数値の入力フィールドで範囲外の値を入れた場合にも、上限を超えていたら上限値に、下限を下回っていたら下限値に修正されます。
スライダーで直感的に操作できる点もGood。
RangeAttributeの使い方あれこれ
使い方は、スライダーにしたいメンバ変数(フィールド)に[Range(最小値, 最大値)]のAttributeをつけるだけ。
Inspectorでの表示
スクリプトで書くと以下のようになります。
publicまたは[SerializeField]であればInspectorに表示されます。その時にRangeAttributeがついていればスライダーが表示されます。
しかし、privateなフィールドで、[SerializeField]の指定がなければ表示されません。上のスクリプトの場合、hpは表示されますが、mpは表示されません。
スクリプト内で値の制限はされない
RangeAttributeが指定されているからといって、スクリプト内でも値の制限をしてくれるかというと、そんなことはありません。
例えば上で挙げたスクリプトでは、hpもmpも0から255の範囲としていますが、スクリプト内でそれより大きい値を突っ込んだ場合、255を超える値を格納できます。
Start()の中で値を足して確認してみます。
このスクリプトを保存してゲームを開始すると、最初のフレームでデバッグ文が出力されます。
Rangeで値の制限がされていれば最後の出力は『255 / 255』となるはずですが、『384 / 384』とどちらも255を超えています。
「RangeAttributeをつけたから値の検証は入れなくていいや!」なんてうっかりさんはご注意。
あくまでInspectorでの操作で値が制限されるだけなんです。
Infinityでも指定可能
以下のスクリプトのように、Mathf.Infinity、およびMathf.NegativeInfinityを使用して値を指定することも可能です。
これをInspectorウィンドウで確認すると以下のようになります。
画面では数値フィールドが小さくて見切れていますが、3.402823e+38と-3.402823e+38となっています。これはfloat型の最大値と最小値ですね。スライダーを右端および左端に移動させると上記の値が入力されます。
また、数値フィールドで[Inf]と入力してから[Enter]を押すと、無限大の値となります。これって型の最大値と一致するんですかね?
ちょっと不安になったので、念のため確認を。
Start()を上記のように書き換えて、2つのフィールドで比較を行います。float同士の比較なので、MathfクラスのApproximatelyメソッドを使っています。
2つ目と3つ目のif文では、float型が持っている無限大かどうかを確かめるメソッドを使って、値が無限大であるかどうかを調べています。
値の設定はInspectorから行い、atkの方ではスライダーを動かして[3.402823e+38]を設定し、atkInfの方は[Infinity]を入力します。見た目はどちらもスライダーの右端にいますが果たして……。
ゲームを実行した結果はこちら。atkとatkInfでは別の値になっていました。atkの方はあくまでfloatの最大値で、atkInfの方は数学的な無限大のようです。
Mathf.Infinityが見ているのはfloat.PositiveInfinityのようで、この値は0で正の数値を除算した結果とのこと。MSDNのリファレンスを見ると、型の最大値を超える値の場合にInfinityが返されるようなので、そりゃ一致はしないよね。
無限大は型の最大値、なんて誤解をしてました。うっかり。
最小値・最大値をconstで指定
RangeAttributeの範囲指定はconstで指定することも可能です。
上のスクリプトを保存すると以下のように表示されます。levelフィールドの最大値をconstで指定できました。
なお、readonlyや、static readonlyだとコンパイルが通りません。
readonlyで指定すると、『An object reference is required for the non-static field, method, or property ‘<スクリプト名>.<変数名>’』のエラーが出ます。staticじゃないフィールドの値を使っちゃってるのでやむなし。
もう一方のstatic readonlyでは『An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type』のエラーが出ます。実行時定数だと使えないみたい。
コンパイル時定数であるconstはコンパイル時に数字に変換されて埋め込まれるので、数字を直書きしてるのと同じイメージです。
じゃあ変数にするメリットは? というと、複数のフィールドで同じ範囲の値を持つ時に便利。例えばRPGで、全てのステータスの範囲を1から255で指定できる設計にした時、constを使って設定すれば、いちいち全部のRangeAttributeに数字を書かなくて済みます。
もし「ステータスの範囲は255までじゃなくて、999がいいっしょ! 時代は999だ!」なんてなった時にも、constを使っていれば1箇所の変更で済みますもんね。
int型やfloat型以外にもつけるとどうなるか
試しにstring型にRangeAttributeを指定したらどうなるんでしょ?
せっかくなので上で確認したconstを使いつつ、string型にRangeAttributeを追加。
この状態でコンパイルが終わると……。
『Use Range with float or int. (Rangeはfloat型かint型で使ってね)』との警告が表示されました。そりゃそうか。
通常のテキストフィールドも表示されずに警告表示。Attribute強し。
2つRangeを指定したらどっちが優先されるの?
以下のように2つ指定したらどうなるかも確かめよう。
おおっと、コンパイルエラー。
『Duplicate ‘Range’ attribute [Assembly-CSharp]』だって。重複して指定するのは無理みたい。
飛び飛びのスライダーとかを想像してましたが、流石に難しいか。
RangeのMaxとMinを逆にしたらどうなるの?
次はMaxで小さい値、Minで大きい値を指定してみましょ。
うーん、見ていて混乱しそう。
これがInspectorウィンドウに反映されると以下のようになります。
実際にスライダーを操作してみると、右に動かすにつれ値が下がっていきます。
直感的には混乱を招きますが、機能の上ではこういう使い方も可能です。
まとめ
RangeAttributeの使い方を紹介しました。
結構変則的な使い方も入れましたが、基本はやっぱり数値で最大値と最小値を指定するだけなので簡単に使えます。
ゲーム実行中のパラメータ調整に使うと超便利なので、デバッグ時にはぜひ使うことをおすすめ。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity】SerializeFieldとNonSerializedの頂上バトル! 勝つのはどっちだ!! 2018.06.13
-
次の記事
【Unity】無限とは一体…うごごご! UnityではInfinityがどう扱われるのか 2018.06.16
コメントを書く