十二、实战项目 十二、实战项目:从零开始构建你的第一个Unity3D游戏 - 简易跑酷游戏 本章节我们将通过一个实际的项目——简易跑酷游戏,来综合运用前面章节所学的 Unity3D 知识。我们将从项目创建、场景搭建、角色控制、障碍物生成、UI 设计到游戏逻辑实现,一步步构建一个完整的可玩游戏。通过这个实战项目,你将更深入地理解 Unity3D 的工作流程,并掌握实际开发中的常用技巧和方法。 学习目标: 掌握 Unity3D 项目的完整开发流程。 学习场景搭建、资源导入和管理。 掌握角色控制脚本的编写,包括移动、跳跃等。 学习障碍物生成和碰撞检测。 学习 UI 界面设计和游戏状态管理。 熟悉 C# 脚本在 Unity3D 中的应用。 提升解决实际问题的能力和项目经验。
本章节我们将通过一个实际的项目——简易跑酷游戏,来综合运用前面章节所学的 Unity3D 知识。我们将从项目创建、场景搭建、角色控制、障碍物生成、UI 设计到游戏逻辑实现,一步步构建一个完整的可玩游戏。通过这个实战项目,你将更深入地理解 Unity3D 的工作流程,并掌握实际开发中的常用技巧和方法。
学习目标:
掌握 Unity3D 项目的完整开发流程。
学习场景搭建、资源导入和管理。
掌握角色控制脚本的编写,包括移动、跳跃等。
学习障碍物生成和碰撞检测。
学习 UI 界面设计和游戏状态管理。
熟悉 C# 脚本在 Unity3D 中的应用。
提升解决实际问题的能力和项目经验。
项目概览:
我们的简易跑酷游戏将是一个 3D 横版跑酷游戏。玩家控制角色不断向前奔跑,躲避随机生成的障碍物,尽可能跑得更远,获得更高的分数。
项目核心功能:
角色控制: 玩家控制角色左右移动和跳跃。
场景生成: 地面无限延伸,营造跑酷的无限感。
障碍物生成: 随机生成不同类型的障碍物。
碰撞检测: 检测角色与障碍物的碰撞,触发游戏结束。
UI 显示: 显示当前得分、游戏结束界面等。
简单的计分系统: 根据奔跑距离计算得分。
12.1.1 创建新项目
打开 Unity Hub,点击 “新建项目”,选择 3D 模板,项目名称命名为 “SimpleRunner”,选择合适的项目存放路径,点击 “创建”。
12.1.2 导入资源
为了快速搭建场景,我们可以使用 Unity Asset Store 中的免费资源包,或者使用 Unity 提供的基础资源。为了简化,我们这里主要使用 Unity 自带的资源和简单的几何体。
地面: 在 Hierarchy 窗口右键点击 "3D Object" -> "Cube",命名为 "Ground"。在 Inspector 窗口调整 Ground 的 Scale 属性,例如 X = 10, Y = 1, Z = 10,使其成为一个宽阔的地面。
角色: 同样在 Hierarchy 窗口右键点击 "3D Object" -> "Capsule",命名为 "Player"。调整其位置和大小,使其位于地面之上,作为玩家角色。
障碍物: 在 Hierarchy 窗口右键点击 "3D Object" -> "Cube",命名为 "Obstacle"。 作为障碍物预制体,稍后我们将创建多个障碍物实例。
光源: 确保场景中有 Directional Light,如果没有,请在 Hierarchy 窗口右键点击 "Light" -> "Directional Light" 创建。调整光源方向,使场景光照适宜。
摄像机: 场景中默认会有一个 Main Camera。调整 Main Camera 的位置和角度,使其能够清晰地看到玩家角色和跑道。例如,将其 Position 设置为 (0, 5, -10),Rotation 设置为 (10, 0, 0)。
12.1.3 场景美化 (可选)
为了使场景更具吸引力,可以进行以下美化操作 (可选):
添加材质: 为地面、角色和障碍物添加不同的材质,例如为地面添加草地材质,为障碍物添加木质或石头材质。可以使用 Unity 自带的 Standard 材质,并调整颜色和纹理。
调整光照: 调整 Directional Light 的颜色和强度,营造不同的光照氛围。可以尝试使用烘焙光照来提升性能和效果 (进阶内容,此处暂不深入)。
添加天空盒: 在 Window -> Rendering -> Lighting Settings 中,可以设置 Skybox Material,为场景添加天空背景。可以使用 Unity 提供的默认天空盒,或者导入自定义天空盒资源。
12.1.4 场景结构图 (Mermaid)
图形解释: 该 Mermaid 图展示了 Hierarchy 窗口中的主要游戏对象及其基本属性。从 Hierarchy Window 分支出 Ground, Player, Obstacle, Directional Light, Main Camera 等对象,并分别展示了 Ground 的 Scale 属性、Player 和 Obstacle 的形状,以及 Main Camera 的 Position 和 Rotation 属性。
12.2.1 创建 PlayerController 脚本
在 Project 窗口的 Assets 文件夹下,右键点击 "Create" -> "C# Script",命名为 "PlayerController"。双击打开脚本开始编写代码。
12.2.2 编写角色移动代码
using UnityEngine; public class PlayerController : MonoBehaviour { public float moveSpeed = 5f; public float jumpForce = 7f; private Rigidbody rb; private bool isGrounded; void Start() { rb = GetComponent<Rigidbody>(); } void Update() { // 左右移动 float horizontalInput = Input.GetAxis("Horizontal"); transform.Translate(Vector3.right * horizontalInput * moveSpeed * Time.deltaTime); // 跳跃 if (Input.GetButtonDown("Jump") && isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); isGrounded = false; // 跳跃后设置为不在地面 } } // 检测地面碰撞 void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag("Ground")) // 假设地面物体添加了 "Ground" 标签 { isGrounded = true; // 接触地面时设置为在地面 } } }
代码详解:
public float moveSpeed = 5f; 和 public float jumpForce = 7f;: 定义了角色移动速度和跳跃力,public 关键字使其可以在 Inspector 窗口中调整。
private Rigidbody rb;: 声明一个 Rigidbody 组件变量,用于控制角色的物理运动。
private bool isGrounded;: 声明一个布尔变量,用于判断角色是否在地面上,控制跳跃条件。
void Start(): 在游戏开始时获取角色的 Rigidbody 组件。
void Update(): 每帧执行的代码,用于处理角色移动和跳跃逻辑。
float horizontalInput = Input.GetAxis("Horizontal");: 获取水平方向的输入,对应键盘的左右方向键或 A/D 键。
transform.Translate(Vector3.right * horizontalInput * moveSpeed * Time.deltaTime);: 使用 Translate 方法控制角色左右移动。Time.deltaTime 用于确保移动速度与帧率无关。
if (Input.GetButtonDown("Jump") && isGrounded): 检测是否按下跳跃键 (默认空格键) 并且角色在地面上。
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);: 使用 AddForce 方法给角色施加一个向上的瞬时力 (Impulse),实现跳跃效果。
isGrounded = false;: 跳跃后立即将 isGrounded 设置为 false,防止空中连续跳跃。
void OnCollisionEnter(Collision collision): 当角色与其他物体发生碰撞时调用。
if (collision.gameObject.CompareTag("Ground")): 判断碰撞到的物体是否带有 "Ground" 标签。
isGrounded = true;: 如果碰撞到地面,则将 isGrounded 设置为 true,允许角色再次跳跃。
12.2.3 应用脚本并配置
选中 Hierarchy 窗口中的 "Player" 对象。
在 Inspector 窗口中,点击 "Add Component" 按钮,搜索并添加 "Rigidbody" 组件。
再次点击 "Add Component" 按钮,搜索并添加 "PlayerController" 脚本。
选中 "Ground" 对象,在 Inspector 窗口中,点击 "Tag" 下拉菜单,选择 "Add Tag",点击 "+" 号,添加一个名为 "Ground" 的 Tag,然后将 "Ground" 对象设置为 "Ground" Tag。
12.2.4 角色控制流程图 (Mermaid)
图形解释: 该 Mermaid 图展示了 PlayerController 脚本中角色控制的逻辑流程。Update 函数中,首先检查是否有水平输入,有则水平移动;然后检查是否有跳跃输入且角色在地面上,有则施加跳跃力并将 isGrounded 设置为 false。OnCollisionEnter 函数中,检测是否与带有 "Ground" 标签的物体碰撞,是则将 isGrounded 设置为 true。
12.3.1 创建 ObstacleSpawner 脚本
在 Project 窗口的 Assets 文件夹下,右键点击 "Create" -> "C# Script",命名为 "ObstacleSpawner"。双击打开脚本开始编写代码。
12.3.2 编写障碍物生成代码
using UnityEngine; public class ObstacleSpawner : MonoBehaviour { public GameObject obstaclePrefab; // 障碍物预制体 public float spawnInterval = 2f; // 生成间隔 public float obstacleSpeed = 5f; // 障碍物移动速度 public Transform spawnPoint; // 生成点 void Start() { InvokeRepeating("SpawnObstacle", 0f, spawnInterval); // 定时生成障碍物 } void SpawnObstacle() { GameObject obstacle = Instantiate(obstaclePrefab, spawnPoint.position, Quaternion.identity); // 生成障碍物 Rigidbody obstacleRb = obstacle.GetComponent<Rigidbody>(); if (obstacleRb == null) { obstacleRb = obstacle.AddComponent<Rigidbody>(); // 如果没有 Rigidbody 组件则添加 obstacleRb.isKinematic = true; // 设置为 Kinematic,手动控制移动 } obstacle.GetComponent<ObstacleMovement>().speed = obstacleSpeed; // 将速度传递给障碍物移动脚本 } }
12.3.3 创建 ObstacleMovement 脚本
在 Project 窗口的 Assets 文件夹下,右键点击 "Create" -> "C# Script",命名为 "ObstacleMovement"。双击打开脚本开始编写代码。
using UnityEngine; public class ObstacleMovement : MonoBehaviour { public float speed = 5f; void Update() { transform.Translate(Vector3.back * speed * Time.deltaTime); // 向后移动 if (transform.position.z < -10f) // 假设超出一定距离后销毁 { Destroy(gameObject); // 销毁障碍物,避免堆积 } } }
代码详解:
ObstacleSpawner.cs:
public GameObject obstaclePrefab;: 公开的障碍物预制体变量,需要在 Inspector 窗口中赋值。
public float spawnInterval = 2f;: 障碍物生成间隔,控制生成频率。
public float obstacleSpeed = 5f;: 障碍物移动速度。
public Transform spawnPoint;: 障碍物生成点,需要在场景中创建一个空物体作为生成点并赋值。
InvokeRepeating("SpawnObstacle", 0f, spawnInterval);: 使用 InvokeRepeating 函数定时调用 SpawnObstacle 方法,实现周期性生成障碍物。
GameObject obstacle = Instantiate(obstaclePrefab, spawnPoint.position, Quaternion.identity);: 实例化障碍物预制体到生成点位置。
obstacleRb.isKinematic = true;: 将障碍物的 Rigidbody 设置为 Kinematic,使其不受物理引擎影响,方便我们手动控制其移动。
obstacle.GetComponent<ObstacleMovement>().speed = obstacleSpeed;: 获取障碍物上的 ObstacleMovement 脚本,并将速度赋值。
ObstacleMovement.cs:
public float speed = 5f;: 障碍物的移动速度,由 ObstacleSpawner 脚本传递过来。
transform.Translate(Vector3.back * speed * Time.deltaTime);: 控制障碍物向后移动,模拟跑酷游戏中障碍物向玩家移动的效果。
if (transform.position.z < -10f): 判断障碍物是否移动到一定距离之外,超出视野范围。
Destroy(gameObject);: 销毁超出视野的障碍物,避免场景中障碍物过多导致性能下降。
12.3.4 应用脚本并配置
选中 Hierarchy 窗口中的 "Obstacle" 对象 (我们之前创建的 Cube)。
在 Inspector 窗口中,点击 "Add Component" 按钮,搜索并添加 "ObstacleMovement" 脚本。
将 "Obstacle" 对象拖拽到 Project 窗口的 Assets 文件夹下,创建一个预制体 (Prefab)。删除 Hierarchy 窗口中的 "Obstacle" 对象,我们之后将通过脚本动态生成障碍物。
在 Hierarchy 窗口中,右键点击 "Create Empty",命名为 "ObstacleSpawner"。
选中 "ObstacleSpawner" 对象,在 Inspector 窗口中,点击 "Add Component" 按钮,搜索并添加 "ObstacleSpawner" 脚本。
在 "ObstacleSpawner" 脚本的 Inspector 窗口中,将 Assets 文件夹下的 "Obstacle" 预制体拖拽到 "Obstacle Prefab" 槽位。
在场景中调整 "ObstacleSpawner" 对象的位置,使其位于玩家的前方,作为障碍物的生成点。例如,Position 设置为 (0, 0, 10)。
12.3.5 障碍物生成流程图 (Mermaid)
图形解释: 该 Mermaid 图展示了障碍物生成和移动的流程。ObstacleSpawner 脚本的 Start 函数启动定时生成障碍物的 SpawnObstacle 函数。SpawnObstacle 函数实例化障碍物预制体,获取或添加 Rigidbody 组件并设置为 Kinematic,获取 ObstacleMovement 脚本并设置速度。ObstacleMovement 脚本的 Update 函数控制障碍物向后移动,并判断是否超出距离,超出则销毁。
12.4.1 修改 PlayerController 脚本添加碰撞检测
在 PlayerController.cs 脚本中添加以下代码:
void OnTriggerEnter(Collider other) { if (other.gameObject.CompareTag("Obstacle")) // 假设障碍物添加了 "Obstacle" 标签 { Debug.Log("Game Over!"); // 简单输出游戏结束信息 // 在这里添加游戏结束逻辑,例如停止游戏、显示游戏结束界面等 Time.timeScale = 0f; // 暂停游戏 } }
代码详解:
void OnTriggerEnter(Collider other): 当角色的 Collider 组件 (需要设置为 Is Trigger) 与其他 Collider 组件发生触发碰撞时调用。
if (other.gameObject.CompareTag("Obstacle")): 判断触发碰撞的物体是否带有 "Obstacle" 标签。
Debug.Log("Game Over!");: 在控制台输出 "Game Over!" 信息,用于调试和提示。
Time.timeScale = 0f;: 将 Time.timeScale 设置为 0,暂停游戏时间,实现游戏暂停效果。
12.4.2 配置碰撞检测
选中 Hierarchy 窗口中的 "Player" 对象。
在 Inspector 窗口中,确保 "Player" 对象已经添加了 Collider 组件 (例如 Capsule Collider)。
勾选 Capsule Collider 组件的 "Is Trigger" 属性,将其设置为触发器。
选中 Project 窗口中的 "Obstacle" 预制体。
在 Inspector 窗口中,点击 "Tag" 下拉菜单,选择 "Add Tag",点击 "+" 号,添加一个名为 "Obstacle" 的 Tag,然后将 "Obstacle" 预制体设置为 "Obstacle" Tag。
确保 "Obstacle" 预制体也带有 Collider 组件 (例如 Box Collider),默认即可,不需要设置为 Trigger。
12.4.3 碰撞检测流程图 (Mermaid)
图形解释: 该 Mermaid 图展示了碰撞检测的流程。当 Player 对象的 Trigger Collider 检测到与其他 Collider 的碰撞时,OnTriggerEnter 函数被调用。判断碰撞物体是否带有 "Obstacle" 标签,是则输出 "Game Over!" 并暂停游戏时间,否则继续游戏。
12.5.1 创建 UI Text 显示得分
在 Hierarchy 窗口右键点击 "UI" -> "Text - TextMeshPro"。 如果提示导入 TextMeshPro 资源,请按照提示导入。TextMeshPro 是 Unity 推荐的文本显示方案,效果更好。
在 Canvas 对象下,找到新创建的 TextMeshPro - Text 对象,命名为 "ScoreText"。
调整 "ScoreText" 对象在 Canvas 中的位置和大小,使其显示在屏幕的合适位置,例如左上角或右上角。
在 Inspector 窗口中,修改 TextMeshPro - Text (Script) 组件的 Text 属性,例如初始设置为 "Score: 0"。调整 Font Size、Color 等属性,使其更美观易读。
12.5.2 创建 ScoreManager 脚本
在 Project 窗口的 Assets 文件夹下,右键点击 "Create" -> "C# Script",命名为 "ScoreManager"。双击打开脚本开始编写代码。
using UnityEngine; using TMPro; // 引入 TextMeshPro 命名空间 public class ScoreManager : MonoBehaviour { public TextMeshProUGUI scoreText; // 分数 TextMeshPro 组件 private float score = 0f; void Update() { score += Time.deltaTime * 10f; // 每秒增加 10 分,根据时间流逝计分 UpdateScoreText(); } void UpdateScoreText() { scoreText.text = "Score: " + Mathf.FloorToInt(score).ToString(); // 更新 UI 显示,取整显示 } }
12.5.3 应用脚本并配置
在 Hierarchy 窗口中,右键点击 "Create Empty",命名为 "GameManager"。
选中 "GameManager" 对象,在 Inspector 窗口中,点击 "Add Component" 按钮,搜索并添加 "ScoreManager" 脚本。
在 "ScoreManager" 脚本的 Inspector 窗口中,将 Hierarchy 窗口中的 "ScoreText" 对象拖拽到 "Score Text" 槽位。
12.5.4 计分流程图 (Mermaid)
图形解释: 该 Mermaid 图展示了计分流程。ScoreManager 脚本的 Update 函数每帧根据时间流逝增加分数,并调用 UpdateScoreText 函数更新 UI 显示。UpdateScoreText 函数将分数取整并转换为字符串,赋值给 scoreText.text 属性,更新屏幕上的分数显示。
完成以上步骤后,你可以运行游戏进行测试。
运行测试: 点击 Unity 编辑器顶部的 "Play" 按钮运行游戏。
测试角色控制: 使用左右方向键或 A/D 键控制角色左右移动,空格键跳跃,测试角色控制是否流畅。
测试障碍物生成: 观察障碍物是否按照设定的间隔生成,并向玩家方向移动。
测试碰撞检测: 尝试让角色碰撞障碍物,观察是否触发游戏结束逻辑 (控制台输出 "Game Over!" 且游戏暂停)。
测试计分: 观察屏幕上的分数是否随着时间流逝而增加。
优化方向 (后续可扩展):
调整游戏难度: 调整障碍物生成频率、障碍物速度、角色移动速度和跳跃力等参数,平衡游戏难度。
添加障碍物类型: 创建不同形状、大小和移动方式的障碍物,增加游戏变化性。
优化场景: 添加更多场景元素,例如背景、天空、跑道纹理等,提升视觉效果。
添加音效和音乐: 为游戏添加背景音乐和操作音效,增强游戏体验。
实现游戏结束界面和重新开始功能: 在游戏结束时显示游戏结束界面,并提供重新开始游戏的按钮。
添加更复杂的计分系统: 例如根据躲避障碍物的数量、奔跑距离等计算得分。
移动平台适配: 将游戏适配到移动平台,实现触屏操作。
在本章节中,我们从零开始构建了一个简易的 3D 跑酷游戏。通过这个实战项目,我们学习并实践了以下内容:
Unity3D 项目创建和场景搭建: 创建新项目,导入和创建游戏对象,搭建基本游戏场景。
角色控制脚本编写: 编写 C# 脚本实现角色移动和跳跃控制。
障碍物生成与管理: 编写脚本实现障碍物定时生成和移动,以及超出视野后销毁。
碰撞检测与游戏结束: 实现角色与障碍物碰撞检测,触发游戏结束逻辑。
UI 显示与基础计分: 创建 UI Text 显示游戏得分,并实现简单的计分系统。
Mermaid 图表应用: 使用 Mermaid 绘制流程图和结构图,辅助理解和文档编写。
这个简易跑酷游戏只是一个开始,你可以基于此项目继续扩展和完善,添加更多功能和玩法,不断提升你的 Unity3D 开发技能。实战项目是学习和提升技能的最佳方式,希望这个章节能帮助你迈出 Unity3D 游戏开发的第一步!