赞
踩
窗口管理
初始化
在场景中挂载一个脚本,用于创建初始界面
- using UnityEngine;
-
- //创建开始界面
- public class CreateStartPanel : MonoBehaviour
- {
- //面板顺序管理器
- PanelStack panelStack;
-
- void Awake()
- {
- panelStack = new PanelStack();
- }
-
- void Start()
- {
- //打开初始面板
- panelStack.Push(new StartPanel());
- }
- }
初始界面
初始界面面板的控制脚本,不用挂载到面板的预制体上
生成面板的时候,直接new一个该类的实例即可
初始界面有一个Open按钮,在预制体中是没有添加点击事件
在面板打开之后,动态地添加点击事件,按下可以打开一个子窗口
- using UnityEngine;
- using UnityEngine.UI;
-
- //开始面板
- public class StartPanel : BasePanel
- {
- //面板存储在Resource/Prefabs目录下:路径从Prefabs开始,需要包含文件名,不用加文件后缀
- static readonly string path="Prefabs/StartPanel";
-
- public StartPanel():base(new PanelType(path)){ Debug.Log("派生类构造函数"); }
-
- //重写进入函数
- public override void OnEnter()
- {
- //给指定的按钮添加点击事件
- panelExtensionTool.GetOrAddComponentInChildren<Button>("OpenAnotherPanelBtn").onClick.AddListener(()=>
- {
- //打开子窗口
- PanelStack.Push(new ChildrenPanel());
- }
- );
-
- panelExtensionTool.GetOrAddComponentInChildren<Button>("MainSceneBtn").onClick.AddListener(() =>
- {
- //使用单例切换场景
- GameManager.instance.sceneSystem.SetScene(new MainScene());
- //弹出场景中的所有面板
- PanelStack.PopAll();
- }
- );
- }
-
- //重写退出函数
- public override void OnExit()
- {
- //关闭面板
- PanelManager.DestroyPanel(this.PanelType);
- }
-
- //暂停:使窗口不能交互
- public override void OnPause()
- {
- //也可以写在基类中
- panelExtensionTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = false;
- }
-
- //恢复:使窗口可以交互
- public override void OnResume()
- {
- panelExtensionTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = true;
- }
- }
子窗口
子界面有一个Close按钮,在预制体中是没有添加点击事件
在面板打开之后,动态地添加点击事件,按下可以关闭这个子窗口
- using UnityEngine;
- using UnityEngine.UI;
-
- //子面板
- public class ChildrenPanel: BasePanel
- {
- static readonly string path = "Prefabs/ChildrenPanel";
-
- public ChildrenPanel() : base(new PanelType(path)) { }
-
- public override void OnEnter()
- {
- //给指定的按钮添加点击事件
- panelExtensionTool.GetOrAddComponentInChildren<Button>("Close").onClick.AddListener(() =>
- {
- //关闭面板
- PanelStack.Pop();
- }
- );
- }
-
- public override void OnExit()
- {
- PanelManager.DestroyPanel(this.PanelType);
- }
-
- public override void OnPause()
- {
- panelExtensionTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = false;
- }
-
- public override void OnResume()
- {
- panelExtensionTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = true;
- }
- }
面板类型信息
面板的类型信息,用于区分不同类型的面板
- /// <summary>
- /// 面板的类型信息
- /// </summary>
- public class PanelType
- {
- /// <summary>
- /// 面板的名称
- /// </summary>
- public string Name;
-
- /// <summary>
- /// 面板的存储路径
- /// </summary>
- public string Path;
-
- public PanelType(string path)
- {
- Path = path;
- //截取斜杠后的全部字符
- Name = path.Substring(path.LastIndexOf('/') + 1);
- }
- }
基础面板
每一个面板都需要基础这个基础面板类
基础面板类保存面板的类型信息
对于面板有四个基本状态,分别是打开、关闭、暂停、恢复
在状态发生转换时,需要执行对应的函数,完成相应的操作
这四个事件函数在基类中被声明为虚函数,需要继承的派生类将它们重写、实现
此外,基础面板还保存了面板扩展工具、面板栈、面板管理器的引用及其属性,以及相应的初始化函数,这样就可以方便地对面板进行管理
- using UnityEngine;
-
- /// <summary>
- /// 基础面板(基类):包含面板的状态信息,以及在派生类中要重写的函数
- /// </summary>
- public class BasePanel
- {
- /// <summary>
- /// 面板类型信息:外部只读,内部可写
- /// </summary>
- /// <value></value>
- public PanelType PanelType{get;private set;}
-
- /// <summary>
- /// 基础面板的构造函数
- /// </summary>
- /// <param name="panelType"></param>
- public BasePanel(PanelType panelType)
- {
- PanelType=panelType;
- Debug.Log("基类构造函数");
- }
-
- /// <summary>
- /// 打开面板时执行
- /// </summary>
- public virtual void OnEnter(){ }
-
- /// <summary>
- /// 停用面板时执行
- /// </summary>
- public virtual void OnPause(){ }
-
- /// <summary>
- /// 恢复面板时执行
- /// </summary>
- public virtual void OnResume(){ }
-
- /// <summary>
- /// 关闭面板时执行
- /// </summary>
- public virtual void OnExit(){ }
-
- //面板扩展工具
- public PanelExtensionTool panelExtensionTool {get;private set;}
-
- //初始化面板扩展工具
- public void Initialize(PanelExtensionTool tool)
- {
- panelExtensionTool = tool;
- }
-
- //面板之栈
- public PanelStack PanelStack { get;private set;}
-
- public void Initialize(PanelStack panelStack)
- {
- PanelStack = panelStack;
- }
-
- //面板管理器
- public PanelManager PanelManager { get;private set;}
-
- public void Initialize(PanelManager panelManager)
- {
- PanelManager = panelManager;
- }
-
- }
面板扩展工具
用于获取面板及其子对象上的组件,也可以为其添加组件
- using UnityEngine;
-
- /// <summary>
- /// 面板扩展工具:给当前活动面板或其子对象添加组件
- /// </summary>
- public class PanelExtensionTool
- {
- /// <summary>
- /// 顶层的活动面板
- /// </summary>
- GameObject topPanel;
-
- //构造函数
- public PanelExtensionTool(GameObject panel)
- {
- topPanel = panel;
- }
-
- /// <summary>
- /// 给当前活动面板获取或者添加一个组件
- /// </summary>
- /// <typeparam name="T">组件类型</typeparam>
- /// <returns>组件</returns>
- public T GetOrAddComponent<T>() where T:UnityEngine.Component
- {
- if(topPanel.GetComponent<T>()==null)
- topPanel.AddComponent<T>();
- return topPanel.GetComponent<T>();
- }
-
- /// <summary>
- /// 根据名称,获取指定子对象上的组件,如果没有,则为其添加
- /// </summary>
- /// <param name="name">子对象的名称</param>
- /// <typeparam name="T">组件类型</typeparam>
- /// <returns>组件</returns>
- public T GetOrAddComponentInChildren<T>(string name) where T:UnityEngine.Component
- {
- GameObject child = FindChildGameObject(name);
- if(child)
- {
- if(child.GetComponent<T>()==null)
- child.AddComponent<T>();
- return child.GetComponent<T>();
- }
- return null;
- }
-
- /// <summary>
- /// 根据名称查找顶层面板上的子对象:比如按钮
- /// </summary>
- /// <param name="name">子对象名称</param>
- /// <returns></returns>
- public GameObject FindChildGameObject(string name)
- {
- //存储顶层面板的子对象的全部组件的Transform属性
- Transform[] trans = topPanel.GetComponentsInChildren<Transform>();
-
- foreach (Transform item in trans)
- {
- //在transform属性数组中,找到指定名称的子对象
- if (item.name == name)
- {
- return item.gameObject;
- }
- }
-
- Debug.Log("找不到子对象");
- return null;
- }
- }
面板之栈
用于管理面板的响应顺序
打开一个面板,入栈,其父面板需要暂停响应
关闭一个面板,出栈,其父面板需要恢复响应
栈顶的面板可以交互,其余面板需要处于暂停响应的状态
- using System.Collections.Generic;
- using UnityEngine;
-
- /// <summary>
- /// 面板之栈:用于管理面板的响应顺序
- /// </summary>
- public class PanelStack
- {
- /// <summary>
- /// 存储面板的栈
- /// </summary>
- private Stack<BasePanel> stack;
-
- /// <summary>
- /// 面板管理器
- /// </summary>
- private PanelManager panelManager;
-
- //面板基类引用
- private BasePanel panel;
-
- /// <summary>
- /// 初始化面板之栈
- /// </summary>
- public PanelStack()
- {
- stack = new Stack<BasePanel>();
- panelManager = new PanelManager();
- }
-
- /// <summary>
- /// 打开面板时将面板入栈
- /// </summary>
- /// <param name="nextPanel"></param>
- public void Push(BasePanel nextPanel)
- {
- //如果还有面板在显示
- if(stack.Count>0)
- {
- //取栈顶
- panel = stack.Peek();
- //暂停上一个面板
- panel.OnPause();
- }
- //新面板入栈
- stack.Push(nextPanel);
- //获取一个面板
- GameObject panelToShow = panelManager.ShowPanel(nextPanel.PanelType);
-
- //初始化面板的工具
- nextPanel.Initialize(new PanelExtensionTool(panelToShow));
- //初始化面板的面板顺序管理器
- nextPanel.Initialize(this);
- //初始化面板管理器
- nextPanel.Initialize(panelManager);
- //面板进入时要执行的任务
- nextPanel.OnEnter();
- }
-
- /// <summary>
- /// 关闭面板时将面板出栈:弹出栈顶的面板,再恢复新栈顶的面板
- /// </summary>
- public void Pop()
- {
- if(stack.Count>0)
- {
- stack.Pop().OnExit();
-
- //栈顶的面板退出
- //stack.Peek().OnExit();
- //面板出栈
- //stack.Pop();
- }
- //恢复下层面板
- if(stack.Count>0)
- stack.Peek().OnResume();
- }
-
- /// <summary>
- /// 关闭所有面板并执行其退出函数
- /// </summary>
- public void PopAll()
- {
- while (stack.Count > 0)
- stack.Pop().OnExit();
- }
- }
面板管理器
用于创建或销毁面板,并存储打开的面板的信息
- using System.Collections.Generic;
- using UnityEngine;
-
- /// <summary>
- /// 面板管理器:创建或销毁面板,并存储打开的面板的信息
- /// </summary>
- public class PanelManager
- {
- /// <summary>
- /// 用字典存储所有打开的面板,每一个面板类对应一个面板
- /// </summary>
- private Dictionary<PanelType,GameObject> panelDict;
-
- /// <summary>
- /// 构造函数:初始化字典
- /// </summary>
- public PanelManager()
- {
- panelDict = new Dictionary<PanelType, GameObject>();
- }
-
- /// <summary>
- /// 显示一个面板
- /// </summary>
- /// <param name="type">UI类型</param>
- /// <returns></returns>
- public GameObject ShowPanel(PanelType type)
- {
- //将画布指定为面板的父对象
- GameObject parent=GameObject.Find("Canvas");
- if(!parent)
- {
- UnityEngine.Debug.Log("Error:画布不存在");
- return null;
- }
- //如果字典中有指定的面板的信息,则返回这个面板的对象
- if(panelDict.ContainsKey(type))
- return panelDict[type];
- //将指定的面板克隆到画布上
- GameObject panel = GameObject.Instantiate(Resources.Load<GameObject>(type.Path),parent.transform);
- //设定面板对象的名字
- panel.name=type.Name;
- //加入字典
- panelDict.Add(type,panel);
- return panel;
- }
-
- /// <summary>
- /// 关闭一个面板
- /// </summary>
- public void DestroyPanel(PanelType type)
- {
- if(panelDict.ContainsKey(type))
- {
- //销毁指定的面板
- Object.Destroy(panelDict[type]);
- //从字典移除该面板的键值对
- panelDict.Remove(type);
- }
- }
- }
场景管理
游戏管理器
用于加载初始场景
保存场景管理系统的引用,通过提供单例,让其他脚本可以获取到场景管理系统
- using UnityEngine;
-
- public class GameManager : MonoBehaviour
- {
- //单例模式:外部可以通过这个实例,调用场景管理系统内的函数
- public static GameManager instance{get;private set;}
-
- //场景管理系统
- public SceneSystem sceneSystem{get;private set;}
-
- private void Awake()
- {
- //如果单例还没有实例化,则赋值。如果已经实例化,则销毁自身。
- if (instance == null)
- instance = this;
- else
- Destroy(gameObject);
-
- sceneSystem = new SceneSystem();
-
- //将单例放入DontDestroyOnLoad,确保切换场景之后不会丢失,可以继续使用
- DontDestroyOnLoad(gameObject);
- }
-
- private void Start()
- {
- //打开初始场景
- sceneSystem.SetScene(new StartScene());
- }
- }
场景基类
将场景基类写为抽象类,用于给具体的场景继承,必须实现它的两个虚函数
- public abstract class SceneBase
- {
- //场景进入时要执行的操作
- public abstract void OnEnter();
-
- //场景退出时要执行的操作
- public abstract void OnExit();
- }
场景管理系统
用于加载和退出场景
- //场景的状态管理系统
- public class SceneSystem
- {
- //场景基类
- SceneBase sceneBase;
-
- //设置场景:退出前一个场景,加载后一个场景
- public void SetScene(SceneBase scene)
- {
- sceneBase?.OnExit();
- sceneBase = scene;
- sceneBase?.OnEnter();
- }
- }
初始场景
场景继承场景基类,并实现其中状态转换时需要执行的两个函数:进入、退出
如果进入时,当前场景已加载,则显示要展示的面板
如果进入时没有加载场景,则加载场景
使用事件函数(委托)对场景的加载进行监听
事件函数SceneLoad的第一个参数Scene是场景,第二个参数是加载模式
无需手动为其赋值,只需要将事件函数绑定到SceneManager.sceneLoaded即可
SceneManager.sceneLoaded += SceneLoaded;
如果监听到场景加载,Scene变化吗,则执行SceneLoaded函数,用于执行加载面板等操作
- using UnityEngine.SceneManagement;
-
- public class StartScene:SceneBase
- {
- //场景名
- readonly string sceneName="StartScene";
-
- //场景顺序管理器
- PanelStack panelStack;
-
- public override void OnEnter()
- {
- panelStack = new PanelStack();
- //当前的活动场景不是指定的场景,就加载指定的场景
- if(SceneManager.GetActiveScene().name!=sceneName)
- {
- SceneManager.LoadScene(sceneName);
- SceneManager.sceneLoaded+=SceneLoaded;
- }
- //如果是指定的场景,那么就显示要显示的面板
- else panelStack.Push(new StartPanel());
- }
-
- public override void OnExit()
- {
- //如果不取消绑定,该函数将被重复绑定,监听到场景加载时,会被错误地重复执行多次
- SceneManager.sceneLoaded-=SceneLoaded;
- }
-
- /// <summary>
- /// 场景加载完毕后执行的方法
- /// </summary>
- /// <param name="scene"></param>
- /// <param name="load"></param>
- private void SceneLoaded(Scene scene,LoadSceneMode load)
- {
- // 显示初始面板
- panelStack.Push(new StartPanel());
- }
- }
主场景
与初始场景类似
- using UnityEngine.SceneManagement;
-
- public class MainScene : SceneBase
- {
- //场景名
- readonly string sceneName="MainScene";
-
- //面板栈
- PanelStack panelStack;
-
- public override void OnEnter()
- {
- panelStack = new PanelStack();
-
- if(SceneManager.GetActiveScene().name!=sceneName)
- {
- SceneManager.LoadScene(sceneName);
- SceneManager.sceneLoaded += SceneLoaded;
- }
- else panelStack.Push(new MainPanel());
- }
-
- public override void OnExit()
- {
- SceneManager.sceneLoaded -= SceneLoaded;
- }
-
- void SceneLoaded(Scene scene,LoadSceneMode load)
- {
- panelStack.Push(new MainPanel());
- }
- }
主场景中的面板
仅仅用于测试能否从主场景返回初始场景
- using UnityEngine;
- using UnityEngine.UI;
-
- //主面板
- public class MainPanel : BasePanel
- {
- static readonly string path = "Prefabs/MainPanel";
-
- public MainPanel() : base(new PanelType(path)){}
-
- public override void OnEnter()
- {
- panelExtensionTool.GetOrAddComponentInChildren<Button>("Exit").onClick.AddListener(() =>
- {
- //返回初始场景
- GameManager.instance.sceneSystem.SetScene(new StartScene());
- PanelStack.PopAll();
- }
- );
- }
-
- public override void OnExit()
- {
- PanelManager.DestroyPanel(this.PanelType);
- }
-
- public override void OnPause()
- {
- panelExtensionTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = false;
- }
-
- public override void OnResume()
- {
- panelExtensionTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = true;
- }
- }
效果
运行前
开始场景
打开子窗口
此时无法点击主面板的按钮
关闭子窗口
主面板上的按钮又可以点击了
跳转到另一个场景
回到初始场景
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。