TryGetComponentとは?
TryGetComponentは、GameObjectにアタッチされたコンポーネントを安全に取得するためのメソッドです。
従来のGetComponentでは「取得してからnullチェックする」という書き方が定番でしたが、TryGetComponentを使うと取得の成否をboolで返してくれるので、if文と組み合わせて自然な流れで書けます。
Unity 2019.2から追加されたメソッドで、現行のUnity 6でも標準的なAPIとして利用できます。
基本的な使い方
TryGetComponentはout変数と組み合わせて使うのが基本です。
コンポーネントが見つかればtrueを返し、out引数に取得結果が入ります。見つからなければfalseを返し、out引数はnullになります。
using UnityEngine;
public class Sample : MonoBehaviour
{
private void Start()
{
// コンポーネントの取得を試みる
if (TryGetComponent(out Rigidbody body))
{
// 取得できた場合のみ処理を行う
// linearVelocityはUnity 6以降の書き方。それ以前のバージョンではvelocity
body.linearVelocity = Vector3.up;
}
}
}
out変数の宣言をif文の中に書けるので、変数のスコープを必要な範囲に絞れるのがポイントです。
他のGameObjectを対象に呼び出すこともできます。
private void OnCollisionEnter(Collision other)
{
// 衝突相手からRigidbodyを取得
if (other.gameObject.TryGetComponent(out Rigidbody body))
{
body.AddForce(Vector3.up * 10f, ForceMode.Impulse);
}
}
GetComponentとの違い
機能面では、GetComponentの結果をnullチェックするのとほぼ同じ結果が得られます。
ただし内部実装に違いがあり、書き方の見た目も変わってきます。
同じ「Rigidbodyが付いていれば速度をリセットする」という処理を書いてみると違いがよく分かります。
// GetComponent + nullチェック
var body = GetComponent<Rigidbody>();
if (body != null)
{
body.linearVelocity = Vector3.zero;
}
// TryGetComponent
if (TryGetComponent(out Rigidbody body))
{
body.linearVelocity = Vector3.zero;
}
行数自体はほとんど変わりませんが、TryGetComponentの方が「取得を試みる」という意図がそのままコードに表れます。
Editor上のGC Allocが発生しない
GetComponentでは、対象のコンポーネントが見つからなかった場合にEditor上でGC Allocが発生することがあります。
これは、対象のコンポーネントが存在しないケースでも、内部でC#側のmanaged wrapperオブジェクトを生成してしまうのが原因です。
TryGetComponentは、このラッパー生成自体を避けるように設計されています。
取得に失敗した場合でも余計なメモリ確保が発生しないため、Editor上でのアロケーションを抑えられます。
ちなみに、ビルド後のアプリでは元々GC Allocが発生しないので、この差は主に開発中のEditor上で意味を持ちます。
実用例:当たり判定で安全にRigidbodyを操作する
衝突時に相手のRigidbodyを操作したいケースは多いと思いますが、相手が必ずしもRigidbodyを持っているとは限りません。
こういう場面でTryGetComponentが活躍します。
using UnityEngine;
public class Bumper : MonoBehaviour
{
[SerializeField] private float _power = 10f;
private void OnCollisionEnter(Collision collision)
{
// 相手にRigidbodyが付いていれば力を加える
if (collision.gameObject.TryGetComponent(out Rigidbody body))
{
var direction = collision.transform.position - transform.position;
body.AddForce(direction.normalized * _power, ForceMode.Impulse);
}
}
}
if文の中だけでbodyを扱えるので、「nullじゃないことを確認したあと使う」というガード処理を一行で書けます。
インターフェイスを取得する用途でも使えます。
GetComponent<T>()はジェネリック型引数にインターフェイスを指定できますが、TryGetComponentでも同じことができます。
using UnityEngine;
public interface IDamageable
{
void TakeDamage(int amount);
}
public class DamageArea : MonoBehaviour
{
[SerializeField] private int _damage = 10;
private void OnTriggerEnter(Collider other)
{
// 相手がIDamageableを実装していればダメージを与える
if (other.TryGetComponent(out IDamageable damageable))
{
damageable.TakeDamage(_damage);
}
}
}
注意点
TryGetComponentは便利ですが、過信は禁物です。
実行時の検索コストは同じ
TryGetComponentは内部でGetComponentと同じ仕組みで検索を行います。
書き方が変わっただけで、コンポーネントを探す処理自体が軽くなるわけではありません。
Updateの中で毎フレーム呼ぶような使い方をすると、GetComponentと同様にパフォーマンスへの影響が出ます。
頻繁に使う参照はAwakeやStartでキャッシュするか、SerializeFieldで事前に設定しておくのが基本方針です。
public class Player : MonoBehaviour
{
private Rigidbody _body;
private void Awake()
{
// 起動時に一度だけ取得してキャッシュする
// 戻り値を確認し、見つからない場合は警告を出しておく
if (!TryGetComponent(out _body))
{
Debug.LogWarning($"{name}にRigidbodyが付いていません", this);
}
}
private void FixedUpdate()
{
// キャッシュ済みの参照を使う
_body.AddForce(Vector3.forward);
}
}
「必ず存在することが保証されている」前提なら、RequireComponentを付けたうえでGetComponentを使う方が意図が明快です。
TryGetComponentは「存在しない可能性がある」ケースに向いています。
最適化の主役はEditor上の挙動
前述の通り、TryGetComponentのメリットはEditor上でGC Allocが発生しない点にあります。
ビルド後のランタイム性能を大きく改善するわけではないので、「TryGetComponentに置き換えるだけで速くなる」というのは誤解です。
「読みやすく、開発中も余計なメモリ確保が起きない書き方」として採用するのが現実的かなと思います。
まとめ
今回はTryGetComponentについて紹介しました。
GetComponentとnullチェックの組み合わせを、out変数で一行にまとめられる書き方です。
コードの意図がはっきりするうえ、Editor上でのGC Allocも抑えられるので、衝突判定など「取得できないこともある」場面では積極的に使っていきたいメソッドかなと思います。
ただし実行時の検索コスト自体はGetComponentと変わらないので、頻繁に呼ぶ場合のキャッシュやSerializeFieldでの事前設定といった基本的な対策は引き続き有効です。場面に応じて使い分けていきましょう。






