# mini_move **Repository Path**: jacky2code/mini_move ## Basic Information - **Project Name**: mini_move - **Description**: Unity 2d 平台跳跃游戏 - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2022-04-02 - **Last Updated**: 2024-07-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MiNi Move 2D 平台跳跃类游戏,使用 unity 开发。 20220421110816 ## Unit 1 Install & Assets 建议使用版本 - Unity 2019.4.X ### Section 1 Setup Assets 资源设置 - 原素材下载链接 itch.io:[点击跳转](https://pixelfrog-store.itch.io/pirate-bomb) 可以下载原始图片进行更改创新。 - 将具有多个元素的精灵纹理 Assets/Sprites Assets/8-Tile-Sets/Tile-Sets (64-64).png 文件做以下更改: - Sprite Mode: Multiple - 精灵模式:多元素 - Pexels Per Unit: 64 - 单元像素:64 - Compression: None - 压缩方式:无 - 切片: - 通过 Sprite Editor 选择 Slice - Type - Grid by cell size - 64 * 64 - Slice 进行对大图按照 64 像素切片 ## Unit 2 Build Level ### Section 1 Use Tilemap 使用 Tilemap 瓦片地图 导入瓦片图片信息并绘制场景 - 选择 Tile Palette 窗口,创建 Assets/Tilemap/Tile Palette/Map.prefab - 注意 Background、Platform 要分开创建,避免修改干扰 ### Section 2 Rule Tile 规则瓦片 - 使用 Package Manager 下载 - (2019.4) https://learning-cdn-public-prd.unitychina.cn/20201210/5633636e-e845-40ab-8af0-2df322849241/2d-extras-master(2019.4).zip 扩展包 - (2020.1)https://learning-cdn-public-prd.unitychina.cn/20201210/bf941621-493b-4588-8702-8c0e0e9c7e72/2d-extras-master(2020.1).zip ### Section 3 Tilemap Collider 瓦片碰撞器 瓦片地图碰撞器,了解 Tilemap Collider 2D 和 Composite Collider 的使用方法。 - 调整 Layer 显示顺序 - 选择 Background 添加Sorting Layer,命名为background,并将其调整为最上面顺序(最上面显示在最后) - 给物体 Background 指定 Sorting Layer为 backgournd - 给 platfrom 添加碰撞体 - 为 platform 添加 Tilemap Collider 2D 地图碰撞体 - 为 platform 添加 Composite Collider 2D 碰撞体。在附带的 Rigidbody 2D 中,由于重力为普通1,运行游戏时,platform会掉落,所以 Body Type 选择 Static。 - 在 Tilemap Collider 2D 中选择 Used By Composite 合并成一个整体碰撞体。 ### Section 4 Other Objects 其他物体 创建场景中的其他物体 设置 刚体 / 碰撞体 / Sorting Layer - 给其他物品添加 Rigidbody 2D - 给其他物品添加 Polygon Collider 2D - 并修改多边形碰撞体,适配每个物品的形状;左键鼠标增加碰撞点,并拖拽贴合物体。 ### Section 5 Physics 2D & Prefab 2D物理及预制体 - 物体碰撞关系设置 - 添加 User Layer 8:Ground; - 添加 User Layer 9:Environment - 设置 Platform 的 Layer 为 Ground - 设置其他物体的 Layer 为 Environment - 在 Project Setting 中设置各个 Layer 的碰撞关系 - prefab 预制体的使用方法 - 设置物体 Rigidbody 2D - Collision Detection - Continuous - 连续碰撞检查 - 设置每个物体的质量:Rigidbody 2D - Mass - ... - 在 Assets 目录中新建 Prefabs 目录,拖拽场景中的其他物体到目录中,自动生成预制体。 ## Unit 3 Create Player ### Section 1 Setup Player 创建玩家 设置 Player 的必要组件,调整各个组件的参数。设置碰撞体大小和碰撞关系。 - 调整 Player-Bomb 图片,按第一章节方式调整 32 像素 - 拖拽一张图片到场景中 命名为 Player,添加刚体和碰撞体 - 调整 Mass 为 5;锁定 Z 轴,Constraints- Freeze Rotation - Z 打钩 - 调整 Player 的碰撞体大小适配脚部宽度大小 - 为 Player 添加 Layer 和 Sorting Layer 为 NPC - 在 Project Setting 中设置 NPC 和 Environment 不碰撞 ### Section 2 Movement 基本的移动 让 Player 可以移动,Input Manager 使用方法,如何反转人物。 - 给 Player 添加 PlayerController.cs 脚本,实现左右移动。 ``` csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { private Rigidbody2D rb; // 速度 public float Speed; // 跳跃力 public float JumpForce; // Start is called before the first frame update void Start() { rb = GetComponent(); } // Update is called once per frame void Update() { } public void FixedUpdate() { Movement(); } // 移动 void Movement() { // 获取键盘输入 float horizontalInput = Input.GetAxisRaw("Horizontal"); // 左右移动 rb.velocity = new Vector2(horizontalInput * Speed, rb.velocity.y); // player 左右翻转 if(horizontalInput != 0) { // 通过控制 x 左右翻转 transform.localScale = new Vector3(horizontalInput, 1, 1); } } } ``` ### Section 3 Jump 跳跃 Update 和 FixedUpdate 的使用方法 地面物理检测 Physics2D 的函数方法 OnDrawGizmos 可视化的范围调整 - 为 PLayer 添加空物体,选择icon颜色,作为检测点,并调整Gizmos范围大小 ``` csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { // 速度 public float Speed; // 跳跃力 public float JumpForce; [Header("Ground Check")] // 检测点 public Transform GroundCheck; // 检测范围 public float CheckRadius; // 检测图层 public LayerMask GroundLayer; [Header("States Check")] public bool IsGround; public bool CanJump; private Rigidbody2D rb; void Start() { rb = GetComponent(); } void Update() { CheckInput(); } public void FixedUpdate() { PhysicsCheck(); Movement(); Jump(); } /// /// 检测输入 /// void CheckInput() { if (Input.GetButtonDown("Jump") && IsGround) { CanJump = true; } } /// /// 移动 /// void Movement() { // 获取键盘输入 float horizontalInput = Input.GetAxisRaw("Horizontal"); // 左右移动 rb.velocity = new Vector2(horizontalInput * Speed, rb.velocity.y); // player 左右翻转 if(horizontalInput != 0) { // 通过控制 x 左右翻转 transform.localScale = new Vector3(horizontalInput, 1, 1); } } /// /// 跳跃 /// void Jump() { if (CanJump) { rb.velocity = new Vector2(rb.velocity.x, JumpForce); // 更改重力,增加下落效果 rb.gravityScale = 4; CanJump = false; } } /// /// 物理检测 /// void PhysicsCheck() { IsGround = Physics2D.OverlapCircle(GroundCheck.position, CheckRadius, GroundLayer); // 落到地面后把重力重新改为1 if (IsGround) { rb.gravityScale = 1; } } /// /// unity 自带方法,绘制检测 /// public void OnDrawGizmos() { Gizmos.DrawWireSphere(GroundCheck.position, CheckRadius); } } ``` ## Unit 4 Animation ### Section 1 Setup Animation 设置动画 制作 2D Sprite 帧动画,制作多个需要的动画片段。 - 通过窗口 Animation 创建动画,拖拽相应动画图片,设置好 Samples 为20; ### Section 2 Animator States 动画控制器状态 动画状态机的切换方法,设置参数和条件,使用代码切换动画。 ``` csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerAnimation : MonoBehaviour { private Animator anim; private Rigidbody2D rb; private PlayerController playerCrl; void Start() { anim = GetComponent(); rb = GetComponent(); playerCrl = GetComponent(); } void Update() { // 获取 Player 移动速度,并取绝对值,传递给 speed 执行 run 动画 anim.SetFloat("speed", Mathf.Abs(rb.velocity.x)); anim.SetFloat("velocityY", rb.velocity.y); anim.SetBool("jump", playerCrl.Jumpping); anim.SetBool("ground", playerCrl.IsGround); } } ``` ### Section 3 Jump VFX 跳跃的特效 制作 跳跃 / 落地 的帧动画特效,代码控制播放的时机,碰撞关系。 - 创建 LandFX 特效动画,在动画最后一帧,添加Event - 创建 JumpFX 同上 ``` csharp using UnityEngine; public class LandJumpFX : MonoBehaviour { public void Finish() { gameObject.SetActive(false); } } ``` ## Unit 5 Create Bomb ### Section 1 Setup Bomb 创建炸弹 创建炸弹,添加必要组件,调整碰撞关系,创建三种状态动画。 - 创建炸弹对象,需要添加 Circle Collider 2D,并调整碰撞体大小。 ### Section 2 Bomb Explosion 炸弹爆炸效果 创建 Animation Event 调用的函数方法实现爆炸效果。通过 **Collider2D[]** 获得范围内物体数组,实现炸开弹飞效果。 ``` csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class Bomb : MonoBehaviour { private Animator animator; private Collider2D coll; private Rigidbody2D rig; public float StartTime; public float WaitTime; // 炸弹威力 public float BombForce; [Header("Check")] public float Radius; public LayerMask TargetLayer; void Start() { animator = GetComponent(); coll = GetComponent(); rig = GetComponent(); StartTime = Time.time; } void Update() { if (Time.time > StartTime + WaitTime) { // 通过名字播放动画 animator.Play("Bomb_Explotion"); } } /// /// unity 自带方法,绘制检测 /// public void OnDrawGizmos() { Gizmos.DrawWireSphere(transform.position, Radius); } /// /// 爆炸 /// animation event 爆炸动画第一帧运行 /// public void Explotion() { // 剔除炸弹碰撞体,避免炸弹自己飞上天。。。 coll.enabled = false; // 获取爆炸影响到的所有物体 Collider2D[] aroundObjects = Physics2D.OverlapCircleAll(transform.position, Radius, TargetLayer); // 防止炸弹因为没有碰撞体而掉落 rig.gravityScale = 0; foreach (var item in aroundObjects) { // 获取物体在 角色 的方向位置 Vector3 pos = transform.position - item.transform.position; // 给物体添加(反方向 + 向上)的爆炸威力和冲击力 item.GetComponent().AddForce((-pos + Vector3.up) * BombForce, ForceMode2D.Impulse); } } /// /// 爆炸万后销毁 /// public void DestroyThis() { Destroy(gameObject); } } ``` ### Section 3 Player Attack 玩家攻击 让 Player 能够控制释放 Bomb 的 Prefab。设置CD时间。 ``` csharp [Header("Attack Setting")] public GameObject BombPrefab; // 下一次攻击时间 public float NextAttack = 0; // 攻击时间间隔 public float AttackRate = 2; public void Attack() { if (Time.time > NextAttack) { Instantiate(BombPrefab, transform.position, BombPrefab.transform.rotation); NextAttack = Time.time + AttackRate; } } ``` ## Unit 6 Create Enemy Scripts & AI ### Section 0 Enemies Assets Overview 敌人素材预览 ### Section 1 Setup Basic Enemy 设置基本敌人 创建基本敌人 Cucumber 黄瓜怪。设置帧动画。添加 碰撞体 / 刚体。设置碰撞图层,调整碰撞关系。 ### Section 2 More Elements 更多的组件 设置巡逻点。添加设置攻击检测范围。调整 Check Area 碰撞图层和关系。 ### Section 3 Basic Methods 基本函数方法 创建 Enemy 代码中的基本函数方法。实现 巡逻 / 移动 / 反转 / 添加攻击列表的方法。学习 List<> 的使用。 ### Section 4 Inheritance 继承 创建单独的 Cucumber 代码并继承 Enemy 基类。学习 virtual 函数方法如何在子类当中 override 重写。调整代码和参数。 ``` csharp using UnityEngine; public class Enemy : MonoBehaviour { ... } ``` ``` csharp using UnityEngine; public class Cucumber : Enemy { } ``` ### Section 5 Finite States Machine 有限状态机 了解 FSM 状态机的概念 / 抽象类概念 / 抽象函数方法。用抽象类继承创建2个敌人AI状态:PatrolState / AttackState。 ``` csharp /// /// 抽象类 EnemyBaseState /// public abstract class EnemyBaseState { /// /// 进入状态 /// public abstract void EnterState(Enemy enemy); public abstract void OnUpdate(Enemy enemy); } ``` ``` csharp using UnityEngine; /// /// 巡逻状态 /// public class PatrolState : EnemyBaseState { public override void EnterState(Enemy enemy) { enemy.AnimState = 0; enemy.SwitchPoint(); } public override void OnUpdate(Enemy enemy) { // 获取当前动画状态,如果不是 Idle 动画 if (!enemy.Anim.GetCurrentAnimatorStateInfo(0).IsName("Idle")) { enemy.AnimState = 1; enemy.MoveToTarget(); } if (Mathf.Abs(enemy.transform.position.x - enemy.TargetPoint.position.x) < 0.01f) { enemy.TransitionToState(enemy.PatrolState); } if (enemy.AttackList.Count > 0) { enemy.TransitionToState(enemy.AttackState); } } } ``` ### Section 6 Animator States 动画状态机 在 Animator 窗口当中,使用多个 Layer 来控制管理多种动画状态。并且通过代码脚本来控制动画的切换。 ### Section 7 Switch Attack Target 切换攻击目标 从供给列表中找到离自己最近的作为目标,并且判断目标的 Tag 是 Player 或者 Bomb 来执行不同的攻击方式。 ``` csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class AttackState : EnemyBaseState { public override void EnterState(Enemy enemy) { //Debug.Log("发现敌人了!!!"); enemy.AnimState = 2; enemy.TargetPoint = enemy.AttackList[0]; } public override void OnUpdate(Enemy enemy) { // 如果没有敌人了,切换到巡逻状态 if (enemy.AttackList.Count == 0) { enemy.TransitionToState(enemy.PatrolState); } // 计算哪个敌人最近 if (enemy.AttackList.Count > 1) { for (int i = 0; i < enemy.AttackList.Count; i++) { if (Mathf.Abs(enemy.transform.position.x - enemy.AttackList[i].position.x) < Mathf.Abs(enemy.transform.position.x - enemy.TargetPoint.position.x)) { enemy.TargetPoint = enemy.AttackList[i]; } } } // 判断敌人类型:Player / Bomb if (enemy.TargetPoint.CompareTag("Player")) { // 攻击 enemy.AttackAction(); } if (enemy.TargetPoint.CompareTag("Bomb")) { // 释放技能 enemy.SkillAction(); } // 追逐最近的目标 enemy.MoveToTarget(); } } ``` ### Section 8 Attack Action 攻击方式 创建攻击有关的变量,实现 AttackAction 和 SkillAction 两个函数方法。判断攻击距离与攻击间隔同时满足条件的情况,采取相应的行动。 ``` csharp /// /// 普通攻击 /// public virtual void AttackAction() { if (Vector2.Distance(transform.position, TargetPoint.position) < AttackRange) { if (Time.time > nextAttack) { Debug.Log("进行攻击!"); nextAttack = Time.time + AttackRate; } } } /// /// 技能攻击 /// public virtual void SkillAction() { if (Vector2.Distance(transform.position, TargetPoint.position) < skillRange) { if (Time.time > nextAttack) { Debug.Log("这是炸弹,释放炸弹技能!"); nextAttack = Time.time + AttackRate; } } } ``` ### Section 9 Hit Point 打击点 创建攻击点 HitPoint 录制动画实现攻击 Player 的方法。创建 HitPoint 代码脚本。 ``` csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class HitPoint : MonoBehaviour { private void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { Debug.Log("Player get hurt !"); } if (other.CompareTag("Bomb")) { } } } ``` ### Section 10 Skill Action 技能攻击方式 通过实际创建 Cucumber 的吹灭炸弹特殊技能,了解当敌人的攻击目标是炸弹的时候,如何采取特殊技能效果。 ``` csharp /// /// 熄灭炸弹 /// public void TurnOff() { animator.Play("Bomb_Off"); // 更改炸弹图层 gameObject.layer = LayerMask.NameToLayer("NPC"); } public void TurnOn() { StartTime = Time.time; animator.Play("Bomb_On"); gameObject.layer = LayerMask.NameToLayer("Bomb"); } ``` ### Section 11 Interface:IDamageable 创建接口 学习 Interface 接口的概念,如何通过创建 1 个接口来实现访问所有继承这个接口的代码脚本。轻松实现炸弹爆炸让周围的人物都有受到伤害的效果。 ``` csharp public interface IDamageable { /// /// 受伤害 /// /// void GetHit(float damage); } ``` ``` csharp public class Cucumber : Enemy, IDamageable { public void GetHit(float damage) { Health = Health - damage; if (Health < 1) { Health = 0; IsDead = true; } Anim.SetTrigger("Hit"); } // Animation Event public void SetOffBomb() { TargetPoint.GetComponent()?.TurnOff(); } } ``` ### Section 12 Player Get Hit 玩家获得伤害 通过 IDamageable 接口实现玩家受伤,并且受伤动画播放期间短暂无敌。创建敌人警示标示,学习 协程 的使用方法 - 协程函数 ``` csharp public void OnTriggerEnter2D(Collider2D collision) { StartCoroutine(OnSign()); } /// /// 用协程的方式打开和关闭,敌人角色遇险警告。 /// /// IEnumerator OnSign() { warningSign.SetActive(true); yield return new WaitForSeconds( // 获取第 0 个Layer的第 0 个动画片段时长 warningSign.GetComponent().GetCurrentAnimatorClipInfo(0)[0].clip.length ); warningSign.SetActive(false); } ``` ## Unit 7 Skill Action for Each Enemies ### Section 0 Unit Overview 本单元概览 ### Section 1 Bald Pirate 光头海盗 光头海盗的特殊技能:踢炸弹。HitPoint 代码添加弹开效果。 ``` csharp using UnityEngine; /// /// 攻击受伤碰撞检测点 /// !!!!!!!!!!!!!!!!!!!!!!注意:!!!!!!!!!!!!!!!!!!!!! /// 给物体的 Collider 2D 勾选 Is Trigger /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! /// public class HitPoint : MonoBehaviour { // 判断是否有针对炸弹可以使用 public bool BombAvialble; // 炸弹位于BaldPirate的方向(用于Bald踢炸弹的方向) private int dir; /// /// 和另一个物体碰撞 /// /// private void OnTriggerEnter2D(Collider2D other) { // 判断碰撞物体在本物体哪一侧 if (transform.position.x > other.transform.position.x) { dir = -1; } else { dir = 1; } if (other.CompareTag("Player")) { Debug.Log("Player get hurt !"); // 普通攻击受伤害:1 other.GetComponent().GetHit(1); // 伤害并对对方有一个方向作用力,力度:10 other.gameObject.GetComponent().AddForce( new Vector2(dir, 1) * 10, ForceMode2D.Impulse); } if (other.CompareTag("Bomb") && BombAvialble) { // 踢走炸弹,力度:10 other.gameObject.GetComponent().AddForce( new Vector2(dir, 1) * 10, ForceMode2D.Impulse); } } } ``` ### Section 2 Big Guy 大块头 实现大块头拾取炸弹并扔向玩家的方法。添加 Animation Event 设置 PickUp Point。 ``` csharp using UnityEngine; /// /// 大块头 /// public class BigGuy : Enemy, IDamageable { public Transform PickPoint; public float ThrowPower; public void GetHit(float damage) { Health = Health - damage; if (Health < 1) { Health = 0; IsDead = true; } Anim.SetTrigger("Hit"); } /// /// 拾取炸弹 Animation Event /// public void PickUpBomb() { if (TargetPoint.CompareTag("Bomb") && !HasBomb) { TargetPoint.gameObject.transform.position = PickPoint.position; // 把炸弹设置到point子集,可以跟随角色 TargetPoint.SetParent(PickPoint); // 取消重力,避免炸弹掉下来 TargetPoint.GetComponent().bodyType = RigidbodyType2D.Kinematic; HasBomb = true; } } /// /// 仍炸弹 Animation Event /// public void ThrowBomb() { if (HasBomb) { TargetPoint.GetComponent().bodyType = RigidbodyType2D.Dynamic; TargetPoint.SetParent(transform.parent.parent); if (FindObjectOfType().gameObject.transform.position.x - transform.position.x < 0) { TargetPoint.GetComponent().AddForce(new Vector2(-1, 1) * ThrowPower, ForceMode2D.Impulse); } else { TargetPoint.GetComponent().AddForce(new Vector2(1, 1) * ThrowPower, ForceMode2D.Impulse); } } HasBomb = false; } } ``` ### Section 3 Captain 船长 粉色船长看见炸弹反跑,利用 Sprite Renderer 组件的 FilpX 参数实现翻转。 ``` csharp using UnityEngine; /// /// 船长 /// 实现人物正向翻转:用 SpriteRenderer 中的 FlipX 实现 /// public class Captain : Enemy, IDamageable { private SpriteRenderer spriteRenderer; public override void Init() { base.Init(); spriteRenderer = GetComponent(); } public override void Update() { base.Update(); // 巡逻模式下面部翻转 if (AnimState == 0) { spriteRenderer.flipX = false; } } public void GetHit(float damage) { Health = Health - damage; if (Health < 1) { Health = 0; IsDead = true; } Anim.SetTrigger("Hit"); } /// /// 重写 Captain 技能,遇见炸弹害怕反跑,奔跑时长为技能动画时长 /// public override void SkillAction() { base.SkillAction(); // 如果正处于 skill 动画状态 if (Anim.GetCurrentAnimatorStateInfo(1).IsName("Captain_Skill")) { // 正面朝向翻转 spriteRenderer.flipX = true; if (transform.position.x > TargetPoint.position.x) { transform.position = Vector2.MoveTowards( transform.position, transform.position + Vector3.right, // 可调整技能动画时长或者速度倍数,来实现角色的反跑距离 Speed * 4 * Time.deltaTime); } else { transform.position = Vector2.MoveTowards( transform.position, transform.position + Vector3.left, Speed * 4 * Time.deltaTime); } } else { spriteRenderer.flipX = false; } } } ``` ### Section 4 Whale 鲸鱼 添加 Animation Event 实现吞灭炸弹,趣味添加吞下炸弹体积变大。 ``` csharp using UnityEngine; /// /// 巨鲸 /// 技能:吃炸弹 /// public class Whale : Enemy, IDamageable { public float Scale; private Vector3 originalScale; public override void Init() { base.Init(); originalScale = transform.localScale; } public void GetHit(float damage) { Health = Health - damage; if (Health < 1) { Health = 0; IsDead = true; } Anim.SetTrigger("Hit"); } /// /// Skill Animation Event /// 吞炸弹 /// public void SwallowBomb() { // 熄灭 TargetPoint.GetComponent().TurnOff(); TargetPoint.gameObject.SetActive(false); if (transform.localScale.y < (originalScale.y * 2.0)) { // 鲸鱼变大 transform.localScale *= Scale; } } } ``` ## Unit 8 User Interface ### Section 1 Create Canvas 创建画布 UGUI 使用方法,添加 Player Health Bar 左上角对齐,并设置 Canvas 的自动缩放。 image-20220422093500768 ### Section 2 Update Health 更新生命值 创建 UIManager 单例模式。获取玩家血量,并实时更新 UI 血量显示。 单例模式: ``` csharp using UnityEngine; public class UIManager : MonoBehaviour { public static UIManager Instance; public void Awake() { if (Instance == null) { Instance = this; } else { Destroy(gameObject); } } } ``` ### Section 3 Pause Menu 暂停菜单 创建暂停菜单,Button 组件添加方法,实现 暂停 / 恢复游戏 image-20220422131438708 ### Section 4 Boss Health Bar Boss 血条 手动创建简单 Slider 组件, Boss 血条实时更新 Enemy.cs ``` csharp void Start() { TransitionToState(PatrolState); if (IsBoss) { UIManager.Instance.SetMaxValueHealthBarBoss(Health); } } public virtual void Update() { Anim.SetBool("Dead", IsDead); if (IsDead) { return; } currentState.OnUpdate(this); Anim.SetInteger("State", AnimState); if (IsBoss) { UIManager.Instance.updateValueHealthBarBoss(Health); } } ``` UIManager.cs ``` csharp public Slider HealthBarBoss; /// /// 设置 Boss 血条最大值 /// /// public void SetMaxValueHealthBarBoss(float health) { HealthBarBoss.maxValue = health; } /// /// 更新 Boss 血条 /// /// public void updateValueHealthBarBoss(float health) { HealthBarBoss.value = health; } ``` ## Unit 9 Game Manager ### Section 1 Game Over 游戏结束画面 创建 GameManager.cs,生成单例,连接 Player 的状态,全局控制游戏的结束,添加 Gameover Panel 实现死亡弹出菜单重新开始游戏。 ``` csharp using UnityEngine; public class Gamemanager : MonoBehaviour { public static Gamemanager Instance; public bool IsGameOver; private PlayerController playerCtrl; public void Awake() { if (Instance == null) { Instance = this; } else { Destroy(gameObject); } playerCtrl = FindObjectOfType(); } public void Update() { IsGameOver = playerCtrl.IsDead; UIManager.Instance.IsShowGameOverPanel(IsGameOver); } } ``` ### Section 2 Way to the next room 通往下一关 - 使用 Platform Effector 组件创建单项平台。 - 创建 Door 入口和出口代码,实现消灭所有敌人后开门的动画,Player 进门触发范围跳转下一场景。 - 观察者模式的使用。 ``` csharp /// /// 游戏结束后重新开始 /// public void RestartGame() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } public void NextLevel() { SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1); } ``` ### Section 3 Save Data 保存数据 使用 PlayerPrefs,保存 Player 的血量延续到下一关,在过关时保存数据。 ``` csharp private string playerHealthKey = "PlayerHealth"; /// /// 读取保存的血量 /// /// public float LoadPlayerHealth() { if (!PlayerPrefs.HasKey(playerHealthKey)) { PlayerPrefs.SetFloat(playerHealthKey, 3.0f); } float currentHealth = PlayerPrefs.GetFloat(playerHealthKey); return currentHealth; } public void SaveData() { PlayerPrefs.SetFloat(playerHealthKey, playerCtrl.Health); PlayerPrefs.Save(); } ``` ### Section 4 Fix bugs 调整bugs - 调整一些代码顺序,更改观察者模式的代码设计方法。 - 创建 Main Menu ### Section 5 Load Game 加载游戏进度 实现保存游戏场景,让玩家可以延续之前的游戏进度。Main Menu 各种 Button 的函数方法实现。 ## Unit 10 Build for Mobile & Monetization ### section 1 Mobile Platform Control 移动平台控制方法 下载使用 Asset Store 中 Joystick 插件, 创建屏幕横向控制器,创建按钮实现跳跃和攻击时放炸弹。重新调整代码配合 Button 的 On Click Event。 - Unity2020开始取消了内置的Asset Store 请访问网页版商城并将需要的插件添加到你的账号中 - Joysick https://assetstore.unity.com/packages/tools/input-management/joystick-pack-107631 - Unity 打开 Package Manager 选择 My Assets 就能看到自己购买的插件下载安装 ``` csharp /// /// 移动 /// void Movement() { // 获取键盘输入 //float horizontalInput = Input.GetAxisRaw("Horizontal"); // 操纵杆 float horizontalInput = joystick.Horizontal; // 左右移动 rb.velocity = new Vector2(horizontalInput * Speed, rb.velocity.y); // player 左右翻转 //if(horizontalInput != 0) //{ // // 通过控制 x 左右翻转 // transform.localScale = new Vector3(horizontalInput, 1, 1); //} if (horizontalInput > 0) { transform.eulerAngles = new Vector3(0, 0, 0); } if (horizontalInput < 0) { transform.eulerAngles = new Vector3(0, 180, 0); } } ``` ### Section 2 Build and Run 生成并运行 生成 Android 和 iOS 平台,真机测试。