第4章 Unity3D脚本编程

Unity3D Script Programming

虚拟现实技术与开发

赋予游戏生命力与可玩性的核心

本章内容概览

脚本基础

  • 脚本的概念与作用
  • C#编程基础回顾
  • 脚本生命周期

面向对象编程

  • 游戏对象增删查改
  • 组件操控
  • 消息传递

核心类与API

  • Vector3、Quaternion
  • Random、Mathf、Time
  • 协程Coroutine
  • Input输入系统

4.1 脚本的概念与作用

脚本(Script):承载在游戏物体上的指令代码片段,描述游戏物体在给定情况下的行为反应

交互性的三个要素

比喻:用户手里的木棍(游戏手柄)→ 木偶(角色模型)→ 提线(脚本逻辑)

Unity脚本语言演变

4.2 Unity编程基础

1. 变量和数据类型

2. 语句与流程控制

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位置和方向转换

静态变量

常用静态方法

4.4.2 Quaternion类

Quaternion(四元数):用于表示旋转,紧凑且不受万向节锁限制

常用方法

欧拉角 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)

协程:在主线程运行的同时开启另一段逻辑处理,实现异步执行

协程特点

协程定义与调用

// 定义协程
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在同一周期中可反复发生