1. 清晰的状态管理
状态机模式允许你以结构化的方式管理不同的UI状态。每个状态(比如主菜单、设置菜单、游戏中界面等)都有其独立的行为和属性,这使得管理复杂UI逻辑变得更加清晰和可维护。
2. 简化的状态切换
状态机模式可以简化不同UI状态之间的切换逻辑。使用状态机,可以很容易地定义状态之间的转换规则,并确保状态切换时的逻辑是正确的和一致的。
3. 分离关注点
通过将UI逻辑分割到不同的状态类中,可以使每个状态类只关心自己的行为和属性。这有助于减少代码的耦合,提高代码的可读性和可维护性。
4. 更容易的扩展和维护
当需要添加新的UI状态或修改现有状态的行为时,状态机模式使得这种修改变得更简单。可以通过添加或修改单个状态类来实现,而不必修改整个UI管理系统。
5. 动画和过渡效果
使用状态机模式,可以轻松地管理UI状态的进入和退出动画。例如,可以在状态进入时播放淡入动画,在状态退出时播放淡出动画。状态机模式可以确保这些动画在状态切换时正确播放。
6. 统一的状态处理逻辑
状态机模式提供了一种统一的方式来处理UI状态的更新、渲染和事件处理。这有助于保持代码的一致性,并避免不同状态处理逻辑的重复代码。
状态接口
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface IState//interface 状态机接口
{void Enter();void Exit();void LogicUpdata();void PhysicUpdata();void AinamtionEvent();
}
状态机管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//用于管理状态机切换
public class StateMachina : MonoBehaviour
{IState currentState;private void Update(){currentState?.LogicUpdata();}private void FixedUpdate(){currentState?.PhysicUpdata();}public virtual void AnimationEvent(){currentState?.AinamtionEvent();}public virtual void SwitchState(IState newState){currentState?.Exit();currentState=newState;currentState.Enter();}}
基础状态类
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
[RequireComponent(typeof(Canvas))]
[RequireComponent(typeof(CanvasGroup))]
[RequireComponent(typeof(RectTransform))]
public class UIState : MonoBehaviour,IState
{protected Canvas canvas;protected CanvasGroup canvasGroup;protected RectTransform rectTransform;//ui位置protected int initialSortingOrder;protected virtual void Awake(){canvas = GetComponent<Canvas>();canvasGroup = GetComponent<CanvasGroup>();rectTransform= GetComponent<RectTransform>();initialSortingOrder = canvas.sortingOrder;}public virtual void AinamtionEvent(){}public virtual void Enter(){canvas.enabled = true;}public virtual void Exit(){canvas.enabled = false;}public virtual void LogicUpdata(){}public virtual void PhysicUpdata(){}
}
具体的ui管理器负责ui的加载和切换状态,设置为单列模式,动态加载ui
using System.Collections.Generic;
using UnityEngine;public class UIManager : StateMachina
{public static UIManager Instance;string realPath = "Prefab/Panel/";private Dictionary<string, GameObject> prefabDict = new Dictionary<string, GameObject>();private IState currentState;private void Awake(){if (Instance == null){Instance = this;DontDestroyOnLoad(gameObject);SwitchPanel(My_UIConst.MainMenuPanel);}else{Destroy(gameObject);}}public GameObject CreatePanel(string name){if (prefabDict.ContainsKey(name)){return prefabDict[name];}GameObject panelPrefab = Resources.Load<GameObject>(realPath + name);if (panelPrefab == null){Debug.LogError($"Failed to load panel prefab: {realPath}{name}");return null;}GameObject panelObject = Instantiate(panelPrefab, gameObject.transform, false);prefabDict[name] = panelObject;return panelObject;}public void SwitchPanel(string name){UIState newState;if (prefabDict.ContainsKey(name)){newState = prefabDict[name].GetComponent<UIState>();}else{GameObject panelObject = CreatePanel(name);if (panelObject == null){Debug.LogError($"Failed to create panel: {name}");return;}newState = panelObject.GetComponent<UIState>();}SwitchState(newState);}
}
UIManager 挂载在canvas上
ui放在 Resources下面
ui的名称路径
public class My_UIConst
{public const string MainMenuPanel = "Menu/MainMenuPanel";public const string UserPanel = "Menu/UserPanel";public const string SettingsPanel = "Menu/SettingsPanel";// 你可以根据需要添加更多的 UI 名称
}
具体的UI状态类 用dotweet来实现动画效果
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class My_MainMenu : UIState
{public Button userButton;public Image bg;private void Start(){userButton.onClick.AddListener(() =>{Debug.Log("主菜单按钮点击 切换用户界面");UIManager.Instance.SwitchPanel(My_UIConst.UserPanel);});}public override void Enter(){Debug.Log("进入主菜单");DOTween.To(() => canvasGroup.alpha=0, x => canvasGroup.alpha=x, 1, 1);// 生成一个随机颜色Color randomColor = new Color(Random.value, Random.value, Random.value);bg.DOColor(randomColor, 1f); // 渐变到随机颜色base.Enter();}public override void Exit(){Color randomColor = new Color(Random.value, Random.value, Random.value);bg.DOColor(randomColor, 1f); // 渐变到随机颜色}}
using System.Collections;
using System.Collections.Generic;
using DG.Tweening; // 引入 DOTween 命名空间
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;public class My_UserPanel : UIState
{public Button mainMenuButton;private void Start(){mainMenuButton.onClick.AddListener(() =>{Debug.Log("用户按钮点击 切换主界面");UIManager.Instance.SwitchPanel(My_UIConst.SettingsPanel);});}public override void Enter(){Debug.Log("进入用户界面");// 设置初始位置canvas.transform.localPosition = new Vector3(-Screen.width, 0, 0);rectTransform.SetAsLastSibling();//将渲染等级移动到最后面 显示在最前面// 使用 DOTween 平移动画将面板移到屏幕中心canvas.transform.DOLocalMoveX(0, 1f).SetEase(Ease.OutQuad);base.Enter();}public override void Exit(){rectTransform.SetAsLastSibling();//将渲染等级移动到最后面 显示在最前面// 在退出时添加平移动画canvas.transform.DOLocalMoveX(-Screen.width, 1f).SetEase(Ease.OutQuad).OnComplete(() =>{base.Exit();});}
}
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class My_SettingPanel : UIState
{public Button mainMenuButton;private void Start(){mainMenuButton.onClick.AddListener(() =>{Debug.Log("用户按钮点击 切换主界面");UIManager.Instance.SwitchPanel(My_UIConst.MainMenuPanel);});}public override void Enter(){Debug.Log("进入用户界面");// 设置初始位置canvas.transform.localPosition = new Vector3(Screen.width, 0, 0);rectTransform.SetAsLastSibling();//将渲染等级移动到最后面 显示在最前面// 使用 DOTween 平移动画将面板移到屏幕中心canvas.transform.DOLocalMoveX(0, 1f).SetEase(Ease.OutQuad);base.Enter();}public override void Exit(){rectTransform.SetAsLastSibling();//将渲染等级移动到最后面 显示在最前面// 在退出时添加平移动画canvas.transform.DOLocalMoveX(-Screen.width, 1f).SetEase(Ease.OutQuad).OnComplete(() =>{base.Exit();});}}