【Unity】Vector3(構造体)に自分自身の値を変更する拡張メソッドを定義する

Unity の Vector3 (Vector2 など構造体)に自分自身を書き換える処理を追加する拡張メソッドの定義方法の紹介です。

前提として以下のように Vector3 に拡張メソッドを定義して値を変更しようとしても値は変わらない事を確認します。

public static class Vector3Extensions
{
    // 特に意味はないけどXを書き換え拡張メソッド
    public static void SetX(this Vector3 self, float x) => self.x = x;
}

vra vec3 = Vector3.zero;
vec3.SetX(100);

Debug.Log(vec3);
// > (0, 0, 0) ★★★変わってない!

これは、Vector3 の宣言が class ではなく struct になっていることが原因です。Vector3 を確認すると「public struct」となっています。

// ★classではなくstructになってる
public struct Vector3 : IEquatable<Vector3>, IFormattable
{

この部分が、struct になってるとメソッドに渡すと受け取った側は内容がコピーされた別物が渡されて、その値を書き換えたところで別物を書き換えただけで元の値には影響が出ません。

... => self.x = x; // selfは元の値がコピーされた別物

そこで受け取ったときにコピーした別物ではなく、同じものを渡すようにするために、ref キーワードを以下の通り追加します。

public static class Vector3Extensions
{
    // thisの前にrefを追加する
    public static void SetX(ref this Vector3 self, float x) => self.x = x;
}

vra vec3 = Vector3.zero;
vec3.SetX(100);

Debug.Log(vec3);
// > (100, 0, 0) ★★★変わってる

今回は変更した値が表示されます。

Immutableな構造体も書き換える

余談ですが、ref キーワードで構造体を参照渡しする場合、以下のようにインスタンスを再生成する方法でも値が変更できます。こちらは一度作成したら後から値が変更できない構造体 (≒immutable な構造体)の値の変更を拡張メソッドで定義しる時に利用できます。

Vector3 も以下の方法で値が変更できますが少し処理効率が悪く、意味も無いので使用しません。

public static class Vector3Extensions
{
    public static void SetX2(this ref Vector3 self, float x)
    {
        // インスタンスを新規作成して代入しても値が変わる
        self = new Vector3() { x = x, y = self.y, z = self.z };
    }
}

var vec3 = new Vector3();
vec3.SetX2(100);
Console.WriteLine(vec3);
// >(100, 0, 0) ★これでも値が変わる

ちょっとしたことですがこれで拡張メソッド作成の幅が広がると思います。