Unity 2D平台跳跃游戏Demo实战:从零构建求职作品集

📅 2026/8/24 12:25:33
Unity 2D平台跳跃游戏Demo实战:从零构建求职作品集
最近在准备游戏策划岗位的面试深刻体会到一份能跑起来的、有亮点的游戏Demo远比一份精美的PPT更有说服力。很多同学卡在“想法很多但无从下手”的阶段或者做出来的Demo过于简陋难以体现策划能力。本文将分享一个从零到一、结构清晰、可玩性尚可的2D平台跳跃游戏Demo 2.0的完整制作过程并重点讲解如何将Demo转化为求职作品集的核心亮点。无论你是想入行的新人还是想用项目经验提升竞争力的同学都能从中获得一套可直接复用的实战方案。1. 游戏Demo的核心价值与策划岗要求在动手之前我们必须明确面试官想从你的游戏Demo里看到什么一份合格的Demo不仅仅是功能的堆砌更是你作为策划的思维能力和执行力的体现。1.1 游戏Demo对策划岗的意义对于游戏策划岗位尤其是系统策划、关卡策划、战斗策划一份自制的Demo具有不可替代的价值证明你的热情与执行力它直观地告诉面试官你不仅喜欢玩游戏更有能力将想法变为可交互的原型。展示核心设计能力通过Demo你可以具体展示对游戏循环、核心玩法、难度曲线、操作手感、反馈设计的理解。提供具体的讨论素材面试中你可以围绕Demo展开讲述设计意图、遇到的挑战和解决方案这比空谈理论有力得多。体现技术协作意识即使使用简单工具制作过程也能体现你对程序实现、美术资源、音频管理的初步认知这是策划与团队沟通的基础。1.2 Demo 2.0的升级目标假设1.0版本是一个能移动、跳跃的简单方块。那么2.0版本的目标是将其升级为一个“麻雀虽小五脏俱全”的迷你游戏应包含以下层次核心玩法清晰、有趣且可扩展。基础系统角色控制、物理交互、资源管理。关卡设计包含引导、挑战和奖励的简单关卡。视听反馈基本的UI、音效和粒子效果。数据驱动意识尝试将部分设计参数如角色速度、跳跃力配置化。我们将使用Unity作为开发引擎2022.3 LTS版本因为其资源丰富、社区活跃是业界主流且对策划友好。即使你是零编程基础跟随本文也能完成。2. 环境准备与项目初始化工欲善其事必先利其器。让我们先搭建一个干净、标准的开发环境。2.1 软件安装与版本管理安装Unity Hub和Unity Editor访问Unity官网下载Unity Hub。在Hub中安装Unity 2022.3 LTS版本。LTS长期支持版更稳定适合项目开发。在安装时至少勾选Windows/MonoDevelop或MacOS构建模块以及Universal Windows Platform Build Support如果目标平台是PC。版本控制强烈推荐使用Git进行版本管理。在项目根目录初始化Git仓库能让你安心尝试和回滚。创建.gitignore文件忽略Unity生成的临时文件。可以使用官方提供的模板。# 在项目根目录执行 git init # 从github.com/github/gitignore/blob/main/Unity.gitignore 获取内容保存为 .gitignore2.2 创建新项目与基础设置新建项目打开Unity Hub点击“New Project”。选择2D核心模板命名为PlatformerDemo2D选择合适的存储路径。项目创建后进入Unity编辑器。初始场景设置默认会有一个名为“SampleScene”的场景。我们将其重命名为MainGame。在菜单栏选择Window Rendering Lighting Settings打开光照设置面板。取消勾选Auto Generate然后点击Generate Lighting。对于纯2D游戏这可以避免不必要的性能开销并固定光照状态。在Project窗口创建清晰的文件夹结构这是良好工程习惯的开始Assets/ ├── Scripts/ ├── Prefabs/ ├── Scenes/ ├── Sprites/ ├── Audio/ └── Settings/将MainGame场景拖入Scenes文件夹。3. 核心玩法与角色控制系统实现我们的核心玩法定为2D平台跳跃包含移动、跳跃、二段跳、下蹲以及简单的攻击动作。我们将采用组件化、参数化的方式实现方便后续调整。3.1 创建玩家角色与精灵创建玩家GameObject在Hierarchy窗口右键选择2D Object Sprites Square创建一个方形精灵作为临时玩家形象重命名为Player。在Inspector窗口中点击Add Component添加Rigidbody 2D2D刚体和Box Collider 2D2D盒型碰撞体。设置Rigidbody 2D属性Body Type:Dynamic动态刚体受物理影响Gravity Scale:3可根据手感调整值越大下落越快Freeze Rotation Z: ✅ 勾选防止角色翻滚编写玩家控制器脚本在Assets/Scripts/文件夹下右键选择Create C# Script命名为PlayerController。双击脚本用VS Code或Rider等编辑器打开编写以下代码using UnityEngine; public class PlayerController : MonoBehaviour { [Header(Movement Settings)] [SerializeField] private float moveSpeed 8f; [SerializeField] private float jumpForce 12f; [SerializeField] private int maxAirJumps 1; // 允许的空中跳跃次数实现二段跳 [Header(Ground Check)] [SerializeField] private Transform groundCheckPoint; [SerializeField] private float groundCheckRadius 0.2f; [SerializeField] private LayerMask groundLayer; [Header(Attack Settings)] [SerializeField] private GameObject attackHitBox; [SerializeField] private float attackDuration 0.3f; // 组件引用 private Rigidbody2D rb; private Animator animator; private SpriteRenderer spriteRenderer; // 状态变量 private float horizontalInput; private bool isGrounded; private bool isFacingRight true; private int airJumpCount; private bool isAttacking; private float attackTimer; void Start() { rb GetComponentRigidbody2D(); animator GetComponentAnimator(); spriteRenderer GetComponentSpriteRenderer(); if (attackHitBox ! null) attackHitBox.SetActive(false); } void Update() { // 1. 获取输入 horizontalInput Input.GetAxisRaw(Horizontal); // 返回 -1, 0, 1 // 2. 地面检测使用圆型检测 isGrounded Physics2D.OverlapCircle(groundCheckPoint.position, groundCheckRadius, groundLayer); // 3. 重置空中跳跃次数当角色落地时 if (isGrounded rb.velocity.y 0.1f) { airJumpCount 0; } // 4. 跳跃输入处理 if (Input.GetButtonDown(Jump)) { if (isGrounded) { PerformJump(); } else if (airJumpCount maxAirJumps) { airJumpCount; PerformJump(); } } // 5. 攻击输入处理 if (Input.GetKeyDown(KeyCode.J) !isAttacking) // 使用J键攻击 { StartAttack(); } // 6. 攻击计时器 if (isAttacking) { attackTimer - Time.deltaTime; if (attackTimer 0) { EndAttack(); } } // 7. 更新动画参数如果有Animator UpdateAnimationState(); } void FixedUpdate() { // 物理移动应在FixedUpdate中处理 Move(); } void Move() { float targetSpeed horizontalInput * moveSpeed; // 使用Velocity直接控制移动保持Y轴速度不变 rb.velocity new Vector2(targetSpeed, rb.velocity.y); // 角色朝向翻转 if (horizontalInput 0 !isFacingRight) { Flip(); } else if (horizontalInput 0 isFacingRight) { Flip(); } } void PerformJump() { // 将Y轴速度置零确保每次跳跃力度一致 rb.velocity new Vector2(rb.velocity.x, 0); rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } void StartAttack() { isAttacking true; attackTimer attackDuration; if (attackHitBox ! null) { attackHitBox.SetActive(true); } // 这里可以触发攻击动画 } void EndAttack() { isAttacking false; if (attackHitBox ! null) { attackHitBox.SetActive(false); } } void Flip() { isFacingRight !isFacingRight; spriteRenderer.flipX !isFacingRight; } void UpdateAnimationState() { if (animator ! null) { animator.SetBool(IsRunning, Mathf.Abs(horizontalInput) 0.1f); animator.SetBool(IsGrounded, isGrounded); animator.SetFloat(VerticalVelocity, rb.velocity.y); animator.SetBool(IsAttacking, isAttacking); } } // 在Scene视图中绘制地面检测点便于调试 void OnDrawGizmosSelected() { if (groundCheckPoint ! null) { Gizmos.color Color.green; Gizmos.DrawWireSphere(groundCheckPoint.position, groundCheckRadius); } } }这段代码实现了完整的移动、跳跃、二段跳、攻击逻辑并且通过[SerializeField]将关键参数暴露在Inspector面板方便策划调整。配置玩家对象将PlayerController脚本拖拽到Player对象上。在Player对象下创建一个空子对象重命名为GroundCheck并将其拖拽到脚本的GroundCheck Point槽位。这个点将用于检测角色是否着地。创建一个新的空对象作为AttackHitBox为其添加Box Collider 2D设置为Is Trigger和脚本用于检测攻击命中然后将其拖拽给玩家控制器。暂时可以先不实现伤害逻辑。在Inspector中调整Move Speed、Jump Force等参数感受不同的手感。3.2 创建基础地形与碰撞创建平台同样使用2D Object Sprites Square创建多个方形重命名为Platform缩放其大小摆放在场景中作为地面和跳台。为每个平台添加Box Collider 2D会自动适配精灵大小。创建一个Ground层Layer。在Inspector顶部点击Layer下拉框选择Add Layer...添加一个名为Ground的新层。选中所有平台将它们的Layer设置为Ground。回到PlayerController脚本所在的Inspector将Ground Layer设置为Ground。这样地面检测就只会检测Ground层的物体。添加相机跟随在Hierarchy中选择Main Camera。点击Add Component搜索并添加Cinemachine包中的CinemachineVirtualCamera。如果未安装Cinemachine可通过Window Package Manager搜索安装。新建一个CinemachineVirtualCamera将其Follow目标设置为Player。这样相机会平滑跟随玩家。4. 构建游戏循环收集物、敌人与UI一个完整的游戏循环需要目标、挑战和反馈。我们来添加收集物和简单的敌人。4.1 创建可收集的硬币创建硬币Prefab创建一个圆形精灵作为硬币重命名为Coin。添加Circle Collider 2D并勾选Is Trigger。添加一个简单的旋转动画选中硬币打开Window Animation Animation窗口创建新动画CoinSpin.anim在0帧和30帧分别设置旋转角度如0°和360°确保循环播放。将制作好的Coin从Hierarchy拖入Assets/Prefabs文件夹生成预制体。然后可以删除场景中的实例后续用预制体摆放。编写硬币收集脚本创建脚本Coin.cs并挂载到硬币预制体上。using UnityEngine; public class Coin : MonoBehaviour { [SerializeField] private int scoreValue 10; [SerializeField] private AudioClip collectSound; private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag(Player)) { // 通知游戏管理器增加分数 GameManager.Instance?.AddScore(scoreValue); // 播放音效如果有 if (collectSound ! null) { AudioSource.PlayClipAtPoint(collectSound, transform.position); } // 销毁硬币 Destroy(gameObject); } } }4.2 创建简单的巡逻敌人创建敌人Prefab创建一个精灵作为敌人重命名为Enemy。添加Rigidbody 2DBody Type设为Kinematic以避免物理推挤问题和Box Collider 2D。创建脚本EnemyPatrol.cs。using UnityEngine; public class EnemyPatrol : MonoBehaviour { [SerializeField] private float moveSpeed 2f; [SerializeField] private Transform leftPoint; [SerializeField] private Transform rightPoint; [SerializeField] private LayerMask playerLayer; private Rigidbody2D rb; private bool movingRight true; private Transform currentTarget; void Start() { rb GetComponentRigidbody2D(); currentTarget rightPoint; } void Update() { Patrol(); } void Patrol() { // 计算移动方向 Vector2 direction (currentTarget.position - transform.position).normalized; rb.velocity new Vector2(direction.x * moveSpeed, rb.velocity.y); // 检查是否到达巡逻点 if (Vector2.Distance(transform.position, currentTarget.position) 0.1f) { // 切换目标点 currentTarget (currentTarget rightPoint) ? leftPoint : rightPoint; } // 简单视觉翻转 if (direction.x 0 transform.localScale.x 0) { Flip(); } else if (direction.x 0 transform.localScale.x 0) { Flip(); } } void Flip() { Vector3 scale transform.localScale; scale.x * -1; transform.localScale scale; } // 简单碰撞伤害可选 private void OnCollisionEnter2D(Collision2D collision) { if (((1 collision.gameObject.layer) playerLayer) ! 0) { // 对玩家造成伤害 collision.gameObject.GetComponentPlayerHealth()?.TakeDamage(1); } } }在场景中创建两个空对象LeftBound和RightBound作为敌人的巡逻点拖拽赋值给敌人脚本。4.3 实现游戏管理器与UI创建游戏管理器单例模式创建脚本GameManager.cs这是一个管理游戏状态分数、生命、关卡的中心。using UnityEngine; using UnityEngine.UI; public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } [Header(UI References)] [SerializeField] private Text scoreText; [SerializeField] private Text livesText; [SerializeField] private GameObject gameOverPanel; private int currentScore 0; private int playerLives 3; void Awake() { // 简单的单例实现确保只有一个GameManager if (Instance null) { Instance this; DontDestroyOnLoad(gameObject); // 跨场景不销毁 } else { Destroy(gameObject); } } void Start() { UpdateUI(); } public void AddScore(int points) { currentScore points; UpdateUI(); } public void LoseLife() { playerLives--; UpdateUI(); if (playerLives 0) { GameOver(); } } void UpdateUI() { if (scoreText ! null) scoreText.text $Score: {currentScore}; if (livesText ! null) livesText.text $Lives: {playerLives}; } void GameOver() { Time.timeScale 0f; // 暂停游戏 if (gameOverPanel ! null) gameOverPanel.SetActive(true); } // 提供给UI按钮调用的方法 public void RestartGame() { Time.timeScale 1f; UnityEngine.SceneManagement.SceneManager.LoadScene(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex); } }搭建UI界面在Hierarchy右键UI Canvas创建画布。在Canvas下创建UI Text显示分数和生命值。创建UI Panel作为游戏结束界面内部包含Text和Button。将对应的UI元素拖拽到GameManager脚本的公开字段中。5. 关卡设计与体验打磨作为策划关卡设计是展示你能力的关键。即使是一个小Demo也要有起承转合。5.1 设计第一个教学关卡引导区起始区域平坦放置几个硬币在必经之路上让玩家自然学会移动和收集。基础挑战区设置几个间距逐渐增大的平台让玩家练习跳跃时机。在最后一个平台放一个硬币作为奖励。引入敌人在一个较长的平台上放置一个巡逻敌人。在敌人路径上方或下方设计一个安全通道让玩家观察敌人移动规律后通过跳过去或蹲下通过。技巧挑战区设计一个需要“二段跳”才能到达的平台上面放一个硬币或特殊奖励。终点设置一个目标点如门、旗帜玩家触碰到即弹出“关卡完成”UI。5.2 添加视听反馈音效在Assets/Audio文件夹中导入或创建音效跳跃声、收集声、受伤声、敌人移动声。使用AudioSource组件播放。对于一次性的音效如收集使用AudioSource.PlayClipAtPoint。粒子效果当玩家跳跃落地时可以生成一个简单的灰尘粒子。在PlayerController的落地检测处实例化一个粒子预制体。硬币被收集时可以播放一个闪烁消失的粒子动画。屏幕抖动当玩家受到伤害或击败敌人时可以添加一个简单的相机抖动效果增强打击感。这可以通过写一个协程Coroutine短暂修改相机位置来实现。5.3 参数调整与手感优化这是策划工作的核心。反复游玩自己的关卡调整PlayerController中的Move Speed、Jump Force、Gravity Scale直到移动和跳跃感觉“舒适”和“可控”。敌人的Move Speed和巡逻范围确保其构成挑战但又不至于无法通过。硬币的旋转速度、粒子效果大小等让反馈清晰明了。6. 构建、打包与作品集呈现一个能独立运行的Demo比在编辑器里打开的工程更专业。6.1 构建可执行文件点击File Build Settings。将当前场景MainGame拖入Scenes In Build列表。选择目标平台如PC, Mac Linux Standalone选择Target PlatformWindows/Mac。点击Build选择一个输出文件夹生成.exeWindows或.appMac文件以及相关的数据文件夹。6.2 制作作品集文档/视频这是将Demo转化为求职利器的最后一步。录制演示视频1-2分钟使用OBS等软件录制游戏过程。视频开头快速展示游戏名称和核心玩法。视频主体流畅地展示从教学到挑战的完整关卡流程重点展示你设计的亮点如二段跳挑战、敌人规避。视频结尾展示简单的UI和游戏结束画面。准备设计说明文档一页PPT或PDF项目概述游戏名称、类型、核心玩法一句话介绍。我的职责明确写出你负责的所有内容系统设计、关卡搭建、参数调优、部分脚本编写。设计亮点可调参数系统展示Inspector面板中你暴露的各类参数说明这是为了方便迭代和平衡。关卡设计思路用截图箭头标注的方式图解你的教学关卡如何引导玩家、逐步提升难度。核心循环用简图画出“移动-跳跃-收集-规避-到达终点”的循环。遇到的问题与解决方案挑1-2个具体问题比如“二段跳手感飘忽”然后说明你如何通过调整Jump Force和Gravity Scale来解决。未来扩展方向列出2-3个如果时间允许你会添加的内容如更多敌人类型、技能系统、关卡编辑器这展示了你的设计视野。7. 常见问题与排查思路在制作过程中你可能会遇到以下典型问题问题现象可能原因排查与解决思路角色不受控制或移动异常1.PlayerController脚本未挂载或禁用。2.Rigidbody 2D组件缺失或属性错误如Body Type不是Dynamic。3. 输入轴名称错误Input.GetAxisRaw(“Horizontal”)拼写需与Input Manager设置一致。1. 检查Hierarchy中Player对象是否有脚本组件且启用。2. 确认Rigidbody 2D存在且为Dynamic。3. 打开Edit Project Settings Input Manager查看水平输入轴名称。跳跃手感“粘滞”或无法跳起1. 地面检测失效。groundCheckPoint未赋值或位置不对。2.groundLayer设置错误平台未设置在Ground层。3.Jump Force值太小或Gravity Scale太大。1. 在Scene视图查看Gizmos绿色检测圈是否与地面接触。2. 检查平台对象的Layer是否为Ground并确认脚本中Ground Layer下拉框选中了Ground。3. 在Inspector中适当增大Jump Force或减小Gravity Scale。硬币收集后分数不增加1.Coin脚本中的GameManager.Instance为null。2.GameManager对象未在场景中或存在多个实例被销毁。3. 玩家对象没有Tag设置为 “Player”。1. 确保场景中有一个GameObject挂载了GameManager脚本。2. 检查GameManager的Awake方法确保单例逻辑正确。3. 在Hierarchy选中Player对象在Inspector顶部将其Tag设为“Player”。敌人不移动或移动方向错误1.EnemyPatrol脚本中的leftPoint/rightPoint未赋值。2. 敌人的Rigidbody 2D的Body Type可能应为Kinematic否则可能与地形发生物理碰撞卡住。3. 巡逻点位置设置不合理如重合。1. 在Inspector中确认两个巡逻点Transform已正确拖拽赋值。2. 尝试将敌人刚体类型改为Kinematic。3. 在Scene视图中直观检查两个巡逻点的位置。构建后游戏运行黑屏或报错1. 场景未添加到Build Settings。2. 使用了编辑器独有的资源或路径。3. 脚本编译错误在构建时未解决。1. 确认File Build Settings中Scenes In Build列表包含你的主场景。2. 检查代码中是否有Application.dataPath等在构建后路径会变化。3. 在构建前务必确保Console窗口没有任何错误红色。8. 最佳实践与进阶建议当你完成基础Demo后以下实践能让你的项目更出彩更贴近工业级开发流程。8.1 代码与架构优化使用ScriptableObject进行数据配置将敌人属性血量、速度、伤害、关卡数据等抽离成ScriptableObject资源文件。这实现了数据与逻辑的分离策划可以在不修改代码的情况下调整数值。状态机管理角色状态对于更复杂的角色行为 idle, run, jump, attack, hurt使用动画状态机Animator配合代码中的枚举状态来管理比一堆布尔变量更清晰。对象池管理频繁创建销毁的对象如子弹、特效、敌人。使用对象池能显著提升性能。8.2 策划思维的体现建立平衡性调整文档用一个Excel或文本文件记录所有可调参数如玩家速度、跳跃力、敌人血量、硬币分数及其调整记录。面试时可以展示这个过程体现你的数值敏感度和迭代意识。设计“体验目标”在开始设计关卡前先写下希望玩家感受到什么如“紧张后的释放”、“探索的惊喜”然后让所有设计服务于这个目标。进行简单的可用性测试让完全没玩过的朋友试玩观察他卡在哪里、哪里感到困惑。根据反馈调整引导和难度。这个过程本身就是极佳的策划经验。8.3 作品集包装要点针对性根据你申请的策划方向系统、关卡、战斗调整Demo和文档的侧重点。申请关卡策划就深挖关卡设计文档申请系统策划就详细阐述游戏循环和经济系统雏形。诚实明确说明哪些是原创设计哪些是参考了经典游戏或教程。对借鉴的部分进行分析和再设计同样能体现你的思考。可访问性将演示视频上传到B站或腾讯视频生成链接。将设计文档和可执行文件打包上传到网盘。在简历中提供清晰的链接和提取码。这个2.0版本的Demo项目从技术实现到设计思考为你构建了一个展示策划综合能力的坚实起点。它证明了你具备将概念落地的基本技术执行力、基础的关卡设计能力和系统性的思考方式。接下来你可以以此为基底尝试加入一个简单的“冲刺”技能、设计两种不同行为的敌人、或者做一个包含3个小关卡的迷你世界。每一次迭代都是你作品集和能力的升级。