第4章 Unity3D脚本编程
Unity3D Script Programming
本章内容概览
核心类与API
- Vector3、Quaternion
- Random、Mathf、Time
- 协程Coroutine
- Input输入系统
4.1 脚本的概念与作用
脚本(Script):承载在游戏物体上的指令代码片段,描述游戏物体在给定情况下的行为反应
交互性的三个要素
- 输入硬件设备 - 将用户操作传入
- 动画资源 - 3D角色上的动画
- 脚本控制器 - 连接美术资源与硬件控制(核心地位)
比喻:用户手里的木棍(游戏手柄)→ 木偶(角色模型)→ 提线(脚本逻辑)
Unity脚本语言演变
- 早期:Boo、JavaScript、C#三种语言
- 现在:C#占据主导地位(85%),JavaScript次之(14%)
- Unity 2018后:内置MonoDevelop编辑器,建议使用Visual Studio
4.2 Unity编程基础
1. 变量和数据类型
- 数值变量 - int, float, double
- 字符串变量 - string
- 逻辑值 - bool
- 数组 - 相同类型数据的集合
- 枚举 - 命名的标识符集合
- 组件变量 - Rigidbody, Camera, Renderer等
2. 语句与流程控制
- 顺序语句 - 程序逐行执行
- 条件分支 - if, switch
- 循环流程 - for, while
3. 函数、类、对象
- 函数/方法 - 预先定义的功能代码段
- 类 - 具有相同功能和属性的对象归类
- 对象 - 类的实例
4.2.1 创建脚本与模板
创建脚本
Project视图 → 右键 → Create → C# Script
默认脚本模板
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SampleClass : MonoBehaviour {
void Start () {
// 初始化代码
}
void Update () {
// 每帧更新代码
}
}
重要规则:类名必须与文件名完全一致,类用public修饰,继承自MonoBehaviour
4.2.2 MonoBehaviour类与脚本生命周期
Unity引擎通过MonoBehaviour类定义脚本生命周期,自动执行30多个函数
| 函数名 |
触发时机 |
用途 |
| Awake |
脚本实例被创建时 |
对象初始化(比Start还早) |
| OnEnable |
对象变为可用状态时 |
激活组件 |
| Start |
Update第1次执行前 |
游戏对象初始化 |
| Update |
每帧调用 |
更新游戏场景和状态 |
| FixedUpdate |
固定时间间隔调用 |
更新物理状态 |
| LateUpdate |
紧跟Update后每帧调用 |
相机更新、追踪 |
| OnDestroy |
对象被销毁时 |
清理资源 |
4.3.1 游戏对象的增删查改
1. 查找场景对象
// 按名称查找
objHero = GameObject.Find("Hero");
// 按标签查找(返回数组)
FoundEnemys = GameObject.FindGameObjectsWithTag("enemy");
// 按组件类型查找
Object.FindObjectsOfType<Collider>();
2. 设置父子关系
kid.transform.parent = father.transform;
int kidsCount = father.transform.childCount;
3. 创建对象
// 创建基本几何体
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// 实例化预制体
Instantiate(prefab, position, rotation);
4.3.1 删除游戏对象
// 删除游戏对象本身
Destroy(gameObject);
// 删除脚本实例
Destroy(this);
// 删除组件
Destroy(GetComponent<Rigidbody>());
// 延迟删除(5秒后)
Destroy(gameObject, 5);
重要提示:查找函数开销较大,应放在Start()或Awake()中,不要放在Update()中
4.3.2 脚本对组件的操控
获取组件
Rigidbody rgd = transform.GetComponent<Rigidbody>();
Renderer rend = GetComponent<Renderer>();
添加组件
cube1.AddComponent<AudioSource>();
禁用/启用组件
cube1.GetComponent<AudioSource>().enabled = false;
cube1.GetComponent<AudioSource>().enabled = true;
销毁组件
Destroy(this.gameObject.GetComponent<AudioSource>());
4.3.2 组件间消息传递
SendMessage方法
// 调用对象上的方法
gameObject.SendMessage("MethodName");
// 广播给所有子对象
gameObject.BroadcastMessage("MethodName");
// 发送给父对象
gameObject.SendMessageUpwards("MethodName");
Invoke方法(延迟调用)
// 2秒后调用方法
Invoke("MethodName", 2f);
// 2秒后调用,之后每3秒重复调用
InvokeRepeating("MethodName", 2f, 3f);
注意:Invoke不能接受带参数的方法;Time.timeScale=0时Invoke无效
4.4.1 Vector3类
Vector3:表示3D向量或点,贯穿整个Unity位置和方向转换
静态变量
- Vector3.zero - (0, 0, 0)
- Vector3.one - (1, 1, 1)
- Vector3.up - (0, 1, 0)
- Vector3.right - (1, 0, 0)
- Vector3.forward - (0, 0, 1)
常用静态方法
- Distance(a, b) - 返回两点间距离
- Lerp(from, to, t) - 线性插值
- MoveTowards(current, target, maxDistance) - 向目标移动
- Dot(a, b) - 点乘
- Cross(a, b) - 叉乘
4.4.2 Quaternion类
Quaternion(四元数):用于表示旋转,紧凑且不受万向节锁限制
常用方法
- Quaternion.Euler(x, y, z) - 欧拉角转四元数
- Quaternion.LookRotation - 看向目标方向
- Quaternion.Slerp - 球面插值
- Quaternion.FromToRotation - 从方向A转到方向B
- Quaternion.identity - 无旋转
欧拉角 vs 四元数
| 欧拉角 |
四元数 |
| 直观易懂 |
避免万向节锁 |
| 可能产生万向节锁 |
能进行增量旋转 |
| 表达方式不唯一 |
给定方位表达方式唯一(互为负) |
4.4.3-4.4.6 其他核心类
Random类
// 返回0-5的随机整数[0,5)
Random.Range(0, 5);
// 返回0.0-5.0的随机浮点数[0,5]
Random.Range(0.0f, 5.0f);
Mathf类
- Abs - 绝对值
- Clamp - 限制范围
- Lerp - 插值
- Sin/Cos/Tan - 三角函数
- Sqrt - 平方根
Time类
- Time.time - 游戏开始到现在的时间
- Time.deltaTime - 上一帧到当前帧的时间(秒)
- Time.timeScale - 时间缩放(1正常,<1慢放,>1快放)
- Time.fixedDeltaTime - 固定时间间隔
Time.deltaTime常用于保证不同帧率下移动速度一致
4.4.4 协程(Coroutine)
协程:在主线程运行的同时开启另一段逻辑处理,实现异步执行
协程特点
- 不是真正的多线程
- 任一时刻只有一个协程在运行
- 使用yield语句暂停,在指定时机恢复
协程定义与调用
// 定义协程
IEnumerator DelayAction() {
yield return new WaitForSeconds(1f); //等待1秒
// 执行后续代码
}
// 调用协程
StartCoroutine(DelayAction());
4.4.7 Input类
Input类:处理键盘、鼠标、手柄、触摸等输入设备
常用方法
| 方法 |
功能 |
| GetAxis("Horizontal") |
获取水平轴输入(A/D或左右箭头) |
| GetAxis("Vertical") |
获取垂直轴输入(W/S或上下箭头) |
| GetKeyDown(KeyCode.Space) |
空格键按下 |
| GetMouseButtonDown(0) |
鼠标左键按下 |
| GetTouch(0) |
获取触摸信息 |
触摸输入
if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) {
Vector2 delta = Input.GetTouch(0).deltaPosition;
}
本章小结
重点掌握
- 脚本生命周期(Awake→Start→Update→FixedUpdate→LateUpdate)
- 游戏对象查找、创建、删除
- 组件获取、添加、禁用
- Vector3、Quaternion使用
- 协程概念与使用
- Input输入处理
核心代码
// 查找对象
GameObject.Find("Name");
// 获取组件
GetComponent<Rigidbody>();
// 实例化
Instantiate(prefab, pos, rot);
// 协程
StartCoroutine(Method());
思考题:OnEnable、Awake、Start运行时的发生顺序?
答案:Awake → OnEnable → Start,OnEnable在同一周期中可反复发生