Unity类银河战士恶魔城学习总结(P151 CheckPoint检查点)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili

教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/

本章节实现了类似于黑魂3,艾尔登法环中的传送点

火堆

死亡后传送到最近的火堆

警告!!!

本章节遇到了几个更加棘手的bug,脚本的加载顺序错误,各种实例化错误,以下是我的解决办法

1.三个管理器脚本执行顺序如以下,GameManager一定要比SaveManager提前

2.Checkpoint.cs中实例化直接放到Awake()中

3.GameManager.cs()中的实例化也放到Awake()中

如果后续因为这些修改遇到bug会及时回来更改的

Checkpoint.cs

ContextMenu 方法

  • ContextMenu 属性使得在Unity编辑器的右键菜单中为这个方法添加一个按钮。点击该按钮时会执行 GenerateId 方法。
  • GenerateId 方法使用 System.Guid.NewGuid().ToString() 生成一个新的唯一标识符,并将其赋值给 id 变量。
    • System.Guid.NewGuid() 生成一个新的全局唯一标识符(GUID),通过 ToString() 转换成字符串。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


//2024.11.28 19:38 疑似有点生病
//
public class Checkpoint : MonoBehaviour
{
    private Animator anim;
    public string id;
    public bool activationStatus;


    private void Awake()
    {
        anim = GetComponent<Animator>();
    }


    [ContextMenu("产生检查点ID")]//在编辑器中生成一个按钮
    private void GenerateId()
    {
        id = System.Guid.NewGuid().ToString();
    }


    private void OnTriggerEnter2D(Collider2D collision)//检测到碰撞
    {
        if (collision.GetComponent<Player>()!=null)//检测到玩家
        {
            ActivateCheckPoint();//激活检查点
        }
    }

    public void ActivateCheckPoint()//激活检查点
    {

        activationStatus = true;
        anim.SetBool("active", true);
    }
}


GameManager.cs

SaveData 方法

  • SaveData 方法用于保存当前游戏的数据。
  • 它首先通过 FindClosestCheckpoint() 方法获取最近激活的检查点,并将该检查点的 ID 存入 GameData 中。
  • 然后清空之前保存的检查点数据,并遍历场景中的所有检查点,将每个检查点的 ID 和激活状态保存到 _data.checkpoints 字典中。

LoadData 方法

  • LoadData 方法用于加载保存的数据。
  • 它遍历保存的数据中的 checkpoints 字典,其中包含每个检查点的 ID 和激活状态。
  • 然后与场景中的所有检查点进行对比,匹配 ID 后,如果该检查点应该被激活,就调用 ActivateCheckPoint() 激活该检查点。
  • 设置 closestCheckpointId 为加载数据中的最近检查点 ID,并使用 Invoke 延迟调用 PlacePlayerAtClosestCheckpoint(),将玩家放置到最近激活的检查点。

RestartScene 方法

  • RestartScene 方法用来重启当前场景。
  • 首先调用 SaveManager.instance.SaveGame() 保存当前游戏的进度。
  • 然后使用 SceneManager.GetActiveScene() 获取当前场景,并通过 SceneManager.LoadScene() 重新加载该场景,达到重启场景的效果。

FindClosestCheckpoint 方法

  • FindClosestCheckpoint 方法用于查找玩家当前位置最近的已激活检查点。
  • 它通过 Vector2.Distance 计算玩家和每个检查点之间的距离,如果当前检查点的距离比已找到的最近距离小,并且该检查点已激活,就更新最近检查点。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;


//2024.11.28 17:04
public class GameManager : MonoBehaviour, ISaveManager
{
    public static GameManager instance;//单例模式,全局变量
    [SerializeField] private Checkpoint[] checkpoints;
    [SerializeField] private string closestCheckpointId;

    private void Awake()
    {
        if (instance != null)
            Destroy(instance.gameObject);
        else
            instance = this;

        checkpoints = FindObjectsOfType<Checkpoint>();//查找所有的检查点
    }


    private void Start()
    {
        
    }


    public void RestartScene()
    {
        SaveManager.instance.SaveGame();//保存游戏
        Scene scene = SceneManager.GetActiveScene();
        SceneManager.LoadScene(scene.name);
    }



    public void LoadData(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();

            }
        }


        closestCheckpointId = _data.closestCheckpointId; ;//将最近检查点ID存入变量

        Invoke("PlacePlayerAtClosestCheckpoint", 0.1f);//延迟调用PlacePlayerAtClosetCheckpoint方法
    }


    private void PlacePlayerAtClosestCheckpoint()
    {
        foreach (Checkpoint checkpoint in checkpoints)
        {
            if (closestCheckpointId == checkpoint.id)
                PlayerManager.instance.player.transform.position = checkpoint.transform.position;
        }
    }



    public void SaveData(ref GameData _data)
    {
        _data.closestCheckpointId = FindClosestCheckpoint().id;//将最近的检查点ID存入数据
        _data.checkpoints.Clear();

        foreach (Checkpoint checkpoint in checkpoints)
        {
            _data.checkpoints.Add(checkpoint.id, checkpoint.activationStatus);//将检查点的ID和激活状态存入数据
        }
    }



    private Checkpoint FindClosestCheckpoint()//找到最近的检查点
    {
        float closestDistance = Mathf.Infinity;//正无穷
        Checkpoint closestCheckpoint = null;

        foreach (var checkpoint in checkpoints)//遍历所有的检查点
        {
            float distanceToCheckpoint = Vector2.Distance(PlayerManager.instance.player.transform.position, checkpoint.transform.position);//计算玩家和检查点之间的距离

            if (distanceToCheckpoint < closestDistance && checkpoint.activationStatus == true)//如果距离小于最近距离且检查点激活
            {
                closestDistance = distanceToCheckpoint;//更新最近距离
                closestCheckpoint = checkpoint;//更新最近检查点
            }

        }
        return closestCheckpoint;
    }
}

GameData.cs

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 GameData()
    {
        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>();   
    }
}

UI.cs

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class UI : MonoBehaviour
{
    [Header("End screens")]
    [SerializeField] private UI_FadeScreen fadeScreen;
    [SerializeField] private GameObject endText;
    [SerializeField] private GameObject restartButton;
    [Space]

    [SerializeField] private GameObject characterUI;
    [SerializeField] private GameObject skillTreeUI;
    [SerializeField] private GameObject craftUI;
    [SerializeField] private GameObject optionsUI;
    [SerializeField] private GameObject inGameUI;

    //物品提示框和状态提示框
    public UI_SkillToolTip skillToolTip;
    public UI_ItemTooltip itemToolTip;
    public UI_StatToolTip statToolTip;
    public UI_CraftWindow craftWindow;

    private void Awake()
    {
        SwitchTo(skillTreeUI);//2024年11月22日,P138 Skill Tree Hot Fix,启动时默认显示技能树界面
        fadeScreen.gameObject.SetActive(true);
    }


    void Start()
    {
        SwitchTo(inGameUI);

        itemToolTip.gameObject.SetActive(false);//戏启动时隐藏物品提示框和状态提示框
        statToolTip.gameObject.SetActive(false);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.C))
            SwitchWithKeyTo(characterUI);

        if (Input.GetKeyDown(KeyCode.B))
            SwitchWithKeyTo(craftUI);

        if (Input.GetKeyDown(KeyCode.K))
            SwitchWithKeyTo(skillTreeUI);

        if (Input.GetKeyDown(KeyCode.O))
            SwitchWithKeyTo(optionsUI);

    }

    public void SwitchTo(GameObject _menu)// 该方法用于切换到指定的UI界面
    {

        for (int i = 0; i < transform.childCount; i++)//遍历当前UI对象的所有子物体
        {
            bool fadeScreen = transform.GetChild(i).GetComponent<UI_FadeScreen>() != null;//检查UI界面是否有FadeScreens

            if (fadeScreen==false)
                transform.GetChild(i).gameObject.SetActive(false);//遍历并隐藏所有子元素,确保了在显示新的UI界面时,所有其他的UI界面都会被隐藏
               
        }


        if (_menu != null)//传入的菜单不为空
        {
            _menu.SetActive(true);//显示
        }
    }


    public void SwitchWithKeyTo(GameObject _menu)//处理切换UI的逻辑
    {
        if (_menu != null && _menu.activeSelf)// UI界面已经显示,隐藏, 如果目标UI界面未显示,调用 SwitchTo 显示。
        {
            _menu.SetActive(false);
            CheckForInGameUI();
            return;
        }

        SwitchTo(_menu);
    }


    private void CheckForInGameUI()//关闭其他UI都会回到InGameUI
    {
        for (int i = 0; i < transform.childCount; i++)
        {
            if (transform.GetChild(i).gameObject.activeSelf)//检查当前 UI 对象的第 i 个子对象是否处于激活状态。
                return;
        }

        SwitchTo(inGameUI);
    }

    public void SwitchOnEndScreen()
    {

        fadeScreen.FadeOut();
        StartCoroutine(EndScreenCorutione());

    }

    IEnumerator EndScreenCorutione()
    {
        yield return new WaitForSeconds(1);
        endText.SetActive(true);
        yield return new WaitForSeconds(1.7f);
        restartButton.SetActive(true);
    }

    public void RestartGameButton() => GameManager.instance.RestartScene(); 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值