Unityで transform.position.x += 3 がエラーになる理由

–– struct を返すプロパティとフィールドの違いを徹底解説 ––

対象読者

  • C#/Unity 初~中級者
  • CS1612(「戻り値を変更できない」)エラーで詰まった人
  • このテーマをブログや講義で分かりやすく伝えたい人

1. 再現コードとエラーメッセージ

Transform t = new Transform();
t.position = new Vector3(1, 2, 3);
t.position.x += 3;          // ★ CS1612 発生
error CS1612:
Cannot modify the return value of 'Transform.position'
because it is not a variable

同じロジックでも position をフィールドにした場合はビルド成功 します。違いはどこに?


2. 核心:値型(struct) + プロパティ = 毎回コピー

プロパティ実装(エラーになる)

class Transform
{
    float x, y, z;
    public Vector3 position
    {
        get => new Vector3(x, y, z);          // ← 毎回コピー生成
        set { x = value.x; y = value.y; z = value.z; }
    }
}
  1. t.position を読むたび 一時的な Vector3 が生成
  2. そのコピーの x を書き換えようとする → 一時値は変数でない
  3. C# 規則により CS1612 で拒否

フィールド実装(エラーにならない)

class Transform
{
    public Vector3 position; // 実体そのもの
}

t.position は Transform インスタンス内の実体。x += 3 が成立します。


3. Unity が mutable struct + プロパティを採用する理由

目的詳細
ネイティブ側と同期C++ エンジンに即通知する必要がある
副作用フックset 時にコライダーや親子階層を更新
GC 負荷回避class にすると大量ヒープ確保 → フレーム落ち

ゲームでは リアルタイム性能が最優先。.NET ガイドラインより速度を取った設計です。


4. 正しい書き方:コピー → 変更 → 再代入

Vector3 pos = transform.position; // ① コピー
pos.x += 3;                       // ② 変更
transform.position = pos;         // ③ 再代入

ありがちな落とし穴

NG 例原因
foreach (var p in points) p.x += 1;p はコピー。元リストに反映されない
transform.position.y++;コピー上で ++ しているだけで CS1612

5. パフォーマンスチューニングのヒント

テクニック効果・注意
ref / in パラメータコピー削減。ただし Unity 組み込み API は ref 戻り値を持たない
Burst + Mathematics パッケージSIMD 化された float3 構造体で高速演算
ジョブシステム大量ベクトル演算をマルチスレッド化

6. 付録:完全版サンプルコード

using UnityEngine;

public class PositionDemo : MonoBehaviour
{
    void Start()
    {
        Vector3 pos = transform.position; // コピー
        pos.x += 3;                       // 変更
        transform.position = pos;         // 再代入
        Debug.Log(transform.position);    // (4.0, 2.0, 3.0)
    }
}

まとめ

  • プロパティが struct を返す=コピー扱い
  • コピーのサブメンバーは直接書き換え不可 → CS1612
  • Unity の Transform は性能最優先設計。正しくは「コピー→変更→再代入」
  • 高速化が必要なら ref / in や Burst, ジョブシステムの活用も検討しよう。
訪問数 4 回, 今日の訪問数 4回