三、Unity 核心模块 三、Unity 核心模块详解 3.1 场景管理 (Scene Management) 场景(Scene)是 Unity 中最基本的组织单元,它代表了游戏世界中的一个关卡、一个菜单、或者任何独立的交互空间。场景管理模块负责加载、卸载、切换和组织这些场景,是构建游戏流程和关卡结构的核心。 3.1.1 核心概念 场景 (Scene): 包含游戏对象、资源和环境的容器。每个场景都是一个独立的文件,可以单独编辑和加载。 场景层级 (Hierarchy): 场景中所有 GameObject 的树状结构,用于组织和管理场景中的对象。 场景加载模式 (LoadSceneMode): 定义场景加载的方式,主要有 (替换当前场景) 和 (添加到当前场景)。
场景(Scene)是 Unity 中最基本的组织单元,它代表了游戏世界中的一个关卡、一个菜单、或者任何独立的交互空间。场景管理模块负责加载、卸载、切换和组织这些场景,是构建游戏流程和关卡结构的核心。
3.1.1 核心概念
场景 (Scene): 包含游戏对象、资源和环境的容器。每个场景都是一个独立的文件,可以单独编辑和加载。
场景层级 (Hierarchy): 场景中所有 GameObject 的树状结构,用于组织和管理场景中的对象。
场景加载模式 (LoadSceneMode): 定义场景加载的方式,主要有 Single (替换当前场景) 和 Additive (添加到当前场景)。
异步加载 (AsyncOperation): 非阻塞地加载场景,避免加载过程中游戏卡顿。
3.1.2 代码实践与详解
using UnityEngine; using UnityEngine.SceneManagement; using System.Collections; public class SceneController : MonoBehaviour { public void LoadSingleScene(string sceneName) { SceneManager.LoadScene(sceneName, LoadSceneMode.Single); } public void LoadAdditiveScene(string sceneName) { SceneManager.LoadScene(sceneName, LoadSceneMode.Additive); } public void UnloadAdditiveScene(string sceneName) { SceneManager.UnloadSceneAsync(sceneName); } public void ReloadCurrentScene() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } public IEnumerator LoadSceneAsync(string sceneName) { AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName); while (!asyncLoad.isDone) { float progress = Mathf.Clamp01(asyncLoad.progress / 0.9f); // 0.9 是因为 AsyncOperation.progress 在场景准备就绪前最大值为 0.9 Debug.Log("场景加载进度: " + progress * 100 + "%"); yield return null; } Debug.Log("场景加载完成!"); } }
代码详解:
SceneManager.LoadScene(string sceneName, LoadSceneMode mode): 加载指定名称的场景。LoadSceneMode.Single 会卸载当前场景并加载新场景,而 LoadSceneMode.Additive 会将新场景添加到当前场景中。
SceneManager.UnloadSceneAsync(string sceneName): 异步卸载指定的附加场景。
SceneManager.GetActiveScene(): 获取当前激活的场景。
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(string sceneName): 异步加载场景,返回 AsyncOperation 对象,可以用来监测加载进度。
asyncLoad.isDone: 判断异步加载是否完成。
asyncLoad.progress: 获取异步加载的进度,范围 0 到 0.9 (场景准备就绪前)。
3.1.3 Mermaid 图表
图表解释:
该图表展示了一个简单的游戏场景管理流程,使用 Additive 模式加载关卡场景,并在关卡完成后卸载,体现了场景的动态加载和卸载过程。
GameObject 和 Component 系统是 Unity 引擎架构的核心。一切游戏对象都基于 GameObject,而 GameObject 的行为和功能则由附加在其上的 Component 决定。这种组合方式赋予了 Unity 极高的灵活性和可扩展性。
3.2.1 核心概念
GameObject (游戏对象): Unity 世界中的基本实体,是所有物体的容器。它本身不具备任何功能,只是一个空壳。
Component (组件): 赋予 GameObject 特定行为和功能的模块。例如,Transform 组件负责位置、旋转和缩放,Renderer 组件负责渲染,Rigidbody 组件负责物理模拟。
Prefab (预制体): 预先配置好的 GameObject 及其 Component 的模板,可以重复使用和实例化,提高开发效率和一致性。
Transform 组件: 每个 GameObject 都有且只有一个 Transform 组件,负责管理 GameObject 在世界空间中的位置、旋转和缩放。
组件的组合: 通过组合不同的 Component,可以创建各种复杂的游戏对象,例如角色、道具、环境物体等。
3.2.2 代码实践与详解
using UnityEngine; public class GameObjectComponentExample : MonoBehaviour { void Start() { // 创建一个新的空 GameObject GameObject newGameObject = new GameObject("MyGameObject"); // 获取 GameObject 的 Transform 组件 Transform transformComponent = newGameObject.transform; // 设置 GameObject 的位置 transformComponent.position = new Vector3(1, 2, 3); // 添加一个 MeshRenderer 组件,使其可以被渲染 MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>(); // 添加一个 BoxCollider 组件,使其具有碰撞检测功能 BoxCollider boxCollider = newGameObject.AddComponent<BoxCollider>(); // 获取已添加的组件 MeshRenderer getMeshRenderer = newGameObject.GetComponent<MeshRenderer>(); BoxCollider getBoxCollider = newGameObject.GetComponent<BoxCollider>(); if (getMeshRenderer != null) { Debug.Log("成功获取 MeshRenderer 组件"); } if (getBoxCollider != null) { Debug.Log("成功获取 BoxCollider 组件"); } // 禁用 GameObject newGameObject.SetActive(false); // 销毁 GameObject // Destroy(newGameObject); // 取消注释以销毁 GameObject } }
代码详解:
new GameObject(string name): 创建一个新的空 GameObject,并可以指定名称。
gameObject.transform: 访问当前 GameObject 的 Transform 组件。每个 MonoBehaviour 脚本都继承自 MonoBehaviour,其中 gameObject 属性指向脚本所附加的 GameObject。
gameObject.AddComponent<T>(): 给 GameObject 添加一个类型为 T 的组件。
gameObject.GetComponent<T>(): 获取 GameObject 上类型为 T 的组件。如果不存在该组件,则返回 null。
gameObject.SetActive(bool active): 激活或禁用 GameObject。禁用 GameObject 会使其及其所有子对象停止更新和渲染。
Destroy(GameObject obj): 销毁 GameObject,将其从场景中移除。注意 Destroy 是异步操作,对象会在当前帧结束时被销毁。
3.2.3 Mermaid 图表
图表解释:
该图表清晰地展示了 GameObject 与 Component 之间的关系,GameObject 作为容器,包含一个 Transform 组件和多个其他 Component,Component 为 GameObject 提供各种功能。
Unity 的脚本系统允许开发者使用 C# 语言编写脚本,控制游戏逻辑、交互行为和自定义组件。脚本系统是 Unity 开发的核心,赋予了游戏无限的可能性。
3.3.1 核心概念
C# 语言: Unity 主要使用的脚本语言,语法简洁、功能强大,易于学习和使用。
MonoBehaviour: Unity 脚本的基类,所有自定义脚本都需要继承自 MonoBehaviour。它提供了 Unity 引擎提供的各种生命周期函数和 API。
生命周期函数 (Lifecycle Functions): Unity 在游戏运行的不同阶段自动调用的函数,例如 Start (初始化)、Update (每帧更新)、FixedUpdate (固定帧率更新)、OnGUI (GUI 绘制) 等。
事件函数 (Event Functions): 响应特定事件的函数,例如 OnMouseDown (鼠标按下)、OnCollisionEnter (碰撞开始)、OnTriggerEnter (触发器进入) 等。
API (Application Programming Interface): Unity 引擎提供的各种类、函数和属性,用于访问引擎功能和控制游戏行为。
3.3.2 代码实践与详解
using UnityEngine; public class SimpleScript : MonoBehaviour { public float speed = 5f; private Rigidbody rb; void Start() { rb = GetComponent<Rigidbody>(); if (rb == null) { Debug.LogError("Rigidbody 组件未找到!"); } Debug.Log("脚本 Start 函数被调用"); } void Update() { float horizontalInput = Input.GetAxis("Horizontal"); float verticalInput = Input.GetAxis("Vertical"); Vector3 movement = new Vector3(horizontalInput, 0f, verticalInput) * speed * Time.deltaTime; if (rb != null) { rb.MovePosition(rb.position + movement); } } void FixedUpdate() { // 物理相关的更新逻辑,例如力、扭矩等,应放在 FixedUpdate 中,以保证物理模拟的稳定性。 // 此处示例 Update 中已经处理了移动,FixedUpdate 可以留空或添加其他物理相关逻辑。 } void OnCollisionEnter(Collision collision) { Debug.Log("碰撞发生! 与物体: " + collision.gameObject.name); } void OnMouseDown() { Debug.Log("鼠标点击了此物体!"); } }
代码详解:
public float speed = 5f;: 声明一个公共的浮点型变量 speed,可以在 Inspector 面板中调整。
private Rigidbody rb;: 声明一个私有的 Rigidbody 组件变量 rb。
Start(): 在脚本实例化的第一帧调用,用于初始化。
Update(): 每帧调用,用于处理游戏逻辑、输入和动画等。
FixedUpdate(): 以固定的时间间隔调用,用于处理物理相关的更新,保证物理模拟的稳定性。
Input.GetAxis("Horizontal") 和 Input.GetAxis("Vertical"): 获取水平和垂直方向的输入,通常对应键盘的 WASD 或方向键,以及手柄的摇杆。
Time.deltaTime: 上一帧到当前帧的时间间隔,用于使移动速度与帧率无关。
rb.MovePosition(Vector3 position): 移动 Rigidbody 到指定位置,用于物理移动。
OnCollisionEnter(Collision collision): 当与其他带有 Collider 的物体发生碰撞时调用。
OnMouseDown(): 当鼠标按下在带有 Collider 的物体上时调用。
3.3.3 Mermaid 图表
图表解释:
该图表展示了 MonoBehaviour 脚本的生命周期流程,包括初始化、循环更新、各种事件响应以及销毁阶段,帮助理解脚本的执行顺序和时机。
渲染系统负责将游戏世界中的场景、模型、材质、灯光等元素转化为最终呈现在屏幕上的图像。Unity 提供了灵活的渲染管线,可以实现各种视觉效果和优化性能。
3.4.1 核心概念
渲染管线 (Render Pipeline): 渲染过程的流程,包括剔除、几何处理、光照计算、着色、后处理等步骤。Unity 提供了内置渲染管线 (Built-in Render Pipeline)、通用渲染管线 (Universal Render Pipeline, URP) 和高清渲染管线 (High Definition Render Pipeline, HDRP) 三种主要渲染管线。
相机 (Camera): 定义场景的观察视角,决定哪些物体会被渲染以及如何渲染。
灯光 (Light): 照亮场景,影响物体表面的颜色和阴影。Unity 支持多种灯光类型,例如方向光、点光、聚光灯和区域光。
材质 (Material): 定义物体表面的外观属性,例如颜色、纹理、反射率、粗糙度等。
着色器 (Shader): 控制材质如何被渲染的程序,决定物体表面的最终颜色。
后处理 (Post-Processing): 在渲染完成后对图像进行处理,添加各种视觉效果,例如景深、色彩校正、Bloom、抗锯齿等。
3.4.2 代码实践与详解
由于渲染系统涉及较为复杂的概念和 API,这里提供一个简单的代码示例,演示如何修改材质的颜色:
using UnityEngine; public class MaterialColorChange : MonoBehaviour { public Color targetColor = Color.red; private Renderer objectRenderer; void Start() { objectRenderer = GetComponent<Renderer>(); if (objectRenderer == null) { Debug.LogError("Renderer 组件未找到!"); return; } } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { ChangeMaterialColor(); } } void ChangeMaterialColor() { if (objectRenderer != null) { objectRenderer.material.color = targetColor; Debug.Log("材质颜色已更改为: " + targetColor); } } }
代码详解:
GetComponent<Renderer>(): 获取 GameObject 上的 Renderer 组件,Renderer 组件负责将 Mesh 或 Sprite 渲染到屏幕上。
objectRenderer.material: 访问 Renderer 组件的材质实例。注意直接修改 material 属性会修改材质的共享实例,影响所有使用该材质的对象。如果只想修改当前对象的材质,应该使用 objectRenderer.sharedMaterial 的副本,或者使用 Instantiate(objectRenderer.sharedMaterial) 创建一个新的材质实例。
objectRenderer.material.color = targetColor;: 修改材质的颜色属性。
3.4.3 Mermaid 图表 (URP 简化渲染管线示例)
图表解释:
该图表展示了 URP 简化渲染管线的流程,包括剔除、几何处理、光照计算、着色和后处理等主要步骤,帮助理解渲染过程的基本流程。 实际的渲染管线会更加复杂,包含更多细节步骤。
输入系统负责接收和处理玩家的输入,例如键盘、鼠标、手柄、触摸屏等。Unity 提供了传统的 Input Manager 和新的 Input System 两种输入系统,新的 Input System 更加灵活和强大。
3.5.1 核心概念
输入设备 (Input Device): 玩家用来输入指令的硬件设备,例如键盘、鼠标、手柄、触摸屏等。
输入动作 (Input Action): 玩家可以执行的操作,例如移动、跳跃、射击等。使用新的 Input System,可以将输入动作与具体的输入设备和按键绑定。
输入映射 (Input Mapping): 将输入动作与输入设备上的按键、轴等进行绑定。
输入事件 (Input Event): 当输入设备状态发生变化时产生的事件,例如按键按下、鼠标移动、触摸开始等。
3.5.2 代码实践与详解 (New Input System 示例)
首先需要安装并启用 New Input System 包 (Package Manager 中搜索 Input System)。
创建 Input Actions Asset 文件 (Create -> Input Actions),并在其中定义输入动作和绑定。例如,创建一个名为 "PlayerInputActions" 的 Input Actions Asset,并添加一个 "Move" 动作,绑定到 WASD 键和手柄摇杆。
using UnityEngine; using UnityEngine.InputSystem; public class NewInputSystemExample : MonoBehaviour { public float speed = 5f; private Rigidbody rb; private PlayerInputActions playerInputActions; private Vector2 moveInput; void Awake() { playerInputActions = new PlayerInputActions(); rb = GetComponent<Rigidbody>(); if (rb == null) { Debug.LogError("Rigidbody 组件未找到!"); } } void OnEnable() { playerInputActions.Player.Enable(); // 启用 Player Action Map playerInputActions.Player.Move.performed += OnMovePerformed; // 绑定 Move 动作的 performed 事件 playerInputActions.Player.Move.canceled += OnMoveCanceled; // 绑定 Move 动作的 canceled 事件 } void OnDisable() { playerInputActions.Player.Move.performed -= OnMovePerformed; playerInputActions.Player.Move.canceled -= OnMoveCanceled; playerInputActions.Player.Disable(); // 禁用 Player Action Map } void FixedUpdate() { Vector3 movement = new Vector3(moveInput.x, 0f, moveInput.y) * speed * Time.fixedDeltaTime; if (rb != null) { rb.MovePosition(rb.position + movement); } } private void OnMovePerformed(InputAction.CallbackContext context) { moveInput = context.ReadValue<Vector2>(); // 读取 Move 动作的值 (Vector2) Debug.Log("Move Input Performed: " + moveInput); } private void OnMoveCanceled(InputAction.CallbackContext context) { moveInput = Vector2.zero; // 输入取消时,将 moveInput 重置为零 Debug.Log("Move Input Canceled"); } }
代码详解:
PlayerInputActions playerInputActions = new PlayerInputActions();: 实例化生成的 PlayerInputActions 类,该类是根据 Input Actions Asset 文件自动生成的 C# 代码。
playerInputActions.Player.Enable() 和 playerInputActions.Player.Disable(): 启用和禁用指定的 Action Map (Player Action Map)。
playerInputActions.Player.Move.performed += OnMovePerformed;: 将 OnMovePerformed 方法绑定到 "Move" 动作的 performed 事件。performed 事件在动作被触发时调用 (例如,按下按键或移动摇杆)。
playerInputActions.Player.Move.canceled += OnMoveCanceled;: 将 OnMoveCanceled 方法绑定到 "Move" 动作的 canceled 事件。canceled 事件在动作被取消时调用 (例如,松开按键或摇杆回到中心位置)。
context.ReadValue<Vector2>(): 从 InputAction.CallbackContext 中读取动作的值,根据动作类型 (Vector2, float, bool 等) 选择不同的 ReadValue 方法。
3.5.3 Mermaid 图表 (New Input System 流程)
图表解释:
该图表展示了 New Input System 的输入处理流程,从输入设备接收输入事件,通过 Input System 转换为输入动作,最终触发脚本中的事件处理函数,驱动游戏逻辑。
物理系统负责模拟游戏世界中的物理现象,例如重力、碰撞、摩擦力、弹力等。Unity 提供了两种物理引擎:内置的 3D 物理引擎 (PhysX) 和 2D 物理引擎。
3.6.1 核心概念
Rigidbody (刚体): 使 GameObject 具有物理属性,受物理引擎控制。刚体可以受到力、扭矩、重力等影响,并与其他刚体发生碰撞和交互。
Collider (碰撞器): 定义 GameObject 的物理形状,用于检测碰撞。Unity 提供了多种碰撞器类型,例如 Box Collider, Sphere Collider, Mesh Collider, Capsule Collider 等。
Physics Material (物理材质): 定义物体表面的物理属性,例如摩擦力、弹力等。
Force (力): 改变物体运动状态的物理量。可以施加力、扭矩、爆炸力等。
Joint (关节): 连接两个刚体的组件,限制它们的相对运动。Unity 提供了多种关节类型,例如 Hinge Joint, Spring Joint, Fixed Joint 等。
Raycast (射线投射): 从一个点向一个方向发射射线,检测射线是否与碰撞器相交,用于实现点击检测、寻路、激光等功能。
3.6.2 代码实践与详解
using UnityEngine; public class PhysicsExample : MonoBehaviour { public float forceMagnitude = 10f; private Rigidbody rb; void Start() { rb = GetComponent<Rigidbody>(); if (rb == null) { Debug.LogError("Rigidbody 组件未找到!"); return; } } void Update() { if (Input.GetKeyDown(KeyCode.F)) { ApplyForceForward(); } if (Input.GetMouseButtonDown(0)) { RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit)) { Debug.Log("射线击中物体: " + hit.collider.gameObject.name + " at point: " + hit.point); Rigidbody hitRb = hit.collider.GetComponent<Rigidbody>(); if (hitRb != null) { hitRb.AddForceAtPosition(ray.direction * forceMagnitude * 5f, hit.point); // 对击中物体施加力 } } } } void ApplyForceForward() { if (rb != null) { rb.AddForce(transform.forward * forceMagnitude, ForceMode.Impulse); // 向前施加瞬时力 Debug.Log("施加力: " + transform.forward * forceMagnitude); } } }
代码详解:
GetComponent<Rigidbody>(): 获取 GameObject 上的 Rigidbody 组件。
rb.AddForce(Vector3 force, ForceMode mode): 对刚体施加力。ForceMode.Impulse 表示施加瞬时力。
Physics.Raycast(Ray ray, out RaycastHit hit): 进行射线投射,检测射线是否与碰撞器相交。RaycastHit 结构体包含碰撞信息,例如碰撞点、法线、碰撞器等。
Camera.main.ScreenPointToRay(Vector3 screenPos): 将屏幕坐标转换为从主相机发出的射线。