MRが楽しい

MRやVRについて学習したことを書き残す

ラムダ演算子を使ってアクセス時に変数を初期化する

本日は C# と Unity の小ネタ枠です。
Get/Set アクセサを使ってアクセス時に変数を初期化する方法を Unity のコンポーネントで活用してみます。

アクセスするタイミングで初期化を行いたいケース

問題ケースとしてコンポーネントにアクセスするタイミングで変数の初期化を行いたいケースを想定します。
以下のような、オブジェクトをカメラの正面に移動するコンポーネントを介してオブジェクトを移動するケースを試しました。
・CameraTracker.cs

using UnityEngine;

/// <summary>
/// オブジェクトをメインカメラの位置を元に移動するコンポーネント
/// </summary>
public class CameraTracker : MonoBehaviour
{
    private Transform cameraTransform;
    
    void Start()
    {
        cameraTransform = Camera.main.transform;
    }

    public void MoveToFront()
    {
        // 正面1mの位置に移動する
        this.transform.position = cameraTransform.position + cameraTransform.forward * 1.0f;
    }
}

・ModelControl.cs

using UnityEngine;

public class ModelControl : MonoBehaviour
{
    [SerializeField]
    private CameraTracker cameraTracker;
    
    void Start()
    {
        // 起動時に対象オブジェクトを有効化して移動する
        cameraTracker.gameObject.SetActive(true);
        cameraTracker.MoveToFront();
    }
}

シーン上では移動対象のオブジェクトは初期状態で無効化されており、シーンの再生と共に有効化され、移動の処理が呼び出されます。

本シーンを再生すると Null 参照のエラーが発生します。
メンバー変数を初期化する Start 関数はオブジェクトを有効化した次のフレームで実行されるため、移動の処理を呼び出した時点ではまだ変数が初期化されていないためです。

ラムダ式を使って変数のアクセス時に変数を初期化する

この問題を解決するため、ラムダ式を使って変数のアクセス時に変数がまだ初期化されていなければ初期化を行うようにします。
スクリプトを以下のように修正しました。
・CameraTracker.cs

using UnityEngine;

/// <summary>
/// オブジェクトをメインカメラの位置を元に移動するコンポーネント
/// </summary>
public class CameraTracker : MonoBehaviour
{
    private Transform cameraTransform;
    
    // アクセス時にcameraTransformがnullの場合にCamera.main.transformを代入して初期化する
    private Transform CameraTransform =>
        cameraTransform ??
        (cameraTransform = Camera.main.transform);

    public void MoveToFront()
    {
        // 正面1mの位置に移動する
        this.transform.position = CameraTransform.position + CameraTransform.forward * 1.0f;
    }
}

これでシーンを再生してもエラーが発生しなくなります。
アクセス時に必ず変数の初期化が行われるので Unity の MonoBehaviour でフィールド変数を扱う際に役立ちます。

Get/Setアクセサを利用する場合の方法は以下の記事を参照ください。
bluebirdofoz.hatenablog.com