Unityでオブジェクトの座標を取得・変更するには、transform.positionを使います。親を基準に指定したいときはlocalPosition、毎フレーム動かすときは移動量にTime.deltaTimeを掛ける、と分けて考えるとわかりやすくなります。

やりたいことから選ぶ
現在位置を読む → transform.position
指定位置へ一度で移す → positionへVector3を代入
一定速度で動かす → positionに「方向 × 速度 × deltaTime」を加算
親からの位置を読む・変える → localPosition

Unity 6.0の公式仕様をもとに、3DのCubeで試せる例を紹介します。位置・回転・大きさの全体像はTransformの基本とWorld・Localの違いも参考にしてください。

positionとlocalPositionの違い

positionはワールド空間の位置、localPositionは親のTransformを基準にした位置です。子オブジェクトのInspectorにあるPosition欄は、親から見たローカル座標を表示します。親がなければ両者は同じ値になります。

親と子の座標の例
親の位置:(-3, 0, -3)
子のlocalPosition:(0, 0, 0)
↓
子のposition:(-3, 0, -3)

子のlocalPositionを(2, 0, 0)にすると、子のpositionは(-1, 0, -3)になります。
※親の回転が(0, 0, 0)、Scaleが(1, 1, 1)の場合です。

ワールド座標だから親の影響を受けない、という意味ではありません。親を動かせば子のpositionも変わります。親の回転やScaleも、ローカル座標からワールド座標を計算するときに反映されます。localPositionは、オブジェクト自身の正面方向を表す値でもありません。

準備:親と子のCubeを用意する

  1. HierarchyでCreate Emptyを選び、Parentという空のGameObjectを作ります。Parentは他のオブジェクトの子にしません。
  2. ParentのPositionを(-3, 0, -3)、Rotationを(0, 0, 0)、Scaleを(1, 1, 1)にします。
  3. 3D Object → CubeでCubeを作り、HierarchyでParentの子にします。その後、CubeのInspectorのPositionを(0, 0, 0)に設定します。
  4. 以下の例は、ファイル名とクラス名をそろえて保存し、Cubeにアタッチします。移動用スクリプトは同時に複数有効にせず、1つずつ試してください。
親GameObjectの子にCubeを置き、スクリプトを追加したUnity画面
Cubeを子にした準備画面。InspectorのPositionは親から見た座標です。

現在の座標を取得する

PositionLogger.csをCubeに付けて再生すると、Consoleにワールド座標とローカル座標を1回ずつ表示します。

using UnityEngine;

public class PositionLogger : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("World: " + transform.position);
        Debug.Log("Local: " + transform.localPosition);
    }
}

準備どおりの配置なら、Worldは(-3, 0, -3)、Localは(0, 0, 0)です。Xだけを読む場合はtransform.position.x、親から見たYだけならtransform.localPosition.yを使えます。

ワールド座標をConsoleで確認する操作例。
同じCubeのローカル座標を確認する操作例。

指定した座標へ移動する

PositionSetter.csは、再生開始時にCubeをワールド原点へ移す例です。positionへの代入はその位置へすぐに移す処理なので、移動途中のアニメーションは作りません。

using UnityEngine;

public class PositionSetter : MonoBehaviour
{
    private void Start()
    {
        transform.position = Vector3.zero;
        Debug.Log("World: " + transform.position);
        Debug.Log("Local: " + transform.localPosition);
    }
}

Vector3.zeroはnew Vector3(0f, 0f, 0f)と同じ値です。好きな座標へ移す場合は、例えばnew Vector3(2f, 1f, -3f)を代入します。準備した親の位置が(-3, 0, -3)なら、ワールド原点へ移したCubeのlocalPositionは(3, 0, 3)になります。

親と同じ位置へ移したい場合は、transform.localPosition = Vector3.zeroとします。代入先がpositionかlocalPositionかで、基準が変わります。

指定したワールド座標へCubeを移す操作例。

Xだけを変更するには、一度取り出して戻す

transform.position.x = 2fのように直接書くと、C#のコンパイルエラーになります。positionから得るVector3は値型なので、変数へ取り出して変更し、positionへ戻します。次の3行はStartなどのメソッド内に書く例です。

Vector3 nextPosition = transform.position;
nextPosition.x = 2f;
transform.position = nextPosition;

この方法ならYとZを維持できます。親を基準にXだけを変える場合は、取得と代入の両方をlocalPositionにそろえてください。

一定速度で移動する:+=とTime.deltaTime

WorldMover.csは、ワールドのXプラス方向へ毎秒2ユニット移動する例です。InspectorのSpeedで速さを調整できます。

using UnityEngine;

public class WorldMover : MonoBehaviour
{
    [SerializeField] private float speed = 2f;

    private void Update()
    {
        transform.position += Vector3.right * speed * Time.deltaTime;
    }
}

1フレームの移動量 = 方向 × 毎秒の速度 × 経過秒数
毎フレーム0.01だけ加算すると、30fpsでは毎秒0.3、60fpsでは毎秒0.6と速さが変わります。Time.deltaTimeを掛けると、フレーム間隔に合わせて移動量を調整できます。

+=は現在位置への加算、-=は減算です。1回だけ呼べば1回分だけ位置が変わり、Updateで繰り返せば連続した移動になります。毎フレームのDebug.LogはConsoleを埋めるので、座標確認は必要なときだけ行いましょう。

座標への加算でCubeが動く様子。移動速度はスクリプトの設定により変わります。

向いている方向へ移動する:Translate

ForwardMover.csは、オブジェクト自身の前方(ローカルZ方向)へ動かします。CubeのRotationのYを90にして、ワールドのZ方向への移動との違いを見てみましょう。

using UnityEngine;

public class ForwardMover : MonoBehaviour
{
    [SerializeField] private float speed = 2f;

    private void Update()
    {
        Vector3 step = Vector3.forward * speed * Time.deltaTime;
        transform.Translate(step, Space.Self);
    }
}

Translateの第2引数を省略するとSpace.Selfです。Space.Worldにすると、オブジェクトの向きにかかわらずワールド軸を基準に移動します。position += Vector3.forward …はワールドのZ方向なので、Space.SelfのTranslateと常に同じ動きになるわけではありません。

Vector3.forwardやVector3.rightの値は、Vector3の座標・加算・正規化で詳しく紹介しています。

目的地で止める:MoveTowards

目標地点まで一定速度で進めたい場合は、Vector3.MoveTowardsが便利です。MoveToPoint.csを付け、InspectorのTarget Positionにワールド座標を設定します。

using UnityEngine;

public class MoveToPoint : MonoBehaviour
{
    [SerializeField] private Vector3 targetPosition = new Vector3(3f, 0f, 0f);
    [SerializeField, Min(0f)] private float speed = 2f;

    private void Update()
    {
        float step = Mathf.Max(0f, speed) * Time.deltaTime;
        transform.position = Vector3.MoveTowards(
            transform.position, targetPosition, step);
    }
}

移動量が目的地までの距離を超える場合は目的地の値になるため、行き過ぎません。これは所要時間を指定する例ではなく、毎秒の速度で動かす例です。

位置が変わらない・戻るときの確認点

  1. Consoleにコンパイルエラーがなく、スクリプトとGameObjectが有効か確認します。ファイル名とクラス名も一致させます。
  2. positionとlocalPositionを混同していないか確認します。子のInspectorのPositionとワールド座標が違うのは正常です。
  3. 別のスクリプト、Animator、NavMeshAgentなどが同じ位置を書き換えていないか確認します。移動処理は1つに絞って試します。
  4. Time.timeScaleが0だと、deltaTimeを掛けた移動も止まります。ポーズ中の動作かどうかを確認します。

Rigidbodyで物理挙動を扱うオブジェクトは、Transformだけで移動させる例と分けて考えます。キネマティックなRigidbodyを連続移動させる場合は、FixedUpdateからMovePositionを使う方法があります。詳しくはRigidbodyでの移動方法を参照してください。

まとめ

まずはpositionで取得 → Vector3を代入して変更 → deltaTimeを掛けて連続移動の順に試してみましょう。親との位置関係はlocalPosition、自身の向きに沿う移動はTranslate、目的地で止める移動はMoveTowardsと使い分けると整理しやすくなります。

参考:Unity公式:position / localPosition / Translate / Time.deltaTime / MoveTowards / Microsoft:CS1612