Unity类银河战士恶魔城学习总结(P163 Game Pause暂停)

时间:2024-12-11 19:14:53

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

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

游戏界面切换到比如说菜单,装备栏时候游戏就会暂停,防止换装备的时候被打

红色的部分改成未缩放的时间,防止黑屏时候暂停

GameManager.cs

修改部分!!!

功能分析:
  • 这个函数控制的是游戏的暂停与恢复。
  • Time.timeScale 是 Unity 中控制时间流逝的属性:
    • Time.timeScale = 0 表示暂停游戏,即时间不再流动,所有物理和动画等都会暂停。
    • Time.timeScale = 1 恢复游戏的正常速度,即时间流动恢复正常。
作用:
  • pause为true: 游戏会被暂停,所有的时间相关操作(如物理引擎、动画等)会被停止,玩家无法进行任何互动。
  • pause为falsepause为: 游戏恢复正常,时间流动恢复,游戏中的物理、动画、输入等都会继续。
    public void PauseGame(bool _pause)//暂停游戏
    {
        if (_pause)
            Time.timeScale = 0;
        else
            Time.timeScale = 1;
    }
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));
    //public void LoadData(GameData _data)//2024.12.2
    //{
    //    LoadClosetCheckpoint(_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+ .3f;

        

        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(.25f);

        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;
    }

    public void PauseGame(bool _pause)//暂停游戏
    {
        if (_pause)
            Time.timeScale = 0;
        else
            Time.timeScale = 1;
    }
}

UI.cs

更新部分!!!

功能分析:
  • GameManager.instance 是单例模式的 GameManager 类的引用。GameManager 是用来管理游戏的核心类之一,它通常负责控制游戏的各种状态(如暂停、开始、游戏结束等)。
  • menu==inGameUI_menu 代表当前菜单界面,inGameUI 是游戏内界面的标识。这个判断用于区分当前显示的是游戏内界面还是其他菜单界面。
作用:
  • 如果当前显示的是 inGameUI(即游戏界面),那么调用 PauseGame(false),表示恢复游戏。
  • 如果当前显示的不是 inGameUI(即其他菜单界面),那么调用 PauseGame(true),表示暂停游戏。
        if (GameManager.instance != null)
        {
            if (_menu == inGameUI)
                GameManager.instance.PauseGame(false);
            else
                GameManager.instance.PauseGame(true);
        }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UI : MonoBehaviour,ISaveManager
{
    [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;

    [SerializeField] private UI_VolumeSlider[] volumeSettings;

    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)//传入的菜单不为空
        {
            AudioManager.instance.PlaySFX(7, null);
            _menu.SetActive(true);//显示
        }

        if (GameManager.instance != null)
        {
            if (_menu == inGameUI)
                GameManager.instance.PauseGame(false);
            else
                GameManager.instance.PauseGame(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++)//
        {
            //p152修复切换到其他UI时,InGameUI关闭的问题
            if (transform.GetChild(i).gameObject.activeSelf && transform.GetChild(i).GetComponent<UI_FadeScreen>() == null)//其他的UI全部关闭并且淡入淡出关闭
                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();

    public void LoadData(GameData _data)
    {
        foreach(KeyValuePair<string,float> pair in _data.volumeSettings)//遍历音量设置
        {
            foreach (UI_VolumeSlider item in volumeSettings)
            {
                if (item.parametr == pair.Key)//如果音量设置的参数和保存的参数一致
                    item.LoadSlider(pair.Value);
            }
        }
    }



    public void SaveData(ref GameData _data)
    {
        _data.volumeSettings.Clear();//清空音量设置

        foreach (UI_VolumeSlider item in volumeSettings)//遍历音量设置
        {
            _data.volumeSettings.Add(item.parametr,item.slider.value);//保存音量设置
        }
    }
}

 Player.cs

暂停后冻结游戏玩家的动作更新

        if (Time.timeScale == 0)
            return;
    protected override void Update()
    {

        if (Time.timeScale == 0)
            return;

        base.Update();

        stateMachine.currentState.Update();

        CheckForDashInput();


        if(Input.GetKeyDown(KeyCode.F)  && skill.crystal.crystalUnlocked)
            skill.crystal.CanUseSkill();//肯定要改到其他地方

        if (Input.GetKeyDown(KeyCode.Alpha1))
            Inventory.instance.UseFlask();

    }