##Unity 2019.2 から
TryGetComponent
という GetComponent
相当のメソッドが追加されています。
このメソッドの紹介をしたいのですが、そのためには切っても切れない、 out var(out変数宣言)
についてから書きたいと思います。
「out var ぐらい知ってらぁ!!」という方は、 TryGetComponentからどうぞ。
「どっちも知ってらぁ!!」という方は・・・。お疲れ様でございました。
out変数宣言
out 宣言。 使ってます? 多分、Unityでゲーム作っている上で自作のなんらかでこのキーワードをわざわざ使ってメソッド宣言している人はほとんどいないでしょう(偏見)
そもそもの機能としては、「out を付けた引数で指定した変数はメソッド内で必ず結果が入りますよ。」 という宣言です。
private void PlusMinusOne(int origin, out int plusResult, out int minusResult)
{
plusResult = origin + 1;
minusResult = origin - 1; //out キーワードを付けた引数への代入が存在しない場合はエラー
}
public void Start()
{
int plusResult
int minusResult;
PlusMinusOne(100, out plusResult, out minusResult); //呼び出す方も out キーワードを付ける必要がある
Debug.Log($"plusResult:{plusResult},minusResult:{minusResult}");
}
plusResult:101,minusResult:99
ちなみに利点は、「必ず値がセットされる事が保証される」 です。今回とは趣旨が違うので詳細は省きますが・・・1
Try**
さて、自分でoutキーワードを使ったメソッドを作らないかもしれませんが、.netで用意されているクラスのメソッドにoutキーワードが使われている場合ももちろんあります。
(個人的に)一番使うであろうメソッドは Try系メソッドです。
例えば、文字列をintに変換しようとした場合、int.Parse("100");
のように、パースメソッドを呼びます。
しかし、例えばint.Parse("ほげ");
こんなことをしたら変換に失敗してExceptionが発生してしまいます。
try~catchで括ってもいいですが、先に変換可能かどうか調べつつ変換できないのかな?。それ既にありますよ!!
bool int.TryParse(string s,out int result);
こんなのが用意されてます。 変換が出来る場合はメソッドの戻り値がtrueになり、さらにresultに結果が入ります。2
出ました!outキーワード!!
使う場合は
int result = 0;
if(int.TryParse("ほげー",out result) == false){
Debug.Log("変換できませんでした");
}else{
Debug.Log($"変換結果:{result}");
}
こんな感じで、if文の中に入れて、戻り値で分岐させるのがよく見る使い方です。
#辛かったこと
さて、このout
キーワードを使うのに辛かった事が2点あります。
①変数を先に宣言しなくてはいけない
②変数の宣言に(仕組み上)varが使いづらい
①はまぁ、格納先なので、当たり前として。 ②は何かというと、var
というやつは右辺から左辺の型を推論してくれる機能です。右辺がint
ならint
、string
ならstring
に宣言してくれているわけです。
では、今回のケースに無理矢理var
を使うとしたらどうなるでしょう。 int
なので・・・。
var plusResult = 0;
var minusResult = 0;
ってやります? この0はどこから来たんだよという話で。
var
を使いたいがために差し当たりの無い値0を持ってきた に過ぎなく、手段と目的が逆転してます。
#前置きが長かったね
それが、なんと、このように書けるようになります。
if (int.TryParse("ぼげー", out var result) == false)
{
Debug.Log("変換できませんでした");
}
else
{
Debug.Log($"result:{result}");
}
やったー! 宣言が中に入った!! そう、 out
に指定する変数をその場で宣言できるようになりました!
注目してほしいのは、この
else
{
Debug.Log($"result:{result}");
}
この、elseブロックにも result
が使われている事です。
for文の中で宣言した変数なんかは
for(var i = 0;i < 10;++i)
{
Debug.Log(i); //スコープ内
}
Debug.Log(i); //スコープ外なのでエラー
このようにブロックがスコープ(生存範囲)になりますが、このout変数宣言
はその式が含まれているブロックがスコープになります。
まぁ、 1行前に変数宣言があるのと同じ。 と考えておけば大体OKです。
##TryGetComponent
さてさて、ようやく TryGetComponent
の話に移れます。
最初にも言いましたが、 Unity 2019.2 から TryGetComponent
という GetComponent
相当のメソッドが追加されています。
bool TryGetComponent<T>(out T component)
今まで GetComponent
は指定したComponentが取得できなかった場合は null
が返ってくるので、それによって成否を判断していました。
それが、他の Tryメソッド
同様、 成否はbool
を返すようになり、 out変数
に直接結果を入れてくれる仕組みです。
また出た!outキーワード!! デモモウコワクナイ!
具体的には今まで
var sr = GetComponent<SpriteRenderer>();
if (sr != null) //nullかどうかで成否判定
{
sr.color = Color.white;
}
こうだったのが、TryGetComponentが追加されたこと、そして out変数宣言 が使えるようになったことで
if (TryGetComponent<SpriteRenderer>(out var sr)) //TryGetComponentの戻り値自体が 成否、out宣言で中身が入る
{
sr.color = Color.white;
}
このようになります。
どっちが速いかーーーというと、正直あんまり差が無いらしいんですが・・・。(テラシュールブログ様参照)
#ちなみに
TryGetComponent<SpriteRenderer>(out var sr))
これは、Genericsの型パラメータに明示的に SpriteRenderer
を渡しているので、 var sr
というように、 var
で 型推論 が効いている状態です。
逆に、使用される型から型パラメータを型推論 することも出来るので、
TryGetComponent(out SpriteRenderer sr))
こう書いてもよいです。
どちらを使うかは完璧好みでしょうから、お好きな方を使えばいいと思います。 僕は前者です。
え?
TryGetComponent(out var sr))
はダメなのかって? 何をGetComponentするおつもりですか・・・?
##補足
そういえば、今のところあまり見ませんが、
public class Main : MonoBehaviour
{
private SpriteRenderer sr;
void Start()
{
if (TryGetComponent(out sr)) //メンバ変数に格納
{
sr.color = Color.white;
}
}
}
こういう書き方も出来るはずですよね。 変数宣言と TryGetComponent
の箇所が離れすぎていると、何をGetComponentしたのか一瞬戸惑いそうですけれど。
しかし、これは案外アリかもしれません。
僕はよく、Inspectorで自分自身が持っているComponentをセットさせる時に、(RequireComponentなど)
- 実行時に GetComponentは重いらしいから、あんまりやりたくないな。
- RequireComponent指定してあって、確実にそのComponentがあるってわかってるのにInspectorでわざわざドラッグ&ドロップとかかったるいな。
って時に、
using UnityEngine;
[RequireComponent(typeof(SpriteRenderer))] //SpriteRendererを要求
public class ForceWhite : MonoBehaviour
{
[SerializeField]
private SpriteRenderer spriteRenderer;
//このMonobehaviorをアタッチした瞬間、または コンテキストメニューから `Reset` を選んだ時に呼ばれる
void Reset()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
void Start()
{
spriteRenderer.color = Color.white;
}
}
Resetメソッド
を使って、半自動的にInspector参照を解決させるんですが
//このMonobehaviorをアタッチした時、または コンテキストメニューから `Reset` を選んだ時に呼ばれる
void Reset()
{
TryGetComponent(out spriteRenderer); //呼びっぱなし
}
そういう用途(Editor上でしか走らない箇所)であれば、こうやっちゃうのも悪くないのでは???(マサカリ案件)
#なんにせよ
用法用量を守って、ステキなUnityライフを。 それでは。