Unity 中使用状态机模式来管理UI

news2024/9/24 3:24:27

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

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1921012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Javaweb11-Filter过滤器

Filter过滤器 1.Filter的基本概念&#xff1a; 在Java Servlet中&#xff0c;Filter接口是用来处理HttpServletRequest和HttpServletResponse的对象的过滤器。主要用途是在请求到达Servlet之前或者响应离开Servlet之前对请求或响应进行预处理或后处理。 2.Filter常见的API F…

Python使用策略模式和openpyxl库创建Excel文件并追加内容

from openpyxl import load_workbook# 数据数组 data [[1, 2, 3],[4, 5, 6],[7, 8, 9] ]# 打开现有的 Excel 文件 excel_file sheetApend_example.xlsx wb load_workbook(excel_file)# 选择要追加数据的工作表 sheet_name test_Sheet2 # 指定要追加数据的工作表名称 sheet…

【微服务】SpringCloud-eureka光速入门

SpringCloud-eureka光速入门 一、Eureka 主要组件 二、工作流程 三、优势 四、Eureka-光速入门【重点】 4.1 案例准备 4.1.1 创建父工程 tingyi-shop 4.1.2 创建子工程 tingyi-goods 4.1.3 创建子工程 tingyi-order 4.1.4 案例调整 4.1.4.1 在order模块创建 RestTemp…

安装和使用vue-router

1.安装vue-router npm install vue-router2.新建页面 views > home > home.vue <script setup lang"ts"></script><template><div>首页</div> </template><style scoped></style>2.配置路由 router > i…

从LLM中完全消除矩阵乘法,效果出奇得好,10亿参数跑在FPGA上接近大脑功耗

一直以来&#xff0c;矩阵乘法&#xff08;MatMul&#xff09;稳居神经网络操作的主导地位&#xff0c;其中很大原因归结为 GPU 专门针对 MatMul 操作进行了优化。这种优化使得 AlexNet 在 ILSVRC2012 挑战赛中一举胜出&#xff0c;成为深度学习崛起的历史性标志。 在这当中&a…

HBase 在统一内容平台业务的优化实践

作者&#xff1a;来自 vivo 互联网服务器团队-Leng Jianyu、Huang Haitao HBase是一款开源高可靠性、扩展性、高性能和灵活性的分布式非关系型数据库&#xff0c;本文围绕数据库选型以及使用HBase的痛点展开&#xff0c;从四个方面对HBase的使用进行优化&#xff0c;取得了一些…

PG 逻辑备份

导出模式&#xff1a; ph_dump 只能备份单个数据库&#xff0c;不会导出角色和表空间相关的信息&#xff0c;而且恢 复的时候需要创建空数据库。 pg_dumpall 可以备份所有数据库&#xff0c;并且备份角色&#xff0c;表空间。 调用pg_dump&#xff1a; 逻辑恢复的恢复顺序&…

国漫推荐07

玄幻、奇幻 1.侠岚系列 《侠岚》&#xff08;第1至6季&#xff09; 《画江湖之侠岚》&#xff08;侠岚第7季&#xff09; 2.《斗破苍穹》 三十年河东&#xff0c;三十年河西&#xff0c;莫欺少年穷&#xff01; 3.《武动乾坤》&#xff08;第1至4季&#xff09; 4.《妖神记》…

热点观察 丨《绝区零》下载量突破5000万、中国厂商占领全球手游收入榜

7月第2周.热点趋势 1. 《绝区零》全球下载量突破5000万 2. 《Character AI》爆火后内容管控变严 3. 芬兰手游厂商Supercell宣布开发新游戏 4. 6月全球手游收入榜中国厂商前十占六 5. 韩版《贪吃蛇大冒险》6月下载亮眼 6. 苹果、三星加入AI手机大战 7. 麦当劳推出首部短剧…

STM32智能仓储管理系统教程

目录 引言环境准备晶智能仓储管理系统基础代码实现&#xff1a;实现智能仓储管理系统 4.1 数据采集模块 4.2 数据处理与决策模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;仓储管理与优化问题解决方案与优化收尾与总结 1. 引言 智能仓储管理系统…

物流智能锁在物流货运智能锁控管理中的应用

一、物流锁控管理的痛点剖析 &#xff08;一&#xff09;货物安全风险高 在传统的物流运输中&#xff0c;常用的机械锁和普通电子锁安全性有限&#xff0c;容易被非法破解或撬开。据不完全统计&#xff0c;每年因货物被盗造成的经济损失高达数十亿。这导致货物在运输途中面临…

IDEA设置代码提示忽略大小写

一、设置代码提示为忽略大小写 IDEA代码提示默认是区分大小写的&#xff0c;设置为提示忽略大小写&#xff1a; Setting——Editor——Code Completion 如图

vue vite+three在线编辑模型导入导出

文章目录 序一、1.0.0版本1.新增2.编辑3.导出4.导入 二、2.0.0版本1. 修复模型垂直方向放置时 模型会重合4. 修复了导出导入功能 现在是1:1导出导入5. 新增一个地面 视角看不到地下 设置了禁止编辑地面 地面设置为圆形6. 新增功能 可选择基本圆形 方形 圆柱形等模型以及可放置自…

每天五分钟深度学习:向量化技术在神经网络中的应用

本文重点 向量化技术,简而言之,就是利用矩阵运算(而非传统的for循环)来执行大规模的计算任务。这种技术依赖于单指令多数据(SIMD)架构,允许一个指令同时对多个数据元素执行相同的操作。例如,在向量化加法中,不再需要逐个元素进行加法操作,而是可以一次性对整个向量执…

防御课第一次作业第一天笔记整理

网络安全概述 网络安全&#xff08;Cyber Security&#xff09;是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不因偶然的或者恶意的原因而遭受到破坏、更改、泄露&#xff0c;系统连续可靠正常地运行&#xff0c;网络服务不中断 中国网络安全市场近年来只增不…

【微信小程序知识点】自定义构建npm

在实际开发中&#xff0c;随着项目的功能越来越多&#xff0c;项目越来越复杂&#xff0c;文件目录也变得很繁琐&#xff0c;为了方便进行项目的开发&#xff0c;开发人员通常会对目录结构进行优化调整&#xff0c;例如&#xff1a;将小程序源码放到miniprogram目录下。 &…

探索最佳海外代理服务商!你知道哪些?

近期收到很多读者回复&#xff0c;咨询我有没有好用的海外代理&#xff0c;许多业务会用到海外代理&#xff0c;给大家整理了几个亲测好用的代理&#xff0c;如果有需要可以去试一试。 一、711Proxy 711Proxy的覆盖范围广&#xff0c;住宅IP质量高&#xff0c;基本上爬虫业务…

【测开能力提升-fastapi框架】fastapi路由分发

1.7 路由分发 apps/app01.py from fastapi import APIRouterapp01 APIRouter()app01.get("/food") async def shop_food():return {"shop": "food"}app01.get("/bed") async def shop_food():return {"shop": "bed&…

华贝甄选干细胞科技,揭秘生命修复的奥秘

在探索生命奥秘的漫漫征途中&#xff0c;华贝甄选凭借干细胞科技的神奇力量&#xff0c;为您点亮健康与活力的希望之光。 我们深知&#xff0c;细胞是生命的基石&#xff0c;而干细胞则是这基石中蕴含的无限潜能。华贝甄选精心打造的干细胞疗法&#xff0c;如同神奇的魔法&…

网络编程学习之tcp

按下*&#xff08;星号&#xff09;可以搜索当前光标下的单词。 Tcp编程的过程 打开网络设备 Bind&#xff1a;给服务地址把ip号和端口号连接进去 Tcp是有状态的 Listen是进入监听状态&#xff0c;看有没有客户端来连接服务器 Tcp比udp消耗过多资源 Upd类似于半双工&#…