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