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系统化教学与最佳实践分享。