Unity基础:使用Transform控制物体移动——Translate与position详解

Unity基础:使用Transform控制物体移动------Translate与position详解

大家好,欢迎回到Unity教程系列。在之前的文章中,我们多次在代码中使用transform.Translate()来移动物体。今天这篇文章,我们要深入学习Transform组件提供的所有移动方式------Translate、position赋值、以及它们各自的适用场景。

移动是游戏中最基础也最频繁的操作。角色走路、子弹飞行、平台升降、UI动画------几乎一切动态行为都涉及移动。掌握Transform移动的所有方式,理解每种方式的优缺点,是写出高质量游戏代码的基础。

一、Transform移动的三种基本方式

1.1 方式概览

Unity提供了三种基本方式来移动GameObject:

① transform.Translate():沿指定方向移动指定距离。最直观的"朝某方向移动"。

② transform.position = 新位置:直接将物体设置到世界坐标中的某个位置。适用于需要精确定位的场景。

③ transform.localPosition = 新位置:设置物体相对于父物体的局部位置。在处理父子层级结构时使用。

每种方式有各自的适用场景和注意事项,我们逐一深入讲解。

二、transform.Translate------方向移动

2.1 基本用法

csharp 复制代码
void Update()
{
    // 最基础:沿世界Z轴(前方)移动
    transform.Translate(Vector3.forward * speed * Time.deltaTime);
    
    // 沿世界Y轴(上方)移动
    transform.Translate(Vector3.up * speed * Time.deltaTime);
    
    // 沿自定义方向移动
    Vector3 direction = new Vector3(1, 0, 0.5f).normalized;
    transform.Translate(direction * speed * Time.deltaTime);
}

2.2 Translate的坐标系参数

Translate方法接受第二个参数------坐标系类型:

csharp 复制代码
// 沿世界坐标系的Z轴移动(无论物体朝向如何,都向世界前方移动)
transform.Translate(Vector3.forward * speed * Time.deltaTime, Space.World);

// 沿物体自身坐标系的Z轴移动(物体的"前方")
transform.Translate(Vector3.forward * speed * Time.deltaTime, Space.Self);

// 默认使用Space.Self
transform.Translate(Vector3.forward * speed * Time.deltaTime);
// 等同于
transform.Translate(Vector3.forward * speed * Time.deltaTime, Space.Self);

💡 Space.World vs Space.Self 的选择:

  • Space.World:物体的朝向不影响移动方向。适合需要严格沿世界坐标轴移动的场景。
  • Space.Self:移动方向跟随物体的旋转。适合角色移动------角色转向前进时,移动方向也自动跟着转。

2.3 相对其他Transform的移动

Translate还支持第三个参数------相对Transform:

csharp 复制代码
// 沿cameraTransform的"前方"方向移动
transform.Translate(Vector3.forward * speed * Time.deltaTime, cameraTransform);

这在第三人称游戏中很常用------移动方向相对于摄像机朝向,而不是角色朝向。

2.4 Translate的移动量计算

Translate本质上是执行:position += 移动向量

csharp 复制代码
// 以下两行等价:
transform.Translate(movement);
transform.position += movement;

⚠️ Translate不会"平滑移动"或"动画移动"------它直接改变位置。如果想实现平滑移动,需要自己在调用Translate之前对移动量做插值处理。

三、transform.position------直接赋值

3.1 设置世界坐标

csharp 复制代码
// 将物体精确放置到世界坐标的某个位置
transform.position = new Vector3(0, 5, 0);

// 将物体放在另一个物体的位置
transform.position = targetObject.transform.position;

// 将物体放在另一个物体上方2单位
transform.position = targetObject.transform.position + Vector3.up * 2f;

3.2 position用于移动

csharp 复制代码
void Update()
{
    // 使用position实现移动
    Vector3 movement = new Vector3(
        Input.GetAxis("Horizontal") * speed * Time.deltaTime,
        0,
        Input.GetAxis("Vertical") * speed * Time.deltaTime
    );
    transform.position += movement;
}

📝 上面这段代码和Translate的效果一样。Translate本质就是position += 移动量。两者的区别在于Translate提供了坐标系参数的支持,而手动position赋值需要自己处理坐标转换。

3.3 获取当前位置

csharp 复制代码
// 获取世界坐标
Vector3 currentPos = transform.position;
float height = transform.position.y;

// 保存初始位置(通常用于重置)
Vector3 startPosition;

void Start()
{
    startPosition = transform.position;
}

void ResetPosition()
{
    transform.position = startPosition;
}

四、transform.localPosition------局部坐标

4.1 世界坐标 vs 局部坐标

💡 这是理解Transform移动的关键概念:

  • 世界坐标(World Position):物体在场景中的绝对位置
  • 局部坐标(Local Position):物体相对于父物体的位置
csharp 复制代码
// 获取世界坐标
Vector3 worldPos = transform.position;

// 获取/设置局部坐标
Vector3 localPos = transform.localPosition;
transform.localPosition = new Vector3(1, 0, 0);

4.2 局部坐标的典型使用场景

场景一:在父物体中定位子物体

csharp 复制代码
// 将武器放在角色手中(相对于角色的位置)
weaponTransform.SetParent(handTransform);
weaponTransform.localPosition = new Vector3(0.1f, -0.05f, 0.3f);
weaponTransform.localRotation = Quaternion.identity;

场景二:相对父物体的平滑移动

csharp 复制代码
// 让UI面板从屏幕外滑入
IEnumerator SlideIn()
{
    Vector3 targetLocalPos = Vector3.zero;
    Vector3 startLocalPos = new Vector3(-500, 0, 0);
    
    transform.localPosition = startLocalPos;
    
    float elapsed = 0;
    float duration = 0.5f;
    
    while (elapsed < duration)
    {
        elapsed += Time.deltaTime;
        float t = elapsed / duration;
        transform.localPosition = Vector3.Lerp(startLocalPos, targetLocalPos, t);
        yield return null;
    }
}

4.3 在代码中控制局部坐标

csharp 复制代码
// 检查子物体是否在正确的局部位置
void ValidateChildPosition()
{
    foreach (Transform child in transform)
    {
        if (child.localPosition.magnitude > maxAllowedDistance)
        {
            Debug.LogWarning($"{child.name} 离父物体太远!");
            child.localPosition = Vector3.zero; // 重置到父物体位置
        }
    }
}

五、Transform方向向量

5.1 方向属性

Transform提供了一组用于移动的便捷属性:

csharp 复制代码
// 物体的前方(局部Z轴在世界空间的方向)
Vector3 forward = transform.forward;

// 物体的上方(局部Y轴在世界空间的方向)
Vector3 up = transform.up;

// 物体的右方(局部X轴在世界空间的方向)
Vector3 right = transform.right;

// 反方向
Vector3 backward = -transform.forward;
Vector3 down = -transform.up;
Vector3 left = -transform.right;

💡 这些属性都是只读的,由物体的旋转自动计算得出。

5.2 使用方向向量移动

csharp 复制代码
void Update()
{
    // 向前移动(物体面对的方向)
    transform.position += transform.forward * speed * Time.deltaTime;
    
    // 向右平移(扫射效果)
    if (Input.GetKey(KeyCode.A))
        transform.position -= transform.right * strafeSpeed * Time.deltaTime;
    if (Input.GetKey(KeyCode.D))
        transform.position += transform.right * strafeSpeed * Time.deltaTime;
    
    // 向上飞行
    if (Input.GetKey(KeyCode.Q))
        transform.position += transform.up * flySpeed * Time.deltaTime;
}

5.3 组合移动------WASD移动

csharp 复制代码
void Update()
{
    float horizontal = Input.GetAxis("Horizontal"); // A/D
    float vertical = Input.GetAxis("Vertical");     // W/S
    
    // 基于物体自身朝向的移动
    Vector3 movement = transform.right * horizontal + transform.forward * vertical;
    movement = movement.normalized * speed * Time.deltaTime;
    transform.position += movement;
    
    // 或者使用Translate
    // transform.Translate(movement, Space.World);
}

六、平滑移动技巧

6.1 Vector3.Lerp------线性插值

csharp 复制代码
// 物体平滑移动到目标位置(从当前位置到目标位置的线性过渡)
IEnumerator MoveToPosition(Vector3 target, float duration)
{
    Vector3 start = transform.position;
    float elapsed = 0;
    
    while (elapsed < duration)
    {
        elapsed += Time.deltaTime;
        float t = Mathf.Clamp01(elapsed / duration);
        transform.position = Vector3.Lerp(start, target, t);
        yield return null;
    }
    
    transform.position = target;
}

6.2 指数衰减式跟随

csharp 复制代码
void Update()
{
    // 平滑跟随目标(速度逐渐减慢,永远不会完全到达)
    transform.position = Vector3.Lerp(
        transform.position, 
        target.position, 
        followSpeed * Time.deltaTime
    );
}

📝 注意这种用法(第三个参数乘deltaTime)不是严格的线性插值,而是指数衰减。它在接近目标时会越来越慢,产生自然的"缓入"效果。

6.3 Vector3.MoveTowards------匀速逼近

csharp 复制代码
void Update()
{
    // 以固定速度朝目标移动(不会超过目标)
    transform.position = Vector3.MoveTowards(
        transform.position, 
        target.position, 
        moveSpeed * Time.deltaTime
    );
}

MoveTowards和Lerp的区别:

  • Lerp:速度随剩余距离减小而减小(越近越慢)
  • MoveTowards:恒定速度移动(直到到达目标)

6.4 Vector3.SmoothDamp------平滑阻尼

csharp 复制代码
private Vector3 velocity = Vector3.zero;

void Update()
{
    // 平滑阻尼移动(有惯性效果)
    transform.position = Vector3.SmoothDamp(
        transform.position,
        target.position,
        ref velocity,
        smoothTime
    );
}

SmoothDamp会产生自然的加速和减速效果,非常适合摄像机跟随。

七、Rigidbody移动------物理驱动

7.1 什么时候用Rigidbody而不是Transform

⚠️ 如果物体有Rigidbody组件,应该使用Rigidbody API来移动它,而不是直接修改Transform:

csharp 复制代码
// ❌ 不推荐:直接修改有Rigidbody的物体的Transform
transform.position += movement;

// ✅ 推荐:通过Rigidbody移动
private Rigidbody rb;

void Start()
{
    rb = GetComponent<Rigidbody>();
}

void Update()
{
    Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    rb.velocity = movement * speed;  // 设置速度
    // 或者
    rb.MovePosition(transform.position + movement * speed * Time.deltaTime);
}

为什么? Rigidbody代表物体由物理引擎控制。直接修改Transform绕过了物理引擎,会导致:

  • 碰撞检测可能失败(物体瞬移穿过其他物体)
  • 物理模拟不稳定
  • FixedUpdate中的物理计算出现不连续

八、完整示例------移动系统

csharp 复制代码
using UnityEngine;

public class AdvancedMovement : MonoBehaviour
{
    [Header("移动速度")]
    [SerializeField] private float walkSpeed = 5f;
    [SerializeField] private float runSpeed = 10f;
    [SerializeField] private float crouchSpeed = 2.5f;
    
    [Header("平滑")]
    [SerializeField] private float acceleration = 10f;
    
    private Vector3 currentVelocity;
    private CharacterController characterController;
    
    void Start()
    {
        characterController = GetComponent<CharacterController>();
    }
    
    void Update()
    {
        // 获取输入
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        
        Vector3 inputDirection = transform.right * horizontal + transform.forward * vertical;
        
        // 根据状态选择速度
        float targetSpeed = walkSpeed;
        if (Input.GetKey(KeyCode.LeftShift))
            targetSpeed = runSpeed;
        if (Input.GetKey(KeyCode.LeftControl))
            targetSpeed = crouchSpeed;
        
        // 计算目标移动向量
        Vector3 targetVelocity = inputDirection.normalized * targetSpeed;
        
        // 平滑加速/减速
        currentVelocity = Vector3.Lerp(currentVelocity, targetVelocity, acceleration * Time.deltaTime);
        
        // 应用移动
        if (characterController != null)
        {
            characterController.Move(currentVelocity * Time.deltaTime);
        }
        else
        {
            transform.position += currentVelocity * Time.deltaTime;
        }
    }
}

九、本篇总结

✅ 本文核心知识点回顾:

  • transform.Translate()沿方向移动,支持Space.World/Space.Self坐标系选择
  • transform.position直接设置/获取世界坐标
  • transform.localPosition处理相对于父物体的位置
  • transform.forward/up/right提供物体方向向量,方便基于朝向的移动
  • Lerp平滑插入、MoveTowards匀速逼近、SmoothDamp阻尼跟随
  • 有Rigidbody的物体应使用Rigidbody API(velocity/MovePosition)而非直接修改Transform

📝 下一篇文章将学习使用Transform控制物体旋转------Rotate与LookAt详解。

动手任务

① 分别用Translate和position实现一个物体的圆周运动

② 比较Space.World和Space.Self下Translate的行为差异

③ 用三种平滑移动方式(Lerp/MoveTowards/SmoothDamp)实现物体跟随


关于作者:拥有多年Unity开发经验的游戏开发者,专注Unity系统化教学与最佳实践分享。

相关推荐
FairGuard手游加固2 小时前
Unity小游戏加密:global-metadata.dat与AssetBundle保护
游戏·unity·游戏引擎
狂人开飞机3 小时前
11、UI系统
游戏引擎·godot
真鬼1234 小时前
【Unity AI】Untiy链接cursor与MCP
unity·游戏引擎
WarPigs6 小时前
Unity设置人物位置无效的原因
unity
程序员正茂6 小时前
Unity3d行为树Behavior Designer Pro
unity·行为树
郝学胜-神的一滴7 小时前
C++11 工程级应用 10:减少拷贝,让容器跑得更快
服务器·开发语言·c++·游戏引擎·opengl
玖玥拾7 小时前
Lua 基础语法(八) Unity Huatuo (HybridCLR)
开发语言·unity·游戏引擎·lua
平行云7 小时前
国产GPU云渲染适配实战:驱动兼容、编码调优与多路并发
unity·ue5·webrtc·webgl·实时云渲染·云桌面·像素流送
狂人开飞机7 小时前
10、相机系统
游戏引擎·godot