Unity C#入门到实战: 启动你的第一个2D游戏项目(平台跳跃/俯视角射击) - 规划与核心玩法实现 (Day 40)

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 实践:创建基础项目结构

  1. 打开 Unity Hub,创建一个新的 2D 项目 (2D URP 或标准 2D Core 均可)。
  2. Assets 窗口中,按照 4.2 节的建议创建 _Project 及其子文件夹。目前只需创建 Scenes, Scripts, Prefabs, Art/Sprites 即可,其他根据需要再创建。

五、实现核心玩家控制

这是游戏的核心,直接关系到玩家的操作手感。我们将分别讨论两种类型的实现思路。

前提: 确保你的玩家对象已添加 Rigidbody 2DCollider 2D (如 Box Collider 2DCapsule 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 处理移动) ...
}

设置:

  1. 在玩家对象下创建一个空对象 GroundCheckPoint,并将其拖到 groundCheckPoint 字段。
  2. 调整 GroundCheckPoint 的位置到玩家碰撞体的底部。
  3. 创建一个新的 Layer (例如 “Ground”),并将你的平台/地面物体的 Layer 设置为 “Ground”。
  4. 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)
    }
}

设置:

  1. 创建一个简单的子弹预制体 (BulletPrefab),给它添加 Rigidbody 2D (设置 Gravity Scale 为 0) 和 Collider 2D (勾选 Is Trigger 可能更好处理命中)。还需要一个脚本来控制子弹移动和销毁。
  2. 在玩家对象下创建一个空对象 FirePoint,放在枪口位置。
  3. BulletPrefabFirePoint 分别拖拽到 PlayerController 组件对应的字段上。

提示: 大量子弹的实例化和销毁会消耗性能,后续需要使用 对象池 (Day 31) 进行优化。

5.2.3 C# 脚本示例 (整合)

上面已提供了分步代码,整合即可成为一个基础的 PlayerController.cs

5.3 脚本组件化思考

虽然现在我们将所有逻辑放在一个脚本里,但随着功能增多,考虑将不同职责分离到不同脚本中会更好(回顾 Day 14 的组件化设计思想):

  • PlayerMovement.cs (处理移动和跳跃/旋转)
  • PlayerShooting.cs (处理射击逻辑)
  • PlayerHealth.cs (处理生命值和受伤逻辑)

暂时我们先放在一起,简化起步。

六、实现基础敌人行为

让游戏世界不那么空旷,我们需要一些简单的敌人。

6.1 敌人类型选择

选择一种最简单的行为开始:

  • 平台跳跃: 左右巡逻的地面敌人。
  • 俯视角射击: 朝玩家移动的追踪敌人。

前提: 创建一个简单的敌人对象,添加 Rigidbody 2DCollider 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);
    }
}

设置:

  1. 创建两个空对象作为巡逻点 PatrolPointA, PatrolPointB
  2. 在敌人对象的 EnemyPatrol 组件中,将 Patrol Points 数组大小设为 2,并将 A 和 B 拖入。
  3. 将敌人的 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);
    }
}

设置:

  1. 确保你的玩家对象有一个唯一的 Tag,例如 “Player”。
  2. 在敌人对象的 EnemyChase 组件中,设置 Detection RadiusPlayer Layer

6.4 简单射击 (Simple Shooting - for Shooter)

如果敌人需要射击,可以在追击逻辑中加入计时器和实例化子弹的代码,类似于玩家的射击逻辑,但目标是玩家。

6.5 C# 脚本示例

上面已提供两种基础敌人的脚本示例。

提示: 这些只是最基础的敌人行为。更复杂的 AI 通常需要状态机 (Day 32) 或行为树 (Day 37)。

七、实践环节:搭建框架与实现基础操作

现在,动手将今天学到的知识应用起来:

  1. 创建项目与结构: 按照第 4 节,创建 Unity 项目并设置好基础文件夹结构。
  2. 创建玩家:
    • 在场景中创建一个 Sprite 或 3D Cube (作为占位符) 代表玩家。
    • 添加 Rigidbody 2DCollider 2D
    • 创建 PlayerMovement.cs (平台跳跃) 或 PlayerController.cs (俯视角射击) 脚本。
    • 将第 5 节的代码复制进去,并挂载到玩家对象上。
    • 根据需要创建 GroundCheckPointFirePoint,并设置好脚本的公共变量(速度、跳跃力、子弹预制体等)。
    • 平台跳跃: 添加一些平台物体 (设置 Layer 为 “Ground”)。
    • 俯视角射击: 创建一个简单的子弹预制体和控制脚本 (Bullet.cs,让它向前移动并定时销毁)。
  3. 测试玩家操作: 运行游戏,测试移动、跳跃或移动、瞄准、射击是否符合预期。反复调整参数(速度、跳跃力、射击速率)直到手感满意。
  4. 创建敌人:
    • 创建敌人 Sprite 或占位符。
    • 添加 Rigidbody 2DCollider 2D
    • 创建 EnemyPatrol.csEnemyChase.cs 脚本。
    • 将第 6 节的代码复制进去,并挂载到敌人对象上。
    • 根据需要设置巡逻点或玩家 Tag/Layer。
    • 将敌人做成预制体 (Prefab) 方便复用。
  5. 测试敌人行为: 运行游戏,观察敌人是否按预期巡逻或追击。

关键在于迭代: 不要期望一次完美。不断测试、调整代码和参数,逐步完善。

八、总结

恭喜你,完成了综合项目启动的第一步!今天我们涵盖了:

  1. 项目启动意义: 明确了通过实战项目整合知识、模拟开发流程、提升能力的重要性。
  2. GDD 核心: 了解了游戏设计文档(GDD)的基本作用和核心要素(概念、玩法、特性),并实践了初步构思。
  3. 项目结构: 学习并实践了搭建清晰、规范的 Unity 项目文件夹结构与命名规范,为后续开发打好基础。
  4. 核心玩家控制: 针对 2D 平台跳跃和俯视角射击两种类型,分别实现了基础的玩家移动、跳跃/瞄准与射击功能,并编写了对应的 C# 脚本。
  5. 基础敌人行为: 为游戏世界添加了简单的敌人,实现了基础的巡逻(平台跳跃)或追击(俯视角射击)逻辑。
  6. 实践奠基: 通过实际操作搭建了项目框架,实现了最核心的角色控制,为明天(Day 42)进一步丰富游戏系统和内容奠定了坚实的基础。

今天的工作重点是打地基。虽然功能还很简单,但这套核心框架是后续所有玩法的基石。明天,我们将在此基础上,继续深化项目,实现核心游戏循环、UI、敌人管理等内容。继续加油!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴师兄大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值