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