第二天 通过脚本控制物体移动和旋转
一、Unity脚本编程基础认知
1.1 为什么说脚本是Unity的灵魂?
Unity引擎的核心架构采用ECS(Entity-Component-System)模式,脚本作为组件的具体实现,控制着游戏对象的所有行为。统计显示,一个中等规模的Unity项目平均包含300+脚本文件,掌握脚本编程是成为合格Unity开发者的必经之路。
1.2 脚本创建规范
- 在Project窗口右键 -> Create -> C# Script
- 命名规范:
- 使用帕斯卡命名法(如PlayerController)
- 避免使用Unity保留字(如Material/Mesh等)
- 脚本结构模板:
using UnityEngine;public class BasicScript : MonoBehaviour
{// 变量声明区public float moveSpeed = 5f;// 生命周期方法区void Start() { /* 初始化代码 */ }void Update() { /* 每帧更新代码 */ }// 自定义方法区void CustomMethod() { /* 业务逻辑 */ }
}
二、深入解析Unity脚本生命周期
2.1 生命周期全景图
2.2 核心方法深度剖析
2.2.1 Awake vs Start
比较项 | Awake | Start |
---|---|---|
调用时机 | 脚本实例化时立即调用 | 所有Awake执行完毕后调用 |
执行次数 | 仅1次 | 仅1次 |
使用场景 | 组件间的初始化依赖 | 需要其他组件就绪的初始化 |
2.2.2 Update三剑客
-
FixedUpdate
- 固定时间间隔(默认0.02秒)
- 物理相关操作(Rigidbody)
-
Update
- 每帧调用(帧率不稳定)
- 游戏逻辑、输入检测
-
LateUpdate
- Update之后调用
- 摄像机跟随等后处理操作
2.3 生命周期实战演示
public class LifecycleDemo : MonoBehaviour
{void Awake() {Debug.Log("Awake: " + Time.frameCount);}void OnEnable() {Debug.Log("OnEnable: " + Time.frameCount);}void Start() {Debug.Log("Start: " + Time.frameCount);}void FixedUpdate() {Debug.Log("FixedUpdate: " + Time.fixedTime);}void Update() {Debug.Log("Update: " + Time.frameCount);}void LateUpdate() {Debug.Log("LateUpdate: " + Time.frameCount);}
}
三、物体移动控制完全指南
3.1 基础移动方法对比
3.1.1 Transform直接修改
// 瞬移效果(无中间过程)
void Update() {if(Input.GetKeyDown(KeyCode.Space)){transform.position += Vector3.up * 2f;}
}
3.1.2 Translate方法
// 平滑移动(受Time.deltaTime影响)
void Update() {float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Vector3 movement = new Vector3(horizontal, 0, vertical);transform.Translate(movement * moveSpeed * Time.deltaTime);
}
3.1.3 Vector3.Lerp插值
// 渐进式移动(适合摄像机跟随)
public Transform target;
public float smoothTime = 0.3f;void Update() {transform.position = Vector3.Lerp(transform.position, target.position, smoothTime * Time.deltaTime);
}
3.2 移动控制进阶技巧
3.2.1 八方向移动
public float rotationSpeed = 180f;void Update() {Vector3 moveDirection = new Vector3(Input.GetAxisRaw("Horizontal"),0,Input.GetAxisRaw("Vertical"));if(moveDirection != Vector3.zero){Quaternion targetRotation = Quaternion.LookRotation(moveDirection);transform.rotation = Quaternion.RotateTowards(transform.rotation,targetRotation,rotationSpeed * Time.deltaTime);transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);}
}
3.2.2 跳跃物理实现
public float jumpForce = 5f;
private Rigidbody rb;
private bool isGrounded;void Start() {rb = GetComponent<Rigidbody>();
}void Update() {if(Input.GetButtonDown("Jump") && isGrounded){rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);}
}void OnCollisionEnter(Collision collision) {if(collision.gameObject.CompareTag("Ground")){isGrounded = true;}
}void OnCollisionExit(Collision collision) {if(collision.gameObject.CompareTag("Ground")){isGrounded = false;}
}
四、物体旋转控制大师课
4.1 旋转的三种表示方式
-
欧拉角(Euler Angles)
- 直观易理解
- 存在万向节死锁问题
-
四元数(Quaternion)
- 无万向节锁
- 数学运算复杂
-
旋转矩阵(Rotation Matrix)
- 底层数学表示
- 不直接用于开发
4.2 常用旋转方法实战
4.2.1 自转与公转
// 绕自身Y轴旋转
void Update() {transform.Rotate(Vector3.up * 90 * Time.deltaTime);
}// 绕场景原点公转
public float orbitSpeed = 30f;void Update() {transform.RotateAround(Vector3.zero,Vector3.up,orbitSpeed * Time.deltaTime);
}
4.2.2 平滑看向目标
public Transform target;
public float rotationSpeed = 5f;void Update() {Vector3 direction = target.position - transform.position;Quaternion targetRotation = Quaternion.LookRotation(direction);transform.rotation = Quaternion.Slerp(transform.rotation,targetRotation,rotationSpeed * Time.deltaTime);
}
4.3 旋转锁定技巧
// 锁定X轴旋转
void Update() {Vector3 currentRotation = transform.eulerAngles;currentRotation.x = 0;transform.eulerAngles = currentRotation;
}
五、综合实战:太空飞船控制
5.1 需求分析
- WASD控制移动
- 鼠标控制视角
- 空格键推进加速
- Q/E键左右横移
5.2 完整实现代码
public class SpaceShipController : MonoBehaviour
{public float mainThrust = 1000f;public float rotationThrust = 100f;public float lateralThrust = 500f;private Rigidbody rb;private float mouseX;private float mouseY;void Start() {rb = GetComponent<Rigidbody>();Cursor.lockState = CursorLockMode.Locked;}void Update() {ProcessInput();}void FixedUpdate() {HandleMovement();HandleRotation();}private void ProcessInput() {mouseX = Input.GetAxis("Mouse X");mouseY = Input.GetAxis("Mouse Y");}private void HandleMovement() {if(Input.GetKey(KeyCode.Space)) {rb.AddRelativeForce(Vector3.forward * mainThrust * Time.deltaTime);}if(Input.GetKey(KeyCode.Q)) {rb.AddRelativeForce(Vector3.left * lateralThrust * Time.deltaTime);}if(Input.GetKey(KeyCode.E)) {rb.AddRelativeForce(Vector3.right * lateralThrust * Time.deltaTime);}}private void HandleRotation() {rb.AddRelativeTorque(Vector3.up * mouseX * rotationThrust * Time.deltaTime);rb.AddRelativeTorque(Vector3.left * mouseY * rotationThrust * Time.deltaTime);}
}
六、性能优化与调试技巧
6.1 Update优化策略
- 使用Time.deltaTime保证帧率无关
- 避免在Update中执行复杂计算
- 使用协程处理延时任务
IEnumerator DelayedAction(){yield return new WaitForSeconds(2f);// 延时执行的代码
}
6.2 常见问题解决方案
-
物体抖动问题
- 物理更新在FixedUpdate
- 移动代码放在FixedUpdate
-
旋转卡顿现象
- 使用Quaternion.Slerp代替直接赋值
- 检查旋转轴锁定
-
输入响应延迟
- 使用Input.GetAxisRaw获取原始输入
- 降低Fixed Timestep值(Edit -> Project Settings -> Time)
七、扩展学习路线
7.1 进阶学习方向
- 输入系统:New Input System
- 物理材质:Friction/Bounce
- 动画系统:Animator Controller
- 网络同步:Netcode for GameObject
7.2 推荐实践项目
- 第三人称角色控制器
- 载具物理系统
- 第一人称射击游戏
- 物理谜题游戏
八、总结与学习建议
通过本文的学习,你应该掌握:
- Unity脚本生命周期的完整流程
- 多种物体移动控制方法
- 三维空间旋转原理与实践
- 复杂控制系统的实现思路