Unity类银河战士恶魔城学习总结(P152 Lost Currency Save拾取掉落的灵魂)
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/
本章节实现了死亡后拾取掉落的灵魂,同时给右上角的灵魂做了一个逐渐增加的效果
LostCurrencyController.cs
整体流程
- 游戏场景中会有丢失的灵魂(或货币),每个丢失灵魂对象都挂载了这个脚本。
- 当玩家的角色与丢失灵魂发生触碰(即进入碰撞触发器区域)时,触发
OnTriggerEnter2D
方法。 - 检查碰撞物体是否是玩家,如果是,则:
- 增加玩家的货币或灵魂数量。
- 销毁丢失灵魂的对象,从场景中移除该物体。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LostCurrencyController : MonoBehaviour
{
public int currency;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.GetComponent<Player>()!= null)
{
PlayerManager.instance.currency += currency;//捡取灵魂
Destroy(gameObject);
}
}
}
GameData.cs
主要作用是初始化数据
这个构造函数初始化了所有变量的默认值。它确保在创建 GameData
对象时,所有字段都有一个初始值,从而避免了未赋值的错误。
具体的初始化内容:
lostCurrencyX
,lostCurrencyY
,lostCurrencyAmount
都初始化为0
,表示初始时没有丢失的灵魂。currency
初始化为0
,表示玩家一开始没有货币。skillTree
,inventory
,checkpoints
使用SerializableDictionary
进行初始化,确保它们是可序列化的,并且在后续存储和加载时不会出现空引用错误。equipmentId
初始化为一个空的列表,表示玩家初始没有装备。closestCheckpointId
初始化为空字符串,表示玩家一开始没有最近的检查点。checkpoints
被初始化为一个空的字典,表示游戏开始时没有任何检查点。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.11.25
[System.Serializable]
public class GameData
{
public int currency;
public SerializableDictionary<string, bool>skillTree;
public SerializableDictionary<string, int> inventory;//物品的名字和数量
public List<string> equipmentId;//装备的ID
public SerializableDictionary<string, bool> checkpoints;//检查点的ID和激活状态
public string closestCheckpointId;//最近的检查点ID
public float lostCurrencyX;
public float lostCurrencyY;
public int lostCurrencyAmount;
public GameData()
{
this.lostCurrencyX = 0;
this.lostCurrencyY = 0;
this.lostCurrencyAmount = 0;
this.currency = 0;
skillTree = new SerializableDictionary<string, bool>();
inventory = new SerializableDictionary<string, int>();
equipmentId = new List<string>();
closestCheckpointId =string.Empty;
checkpoints = new SerializableDictionary<string, bool>();
}
}
GameManager.cs
这段 GameManager
脚本负责游戏的核心管理任务,包括:
- 玩家位置的管理。
- 检查点和丢失灵魂的保存与加载。
- 游戏数据的保存和恢复。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
//2024.11.28 17:04
public class GameManager : MonoBehaviour, ISaveManager
{
public static GameManager instance;//单例模式,全局变量
private Transform player;//玩家的位置
[SerializeField] private Checkpoint[] checkpoints;
[SerializeField] private string closestCheckpointId;
[Header("Lost Currency")]
[SerializeField] private GameObject lostCurrencyPrefab;
public int lostCurrencyAmount ;
[SerializeField] private float lostCurrencyX;//丢失灵魂的位置
[SerializeField] private float lostCurrencyY;
private void Awake()
{
if (instance != null)
Destroy(instance.gameObject);
else
instance = this;
checkpoints = FindObjectsOfType<Checkpoint>();//查找所有的检查点
}
private void Start()
{
player = PlayerManager.instance.player.transform;
}
public void RestartScene()
{
SaveManager.instance.SaveGame();//保存游戏
Scene scene = SceneManager.GetActiveScene();
SceneManager.LoadScene(scene.name);
}
public void LoadData(GameData _data)=> StartCoroutine(LoadWithDelay(_data));
private void LoadCheckpoint(GameData _data)//加载检查点
{
foreach (KeyValuePair<string, bool> pair in _data.checkpoints)//遍历数据中的检查点
{
foreach (Checkpoint checkpoint in checkpoints)//遍历场景中的检查点
{
if (checkpoint.id == pair.Key && pair.Value == true) //如果检查点的ID和数据中的ID相同且激活状态为真
checkpoint.ActivateCheckPoint();
}
}
}
private void LoadLostCurrency(GameData _data)//加载丢失的灵魂
{
lostCurrencyAmount = _data.lostCurrencyAmount;
lostCurrencyX = _data.lostCurrencyX;
lostCurrencyY = _data.lostCurrencyY;
if (lostCurrencyAmount > 0)
{
GameObject newlostCurrency = Instantiate(lostCurrencyPrefab, new Vector3(lostCurrencyX, lostCurrencyY), Quaternion.identity);
newlostCurrency.GetComponent<LostCurrencyController>().currency = lostCurrencyAmount;
}
lostCurrencyAmount = 0;//重置丢失的灵魂数量
}
private IEnumerator LoadWithDelay(GameData _data)//延迟加载,防止其他数据未加载
{
yield return new WaitForSeconds(.5f);
LoadCheckpoint(_data);
LoadClosetCheckpoint(_data);
LoadLostCurrency(_data);
}
public void SaveData(ref GameData _data)
{
_data.lostCurrencyAmount = lostCurrencyAmount;
_data.lostCurrencyX = player.position.x;
_data.lostCurrencyY = player.position.y;
if (FindClosestCheckpoint() != null)//如果最近的检查点不为空
_data.closestCheckpointId = FindClosestCheckpoint().id;//将最近的检查点ID存入数据
_data.checkpoints.Clear();
foreach (Checkpoint checkpoint in checkpoints)
{
_data.checkpoints.Add(checkpoint.id, checkpoint.activationStatus);//将检查点的ID和激活状态存入数据
}
}
private void LoadClosetCheckpoint(GameData _data)//将玩家放在最近的检查点
{
if(_data.closestCheckpointId == null)
return;
closestCheckpointId = _data.closestCheckpointId; ;//将最近检查点ID存入变量
foreach (Checkpoint checkpoint in checkpoints)
{
if (closestCheckpointId == checkpoint.id)
player.position = checkpoint.transform.position;
}
}
private Checkpoint FindClosestCheckpoint()//找到最近的检查点
{
float closestDistance = Mathf.Infinity;//正无穷
Checkpoint closestCheckpoint = null;
foreach (var checkpoint in checkpoints)//遍历所有的检查点
{
float distanceToCheckpoint = Vector2.Distance(player.position, checkpoint.transform.position);//计算玩家和检查点之间的距离
if (distanceToCheckpoint < closestDistance && checkpoint.activationStatus == true)//如果距离小于最近距离且检查点激活
{
closestDistance = distanceToCheckpoint;//更新最近距离
closestCheckpoint = checkpoint;//更新最近检查点
}
}
return closestCheckpoint;
}
}
UI_InGame.cs
增加了更新灵魂值得效果,可以更改增加得速率
using UnityEngine;
using UnityEngine.UI;
using TMPro;
//2024年11月22日
public class UI_InGame : MonoBehaviour
{
[SerializeField] private PlayerStats playerStats;//存储玩家的状态信息
[SerializeField] private Slider slider;//显示玩家血量
[SerializeField] private Image dashImage;
[SerializeField] private Image parryImage;
[SerializeField] private Image crystalImage;
[SerializeField] private Image swordImage;
[SerializeField] private Image blackholeImage;
[SerializeField] private Image flaskImage;
private SkillManager skills;
[Header("Souls info")]
[SerializeField] private TextMeshProUGUI currentSouls;
[SerializeField] private float soulsAmount;
[SerializeField] private float increaseRate ;//灵魂值增长速度
void Start()
{
if(playerStats != null)
playerStats.onHealthChanged += UpdateHealthUI;
skills = SkillManager.instance;
}
void Update()
{
UpdataSoulsUI();
if (Input.GetKeyDown(KeyCode.LeftShift) && skills.dash.dashUnlocked)
SetCooldownOf(dashImage);
if (Input.GetKeyDown(KeyCode.Q) && skills.parry.parryUnlocked)
SetCooldownOf(parryImage);
if (Input.GetKeyDown(KeyCode.F) && skills.crystal.crystalUnlocked)
SetCooldownOf(crystalImage);
if (Input.GetKeyDown(KeyCode.Mouse1) && skills.sword.swordUnlocked)
SetCooldownOf(swordImage);
if (Input.GetKeyDown(KeyCode.R) && skills.blackhole.blackHoleUnlocked)
SetCooldownOf(blackholeImage);
if (Input.GetKeyDown(KeyCode.Alpha1) && Inventory.instance.GetEquipment(EquipmentType.Flask) != null)//必须获取药水
SetCooldownOf(flaskImage);
CheckCoolDownOf(dashImage, skills.dash.cooldown);
CheckCoolDownOf(parryImage, skills.parry.cooldown);
CheckCoolDownOf(crystalImage, skills.crystal.cooldown);
CheckCoolDownOf(swordImage, skills.sword.cooldown);
CheckCoolDownOf(blackholeImage, skills.blackhole.cooldown);
CheckCoolDownOf(flaskImage, Inventory.instance.flaskCoolDown);
}
private void UpdataSoulsUI()//更新灵魂值
{
if (soulsAmount < PlayerManager.instance.GetCurrency())
{
soulsAmount += increaseRate * Time.deltaTime;
}
else
soulsAmount = PlayerManager.instance.GetCurrency();
currentSouls.text = ((int)soulsAmount).ToString();
}
private void UpdateHealthUI()//更新血条值
{
slider.maxValue = playerStats.GetMaxHealthValue();
slider.value = playerStats.currentHealth;
}
private void SetCooldownOf(Image _image)//设置技能冷却时间动画
{
if (_image.fillAmount <= 0)
_image.fillAmount = 1;
}
private void CheckCoolDownOf(Image _image,float _coolDown)
{
if (_image.fillAmount > 0)
_image.fillAmount -= 1 / _coolDown * Time.deltaTime;//调整转的速度
}
}