Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
PyTorch系列文章目录
Python系列文章目录
C#系列文章目录
01-C#与游戏开发的初次见面:从零开始的Unity之旅
02-C#入门:从变量与数据类型开始你的游戏开发之旅
03-C#运算符与表达式:从入门到游戏伤害计算实践
04-从零开始学C#:用if-else和switch打造智能游戏逻辑
05-掌握C#循环:for、while、break与continue详解及游戏案例
06-玩转C#函数:参数、返回值与游戏中的攻击逻辑封装
07-Unity游戏开发入门:用C#控制游戏对象移动
08-C#面向对象编程基础:类的定义、属性与字段详解
09-C#封装与访问修饰符:保护数据安全的利器
10-如何用C#继承提升游戏开发效率?Enemy与Boss案例解析
11-C#多态性入门:从零到游戏开发实战
12-C#接口王者之路:从入门到Unity游戏开发实战 (IAttackable案例详解)
13-C#静态成员揭秘:共享数据与方法的利器
14-Unity 面向对象实战:掌握组件化设计与脚本通信,构建玩家敌人交互
15-C#入门 Day15:彻底搞懂数组!从基础到游戏子弹管理实战
16-C# List 从入门到实战:掌握动态数组,轻松管理游戏敌人列表 (含代码示例)
17-C# 字典 (Dictionary) 完全指南:从入门到游戏属性表实战 (Day 17)
18-C#游戏开发【第18天】 | 深入理解队列(Queue)与栈(Stack):从基础到任务队列实战
19-【C# 进阶】深入理解枚举 Flags 属性:游戏开发中多状态组合的利器
20-C#结构体(Struct)深度解析:轻量数据容器与游戏开发应用 (Day 20)
21-Unity数据持久化进阶:告别硬编码,用ScriptableObject优雅管理游戏配置!(Day 21)
22-Unity C# 健壮性编程:告别崩溃!掌握异常处理与调试的 4 大核心技巧 (Day 22)
23-C#代码解耦利器:委托与事件(Delegate & Event)从入门到实践 (Day 23)
24-Unity脚本通信终极指南:从0到1精通UnityEvent与事件解耦(Day 24)
25-精通C# Lambda与LINQ:Unity数据处理效率提升10倍的秘诀! (Day 25)
26-# Unity C#进阶:掌握泛型编程,告别重复代码,编写优雅复用的通用组件!(Day26)
27-Unity协程从入门到精通:告别卡顿,用Coroutine优雅处理异步与时序任务 (Day 27)
28-搞定玩家控制!Unity输入系统、物理引擎、碰撞检测实战指南 (Day 28)
29-# Unity动画控制核心:Animator状态机与C#脚本实战指南 (Day 29)
30-Unity UI 从零到精通 (第30天): Canvas、布局与C#交互实战 (Day 30)
31-Unity性能优化利器:彻底搞懂对象池技术(附C#实现与源码解析)
32-Unity C#进阶:用状态模式与FSM优雅管理复杂敌人AI,告别Spaghetti Code!(Day32)
33-Unity游戏开发实战:从PlayerPrefs到JSON,精通游戏存档与加载机制(Day 33)
34-Unity C# 实战:从零开始为游戏添加背景音乐与音效 (AudioSource/AudioClip/AudioMixer 详解)(Day 34)
35-Unity 场景管理核心教程:从 LoadScene 到 Loading Screen 实战 (Day 35)
36-Unity设计模式实战:用单例和观察者模式优化你的游戏架构 (Day 36)
37-Unity性能优化实战:用Profiler揪出卡顿元凶 (CPU/GPU/内存/GC全面解析) (Day 37)
38-Unity C# 与 Shader 交互入门:脚本动态控制材质与视觉效果 (含 MaterialPropertyBlock 详解)(Day 38)
39-Unity网络编程入门:掌握Netcode for GameObjects实现多人游戏基础(Day 39)
40-Unity C#入门到实战: 启动你的第一个2D游戏项目(平台跳跃/俯视角射击) - 规划与核心玩法实现 (Day 40)
文章目录
前言
经过前面40天的学习,我们已经掌握了C#的基础语法、面向对象编程、数据结构、Unity核心机制以及一些进阶技巧。理论学习固然重要,但将知识融会贯通并应用于实际项目,才是检验学习成果、提升实战能力的最佳途径。
从今天(第41天)开始,我们将正式启动一个综合性的迷你游戏项目!这是一个激动人心的时刻,你将有机会选择制作一款经典的 2D平台跳跃游戏 或 俯视角射击游戏。本篇的目标是:
- 理解项目启动前规划的重要性。
- 了解游戏设计文档(GDD)的核心要素。
- 学习如何搭建一个清晰、可维护的Unity项目结构。
- 着手实现游戏的核心玩法:玩家的基本控制(移动、跳跃/射击)。
- 初步构建基础的敌人行为逻辑。
准备好了吗?让我们整合所学,开始构建属于你的第一个小型完整游戏吧!
一、为何启动综合项目?
在深入技术细节之前,我们先明确为何要在学习到这个阶段启动一个综合项目。
- 知识整合与应用: 将分散的知识点(C#语法、OOP、Unity API、事件、协程、物理等)在一个实际场景中串联起来,加深理解。
- 模拟真实开发流程: 从规划、设计、编码到测试(后续会涉及),体验一个小型项目的完整生命周期。
- 问题驱动学习: 在实践中必然会遇到各种预想不到的问题,解决这些问题的过程是最好的学习。
- 建立作品集: 完成的项目(哪怕很小)是你学习成果的直观展示,对未来求职或个人展示非常有价值。
- 提升开发信心: 成功构建一个可玩的游戏原型,能极大地增强你继续深入学习的动力。
二、游戏选择:2D平台跳跃 vs 俯视角射击
为了让大家能专注于核心玩法的实现,我们提供了两个经典的2D游戏类型作为选项:
2.1 2D平台跳跃 (2D Platformer)
- 核心玩法: 控制角色在不同平台间移动、跳跃,躲避障碍或敌人,达成关卡目标(如到达终点、收集物品)。
- 技术侧重: 精确的物理控制(重力、跳跃力)、碰撞检测(地面检测、墙壁检测)、关卡设计基础。
- 常见元素: 跳跃平台、敌人(巡逻、跳跃)、金币/道具、陷阱。
2.2 俯视角射击 (Top-Down Shooter)
- 核心玩法: 在2D平面上控制角色移动,向鼠标或指定方向射击,消灭敌人,生存或完成任务。
- 技术侧重: 全方位移动控制、旋转与瞄准、子弹发射与管理(对象池技术很关键,回顾Day 31)、敌人AI(追踪、射击)。
- 常见元素: 可自由移动的玩家、多种敌人(追踪型、射击型)、武器/子弹、障碍物。
如何选择?
- 兴趣导向: 选择你更感兴趣或更想挑战的类型。
- 学习目标: 如果想深入研究Unity的2D物理系统和精确控制,平台跳跃是不错的选择;如果对AI、对象池、向量运算更感兴趣,俯视角射击会提供更多实践机会。
在本文后续的示例中,我们会尽量兼顾两种类型的核心实现思路。
三、游戏设计文档(GDD)要素简介
即使是小型个人项目,一份简单的游戏设计文档(Game Design Document, GDD)也能帮助你保持专注、明确目标。它就像建筑蓝图,指导你的开发方向。
3.1 什么是GDD?
GDD是描述游戏设计方方面面的核心文档,用于统一团队(即使只有你自己)对游戏愿景、玩法、功能和内容的理解。
3.2 为何需要GDD(即使是小型项目)?
- 明确目标: 清晰定义你要做什么样的游戏,避免中途迷失方向或功能蔓延。
- 梳理思路: 强迫你思考游戏的核心机制、玩家体验等关键问题。
- 范围控制: 帮助你确定项目范围,知道哪些是必须做的(核心),哪些是后续可以添加的(锦上添花)。
3.3 核心要素
对于我们的小项目,GDD可以很简单,包含以下几个核心要素即可:
3.3.1 游戏概念/核心循环 (Game Concept / Core Loop)
用一两句话描述你的游戏是什么,玩家主要做什么。
- 示例 (平台跳跃): “一个控制小英雄在多彩平台间跳跃、躲避简单敌人、收集宝石并到达终点的2D平台游戏。”
- 示例 (俯视角射击): “一个在竞技场中控制飞船移动、向四周发射子弹、消灭不断生成的敌人的俯视角射击游戏。”
3.3.2 目标平台与受众 (Target Platform & Audience)
- 平台: 主要面向PC (Windows/Mac)。
- 受众: 学习者、休闲玩家。
3.3.3 核心玩法机制 (Core Gameplay Mechanics)
列出玩家能做的最主要的操作。
- 平台跳跃:
- 左右移动
- 跳跃(可能有多段跳或爬墙)
- (可选)攻击/踩踏敌人
- 俯视角射击:
- 上下左右移动
- 朝鼠标方向瞄准
- 射击
- (可选)特殊技能/冲刺
3.3.4 关键特性 (Key Features)
列出游戏的主要组成部分。
- 平台跳跃: 玩家控制器、至少一种敌人(如左右巡逻)、基础平台、收集物(如金币)、简单的终点机制。
- 俯视角射击: 玩家控制器、至少一种敌人(如追踪玩家)、子弹系统、简单的敌人生成机制。
3.3.5 (可选) 美术/音效风格 ((Optional) Art/Audio Style)
简单描述期望的视觉和听觉感受(例如:像素风、卡通风格、8-bit音效)。这有助于后续寻找或制作资源。
3.4 实践:为我们的项目勾勒 GDD 雏形
花几分钟时间,根据你选择的游戏类型,在记事本或思维导图中快速勾勒出上述GDD核心要素。不必追求完美,重点是开始思考和规划。
四、项目结构规划
良好的项目结构是代码可维护性、可扩展性的基础,即使是个人项目也应养成好习惯。
4.1 为何要规划结构?
- 清晰性: 快速定位所需资源(脚本、预制体、贴图等)。
- 模块化: 便于管理不同功能模块的代码和资源。
- 可维护性: 当项目变大时,结构混乱会导致难以修改和调试。
- 协作基础: (虽然是个人项目) 遵循标准结构是团队协作的前提。
4.2 Unity 推荐的文件夹结构
在 Assets
文件夹下,建议创建以下结构(可以根据项目实际情况调整):
Assets/
├── _Project/ # 项目专属顶级文件夹(避免与插件等冲突)
│ ├── Scenes/ # 存放所有场景文件 (.unity)
│ │ ├── MainMenu.unity
│ │ └── Level_01.unity
│ ├── Scripts/ # 存放所有 C# 脚本
│ │ ├── Player/ # 玩家相关脚本
│ │ ├── Enemy/ # 敌人相关脚本
│ │ ├── Managers/ # 游戏管理器脚本 (GameManager, UIManager等)
│ │ ├── UI/ # UI 交互脚本
│ │ ├── Core/ # 核心系统脚本 (事件系统, 对象池等)
│ │ └── Utilities/ # 通用工具脚本
│ ├── Prefabs/ # 存放预制体 (.prefab)
│ │ ├── Player.prefab
│ │ ├── Enemies/
│ │ └── Projectiles/
│ ├── Art/ # 存放美术资源
│ │ ├── Sprites/ # 2D 图片资源
│ │ ├── Animations/ # 动画文件 (.anim, .controller)
│ │ ├── Materials/ # 材质球
│ │ └── Fonts/ # 字体
│ ├── Audio/ # 存放音频资源
│ │ ├── Music/ # 背景音乐
│ │ └── SFX/ # 音效
│ ├── ScriptableObjects/ # 存放 ScriptableObject 资源 (如果使用)
│ └── Resources/ # (谨慎使用) 用于 Resources.Load 加载的资源
└── (ThirdParty Plugins, etc.) # 第三方插件通常放在 Assets 根目录
4.3 命名规范建议
- 文件夹: 使用
PascalCase
(首字母大写)。 - 文件 (脚本、预制体、场景等): 使用
PascalCase
。 - C# 类/结构体/枚举/方法: 使用
PascalCase
。 - C# 变量 (公有/保护): 使用
PascalCase
或_camelCase
(看团队习惯,保持一致)。 - C# 变量 (私有/局部): 使用
camelCase
(首字母小写) 或_camelCase
(带下划线前缀)。 - 常量: 使用
ALL_CAPS
(全大写,下划线分隔)。 - 布尔变量: 尽量使用肯定形式,如
IsGrounded
,CanShoot
。 - 有意义: 名称应清晰反映其用途,避免使用模糊不清或过于简写的名称。
4.4 实践:创建基础项目结构
- 打开 Unity Hub,创建一个新的 2D 项目 (2D URP 或标准 2D Core 均可)。
- 在
Assets
窗口中,按照 4.2 节的建议创建_Project
及其子文件夹。目前只需创建Scenes
,Scripts
,Prefabs
,Art/Sprites
即可,其他根据需要再创建。
五、实现核心玩家控制
这是游戏的核心,直接关系到玩家的操作手感。我们将分别讨论两种类型的实现思路。
前提: 确保你的玩家对象已添加 Rigidbody 2D
和 Collider 2D
(如 Box Collider 2D
或 Capsule Collider 2D
) 组件。回顾 Day 28 了解物理与碰撞基础。
5.1 平台跳跃 (Platformer)
5.1.1 移动 (Movement)
通常使用 Rigidbody2D.velocity
来控制物理移动,以获得更自然的惯性效果。
// PlayerMovement.cs (挂载在玩家对象上)
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float moveSpeed = 5f;
private Rigidbody2D rb;
private float moveInput;
void Start()
{
rb = GetComponent<Rigidbody2D>(); // 获取 Rigidbody2D 组件
}
void Update()
{
// 获取水平输入 (-1 到 1)
moveInput = Input.GetAxis("Horizontal");
}
void FixedUpdate()
{
// 在 FixedUpdate 中应用物理力或速度
// 直接设置速度,会覆盖原有Y轴速度,适合需要精确控制的情况
rb.velocity = new Vector2(moveInput * moveSpeed, rb.velocity.y);
// (可选)翻转角色朝向
if (moveInput > 0)
{
transform.localScale = new Vector3(1, 1, 1); // 面向右
}
else if (moveInput < 0)
{
transform.localScale = new Vector3(-1, 1, 1); // 面向左 (通过缩放翻转)
}
}
}
5.1.2 跳跃 (Jumping)
跳跃需要检测玩家是否在地面上,然后施加一个向上的力。
// 在 PlayerMovement.cs 中添加
using UnityEngine;
public class PlayerMovement : MonoBehaviour // (假设已有移动代码)
{
// ... (移动相关变量) ...
public float jumpForce = 10f;
public Transform groundCheckPoint; // 一个空对象,放在玩家脚底用于检测地面
public LayerMask groundLayer; // 设置哪些层级是地面
public float groundCheckRadius = 0.2f; // 检测范围半径
private bool isGrounded;
private Rigidbody2D rb; // (假设已在 Start 中获取)
void Update()
{
// ... (获取移动输入) ...
// 检测是否在地面
isGrounded = Physics2D.OverlapCircle(groundCheckPoint.position, groundCheckRadius, groundLayer);
// 处理跳跃输入 (在 Update 中检测按键,避免丢失输入)
if (Input.GetButtonDown("Jump") && isGrounded)
{
Jump();
}
}
void Jump()
{
// 施加一个瞬时向上的力
// 使用 Impulse 模式模拟瞬间爆发力
rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
}
// (可选) 在 Scene 视图中绘制检测范围,方便调试
void OnDrawGizmosSelected()
{
if (groundCheckPoint != null)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(groundCheckPoint.position, groundCheckRadius);
}
}
// ... (FixedUpdate 处理移动) ...
}
设置:
- 在玩家对象下创建一个空对象
GroundCheckPoint
,并将其拖到groundCheckPoint
字段。 - 调整
GroundCheckPoint
的位置到玩家碰撞体的底部。 - 创建一个新的
Layer
(例如 “Ground”),并将你的平台/地面物体的 Layer 设置为 “Ground”。 - 在
PlayerMovement
组件的 Inspector 中,将Ground Layer
设置为 “Ground”。
5.1.3 C# 脚本示例 (整合)
上面已提供了分步代码,整合即可成为一个基础的 PlayerMovement.cs
。
5.2 俯视角射击 (Top-Down Shooter)
5.2.1 移动 (Movement)
通常允许玩家在 X 和 Y 轴自由移动。
// PlayerController.cs (挂载在玩家对象上)
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed = 5f;
private Rigidbody2D rb;
private Vector2 moveInput;
private Vector2 mousePos;
private Camera mainCamera;
void Start()
{
rb = GetComponent<Rigidbody2D>();
mainCamera = Camera.main; // 获取主摄像机
}
void Update()
{
// 获取移动输入
float moveX = Input.GetAxisRaw("Horizontal"); // 使用 GetAxisRaw 获取无平滑的输入
float moveY = Input.GetAxisRaw("Vertical");
moveInput = new Vector2(moveX, moveY).normalized; // 标准化,防止斜向移动过快
// 获取鼠标在世界坐标系的位置 (用于瞄准)
mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
}
void FixedUpdate()
{
// 应用移动速度
rb.velocity = moveInput * moveSpeed;
// 角色朝向鼠标方向旋转
Vector2 lookDir = mousePos - rb.position;
float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg - 90f; // 减90度,如果你的精灵默认朝上
rb.rotation = angle; // 直接设置 Rigidbody 的旋转
}
}
注意: 上述旋转代码假设你的精灵资源默认是朝上的。如果默认朝右,则不需要减 90 度。
5.2.2 瞄准与射击 (Aiming & Shooting)
瞄准已在移动代码中实现(旋转)。射击通常涉及实例化子弹预制体。
// 在 PlayerController.cs 中添加
using UnityEngine;
public class PlayerController : MonoBehaviour // (假设已有移动和瞄准代码)
{
// ... (移动和瞄准相关变量) ...
public GameObject bulletPrefab; // 子弹预制体
public Transform firePoint; // 子弹发射点 (玩家对象下的一个空物体)
public float fireRate = 0.5f; // 每秒发射多少子弹 (越小越快)
private float nextFireTime = 0f;
// ... (Start, Update, FixedUpdate) ...
void Update()
{
// ... (获取移动输入和鼠标位置) ...
// 处理射击输入
if (Input.GetButton("Fire1") && Time.time >= nextFireTime) // Fire1 通常是鼠标左键
{
Shoot();
nextFireTime = Time.time + fireRate; // 更新下次可以射击的时间
}
}
void Shoot()
{
// 实例化子弹
// firePoint.rotation 保证子弹初始方向与玩家朝向一致
Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
// 播放音效等... (回顾 Day 34)
}
}
设置:
- 创建一个简单的子弹预制体 (
BulletPrefab
),给它添加Rigidbody 2D
(设置 Gravity Scale 为 0) 和Collider 2D
(勾选Is Trigger
可能更好处理命中)。还需要一个脚本来控制子弹移动和销毁。 - 在玩家对象下创建一个空对象
FirePoint
,放在枪口位置。 - 将
BulletPrefab
和FirePoint
分别拖拽到PlayerController
组件对应的字段上。
提示: 大量子弹的实例化和销毁会消耗性能,后续需要使用 对象池 (Day 31) 进行优化。
5.2.3 C# 脚本示例 (整合)
上面已提供了分步代码,整合即可成为一个基础的 PlayerController.cs
。
5.3 脚本组件化思考
虽然现在我们将所有逻辑放在一个脚本里,但随着功能增多,考虑将不同职责分离到不同脚本中会更好(回顾 Day 14 的组件化设计思想):
PlayerMovement.cs
(处理移动和跳跃/旋转)PlayerShooting.cs
(处理射击逻辑)PlayerHealth.cs
(处理生命值和受伤逻辑)
暂时我们先放在一起,简化起步。
六、实现基础敌人行为
让游戏世界不那么空旷,我们需要一些简单的敌人。
6.1 敌人类型选择
选择一种最简单的行为开始:
- 平台跳跃: 左右巡逻的地面敌人。
- 俯视角射击: 朝玩家移动的追踪敌人。
前提: 创建一个简单的敌人对象,添加 Rigidbody 2D
和 Collider 2D
。
6.2 简单巡逻 (平台跳跃)
让敌人在两个点之间来回移动。
// EnemyPatrol.cs (挂载在敌人对象上)
using UnityEngine;
public class EnemyPatrol : MonoBehaviour
{
public float moveSpeed = 2f;
public Transform[] patrolPoints; // 存储巡逻点的数组
public SpriteRenderer spriteRenderer; // 用于翻转精灵
private int currentPointIndex = 0;
private Rigidbody2D rb;
private bool movingRight = true; // 初始移动方向
void Start()
{
rb = GetComponent<Rigidbody2D>();
if (patrolPoints.Length > 0)
{
transform.position = patrolPoints[0].position; // 初始化位置到第一个点
}
}
void Update()
{
if (patrolPoints.Length == 0) return; // 没有巡逻点则不移动
// 朝当前目标点移动
Transform targetPoint = patrolPoints[currentPointIndex];
transform.position = Vector2.MoveTowards(transform.position, targetPoint.position, moveSpeed * Time.deltaTime);
// 到达目标点后,切换到下一个点
if (Vector2.Distance(transform.position, targetPoint.position) < 0.1f)
{
currentPointIndex = (currentPointIndex + 1) % patrolPoints.Length;
// 翻转朝向
Flip();
}
}
void Flip()
{
movingRight = !movingRight;
// spriteRenderer.flipX = !movingRight; // 或者使用 LocalScale 翻转
transform.localScale = new Vector3(transform.localScale.x * -1, transform.localScale.y, transform.localScale.z);
}
}
设置:
- 创建两个空对象作为巡逻点
PatrolPointA
,PatrolPointB
。 - 在敌人对象的
EnemyPatrol
组件中,将Patrol Points
数组大小设为 2,并将 A 和 B 拖入。 - 将敌人的
SpriteRenderer
拖入spriteRenderer
字段。
6.3 基础追击 (俯视角射击)
让敌人朝玩家移动。
// EnemyChase.cs (挂载在敌人对象上)
using UnityEngine;
public class EnemyChase : MonoBehaviour
{
public float moveSpeed = 3f;
public float detectionRadius = 5f; // 探测玩家的范围
public LayerMask playerLayer; // 玩家所在的层
private Transform playerTransform;
private Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
// 尝试查找玩家对象,如果场景中只有一个玩家且有"Player"标签
GameObject playerObject = GameObject.FindGameObjectWithTag("Player");
if (playerObject != null)
{
playerTransform = playerObject.transform;
}
}
void Update()
{
if (playerTransform == null) return; // 找不到玩家则不行动
// 检测玩家是否在范围内
float distanceToPlayer = Vector2.Distance(transform.position, playerTransform.position);
if (distanceToPlayer <= detectionRadius)
{
// 朝玩家移动
Vector2 direction = (playerTransform.position - transform.position).normalized;
rb.velocity = direction * moveSpeed;
// (可选) 让敌人朝向玩家旋转
// float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 90f;
// rb.rotation = angle;
}
else
{
// 玩家不在范围内,停止移动
rb.velocity = Vector2.zero;
}
}
// (可选) 在 Scene 视图中绘制检测范围
void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRadius);
}
}
设置:
- 确保你的玩家对象有一个唯一的 Tag,例如 “Player”。
- 在敌人对象的
EnemyChase
组件中,设置Detection Radius
和Player Layer
。
6.4 简单射击 (Simple Shooting - for Shooter)
如果敌人需要射击,可以在追击逻辑中加入计时器和实例化子弹的代码,类似于玩家的射击逻辑,但目标是玩家。
6.5 C# 脚本示例
上面已提供两种基础敌人的脚本示例。
提示: 这些只是最基础的敌人行为。更复杂的 AI 通常需要状态机 (Day 32) 或行为树 (Day 37)。
七、实践环节:搭建框架与实现基础操作
现在,动手将今天学到的知识应用起来:
- 创建项目与结构: 按照第 4 节,创建 Unity 项目并设置好基础文件夹结构。
- 创建玩家:
- 在场景中创建一个 Sprite 或 3D Cube (作为占位符) 代表玩家。
- 添加
Rigidbody 2D
和Collider 2D
。 - 创建
PlayerMovement.cs
(平台跳跃) 或PlayerController.cs
(俯视角射击) 脚本。 - 将第 5 节的代码复制进去,并挂载到玩家对象上。
- 根据需要创建
GroundCheckPoint
或FirePoint
,并设置好脚本的公共变量(速度、跳跃力、子弹预制体等)。 - 平台跳跃: 添加一些平台物体 (设置 Layer 为 “Ground”)。
- 俯视角射击: 创建一个简单的子弹预制体和控制脚本 (
Bullet.cs
,让它向前移动并定时销毁)。
- 测试玩家操作: 运行游戏,测试移动、跳跃或移动、瞄准、射击是否符合预期。反复调整参数(速度、跳跃力、射击速率)直到手感满意。
- 创建敌人:
- 创建敌人 Sprite 或占位符。
- 添加
Rigidbody 2D
和Collider 2D
。 - 创建
EnemyPatrol.cs
或EnemyChase.cs
脚本。 - 将第 6 节的代码复制进去,并挂载到敌人对象上。
- 根据需要设置巡逻点或玩家 Tag/Layer。
- 将敌人做成预制体 (
Prefab
) 方便复用。
- 测试敌人行为: 运行游戏,观察敌人是否按预期巡逻或追击。
关键在于迭代: 不要期望一次完美。不断测试、调整代码和参数,逐步完善。
八、总结
恭喜你,完成了综合项目启动的第一步!今天我们涵盖了:
- 项目启动意义: 明确了通过实战项目整合知识、模拟开发流程、提升能力的重要性。
- GDD 核心: 了解了游戏设计文档(GDD)的基本作用和核心要素(概念、玩法、特性),并实践了初步构思。
- 项目结构: 学习并实践了搭建清晰、规范的 Unity 项目文件夹结构与命名规范,为后续开发打好基础。
- 核心玩家控制: 针对 2D 平台跳跃和俯视角射击两种类型,分别实现了基础的玩家移动、跳跃/瞄准与射击功能,并编写了对应的 C# 脚本。
- 基础敌人行为: 为游戏世界添加了简单的敌人,实现了基础的巡逻(平台跳跃)或追击(俯视角射击)逻辑。
- 实践奠基: 通过实际操作搭建了项目框架,实现了最核心的角色控制,为明天(Day 42)进一步丰富游戏系统和内容奠定了坚实的基础。
今天的工作重点是打地基。虽然功能还很简单,但这套核心框架是后续所有玩法的基石。明天,我们将在此基础上,继续深化项目,实现核心游戏循环、UI、敌人管理等内容。继续加油!