知识介绍:
c# 属性: public int Id { get; private set; }
说明Id这个属性在其所在的类外被调用时,只能获取它的值而不能设置它的值
用来存放预制体中该UI的名字以及路径,基础面板含有这个对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIType
{
public string Name { get; private set; }
///
/// UI路径
///
public string Path { get; private set; }
public UIType(string path)
{
Path = path;
Name = path.Substring(path.LastIndexOf('/') + 1);
}
}
它是所有面板类的基类,当需要新的面板时,首先需要继承该类。
它需要一个UIType,记录它需要的预制体的路径及名字
然后,对于一个面板来说,存在四种状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class BasePanel
{
public UIType UIType { get; private set; }
public BasePanel(UIType uIType)
{
UIType = uIType;
}
public virtual void OnEnter() { }
public virtual void OnPause() { }
///
/// UI继续时的操作
///
public virtual void OnResume() { }
public virtual void OnExit() { }
}
例如当从开始界面调出设置界面的时候,此时无法再对开始界面的按钮进行操作,因此此时也就是OnPause状态
每一个面板下面可能有很多个UI属性,因此每个面板都需要一个UI管理器,它有获取面板下的UI,创建UI,或者销毁UI的功能。
首先需要掌握Instantiate函数的用法,因为我们接下来需要生成预制体。
Instantiate函数是unity3d中进行实例化的函数,也就是对一个对象进行复制操作的函数,这个函数共有五个重载(overloaded)函数,对这五个函数的理解不清楚的话产生的效果也不相同,现在对这五个函数做一定的理解。
先附上unity3d API 中对这个函数的描述:
Instantiate函数实例化是将original对象的所有子物体和子组件完全复制,成为一个新的对象。这个新的对象拥有与源对象完全一样的东西,包括坐标值等。
original:用来做复制操作的对像物体,源对象
position:实例化的对象的位置坐标
rotation:实例化的对象的旋转坐标(旋转四元数)
parent:实例化对象的父对象,就是给这个实例化对象找的爹,在完成实例化函数处理后,实例化对象将在父对象下,是父对象的子对象
instantiateWorldSpace(老的叫WorldSpaceStays):这个值为TRUE,表示实例化对象相对于世界坐标系的位置(是位置,不是坐标值,比如实例化前在Canvas左上角,实例化后还在左上角)不变,相对于父对象的坐标值变了。为false,表示实例化对象相对于父对象的坐标值不变,但在世界坐标系中的位置变了。
这两个函数的区别是instantiateWorldSpace等于true/false的区别。
第一个函数相当于instantiateWorldSpace等于false。效果:实例化对象将放在父对象下,完全保存了源对象的属性,其位置坐标值是相对于父对象的,值不变,在世界坐标系下位置发生变化。
第二个函数相当于instantiateWorldSpace等于TRUE。效果:实例化对象将放在父对象下,完全保存了源对象的属性,其位置坐标值是相对于父对象的,值改变,在世界坐标系下位置不发生变化。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 存储所有UI信息,并可以创建或者销毁UI
///
public class UIManager
{
// Start is called before the first frame update
///
/// 存储所有UI信息的字典,每一个UI信息都会对应一个GameObject
///
private Dictionary<UIType, GameObject> dicUI;
public UIManager()
{
dicUI = new Dictionary<UIType, GameObject>();
}
///
/// 获取一个UI对象
///
/// UI信息
///
public GameObject GetSingleUI(UIType type)
{
GameObject parent = GameObject.Find("Canvas");
if (!parent)
{
Debug.LogError("Canvas不存在,请仔细查找有无这个对象");
return null;
}
if (dicUI.ContainsKey(type)) return dicUI[type];
GameObject ui = GameObject.Instantiate(Resources.Load<GameObject>(type.Path), parent.transform);//此处是在Canvas下生成预制体,函数使用方法见上面解析
ui.name = type.Name;
return ui;
}
///
/// 销毁一个UI对象
///
/// UI信息
public void DestroyUI(UIType type)
{
if (dicUI.ContainsKey(type))
{
DestroyUI(type);
dicUI.Remove(type);
}
}
}
对于一个面板来说,我们可能会需要通过点击这个面板下的按钮然后开启新的面板,当我们开启一个新的面板时,我们可能无法操作原来的那个面板,因此此时就需要让原来的那个面板执行暂停功能。
而当我们连续开启多个面板时,然后再一个个关闭,为了保证顺序,那么此时需要用到栈的结构。
当新的面板出现时,需要暂停原来的面板。
关闭面板时,需要执行该面板的Exit工作,然后将其Pop出来,当Pop面板后,如果当前还有面板,我们需要将该面板由暂停功能恢复到正常状态的功能,因此执行OnResume函数。
PanelManager还有一个功能就是调用UIManager来生成预制体,采用这个函数:
uiManager.GetSingleUI(nextPanel.UIType);每次Push的时候都要调用这个函数,这样才能生成预制体。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 面板管理器 用栈来存储UI
///
public class PanelManager
{
///
/// 存储UI面板的栈
///
private Stack<BasePanel> stackPanel;
///
/// UI管理器
///
private UIManager uiManager;
private BasePanel panel;
public PanelManager()
{
stackPanel = new Stack<BasePanel>();
uiManager = new UIManager();
}
///
/// UI的入栈操作,此操作会显示一个面板
///
/// 要显示的面板
public void Push(BasePanel nextPanel)
{
if (stackPanel.Count > 0)//如果当前没有面板,则不需要执行暂停面板的操作
{
panel = stackPanel.Peek();//获取栈顶
panel.OnPause();
}
stackPanel.Push(nextPanel);
GameObject panelGo = uiManager.GetSingleUI(nextPanel.UIType);
}
///
/// 执行面板的出栈操作,此操作会执行面版的OnExit方法
///
public void Pop()
{
if (stackPanel.Count > 0)
{
stackPanel.Peek().OnExit();
stackPanel.Pop();
}
if (stackPanel.Count > 0) stackPanel.Peek().OnResume();
}
}
接下来可以创建实例面板的脚本了。
StartPanel:
它需要带有预制体的路径,然后用路径创建一个UIType,然后用UIType来作为基类构造函数的参数。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 开始主面板
///
public class StartPanel :BasePanel
{
static readonly string path = "Prefabs/UI/Panel/StartPanel";
public StartPanel():base(new UIType(path)) { }//用预制体的路径创建一个UIType,并且将这个UIType作为参数,然后去调用基类的构造函数。
}
有了上面那个脚本之后,我们只需要在
StartManager:
创建一个StartManager脚本,并且生成上面的那个StartPanel,并将其Push放入PanelManager即可产生StartPanel面板
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StartManager : MonoBehaviour
{
PanelManager panelManager;
// Start is called before the first frame update
private void Awake()
{
panelManager = new PanelManager();
}
void Start()
{
panelManager.Push(new StartPanel());
}
// Update is called once per frame
}
接下来实现场景间的跳转
首先书写一个SceneState,作为抽象基类,包含有进入场景和退出场景操作的方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class SceneState
{
///
/// 场景进入时执行的操作
///
public abstract void OnEnter();
///
/// 场景退出时执行的操作
///
public abstract void OnExit();
}
接下来书写一个开始场景,继承初始场景
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartScene :SceneState
{
///
/// 场景名称
///
readonly string sceneName = "Start";
PanelManager panelManager;
public override void OnEnter()
{
panelManager = new PanelManager();
if (SceneManager.GetActiveScene().name != sceneName)
{
SceneManager.LoadScene(sceneName);
SceneManager.sceneLoaded += SceneLoaded;//添加方法的绑定
}
else
{
panelManager.Push(new StartPanel());
}
}
public override void OnExit()
{
SceneManager.sceneLoaded -= SceneLoaded;
}
///
/// 场景加载完毕后执行的方法
///
///
///
private void SceneLoaded(Scene scene, LoadSceneMode load)
{
panelManager.Push(new StartPanel());
Debug.Log($"{sceneName}场景加载完毕");
}
}
事实上,在阅读上面的代码的时候,你可能会产生了这样的问题,这是委托的用法
C#委托可以查看我写的另外一篇文章
然后再仿照上面,书写一个主场景MainScene
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainScene :SceneState
{
// Start is called before the first frame update
///
/// 场景名称
///
readonly string sceneName = "Main";
PanelManager panelManager;
public override void OnEnter()
{
panelManager = new PanelManager();
if (SceneManager.GetActiveScene().name != sceneName)
{
SceneManager.LoadScene(sceneName);
SceneManager.sceneLoaded += SceneLoaded;//将函数委托于sceneLoaded上。Unity的机制中,当加载场景时,会自动调用SceneLoaded。而我们只需要将函数委托于其上,就可以在加载场景时调用我们委托与其上的函数了。
}
else
{
panelManager.Push(new StartPanel());
}
}
public override void OnExit()
{
SceneManager.sceneLoaded -= SceneLoaded;
}
///
/// 场景加载完毕后执行的方法
///
///
///
private void SceneLoaded(Scene scene, LoadSceneMode load)
{
Debug.Log($"{sceneName}场景加载完毕");
}
}
再书写一个场景的状态管理系统,其实它只有一个函数,就是设置当前场景为所需要的场景。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 场景的状态管理系统
///
public class SceneSystem
{
///
/// 场景状态类
///
SceneState sceneState;
///
/// 设置当前场景并进入当前场景
///
///
public void SetScene(SceneState state)
{
if (sceneState != null) sceneState.OnExit();
sceneState = state;
if (sceneState != null) sceneState.OnEnter();
//sceneState?.OnExit();//这段书写方式和上面那段代码是相同的意义
//sceneState = state;
//sceneState?.OnEnter();
}
}
再创建一个GameRoot ,然后在unity中用空物体挂接这个脚本,它用来生成场景
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
///管理全局的一些东西
///
public class GameRoot : MonoBehaviour
{
public static GameRoot Instance { get; private set; }
public SceneSystem SceneSystem { get; private set; }
private void Awake()
{
Instance = this;
SceneSystem = new SceneSystem();
}
private void Start()
{
SceneSystem.SetScene(new StartScene());
}
}
然后在StartPanel中添加Start的按键:
并且在StartPanel的面板中添加跳转的按钮,然后想要按下按钮时跳转,写下以下脚本:
此时,点击按钮便可以成功从Start场景跳转至Main场景了
但是我们注意到,当进入Main场景时,并没有面板被推出,于是为此,首先需要写一个MainPanel的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
///
/// Main场景的主面板
///
public class MainPanel : BasePanel
{
static readonly string path = "Prefabs/UI/Panel/MainPanel";
public MainPanel() : base(new UIType(path)) { }
public override void OnEnter()
{
}
}
然后修改MainScene,下面这个方法是加载完场景后调用的,我们在场景加载完后推出新面板:
此时按下主面板的开始建
即可跳转到Start界面并推出Panel
当从Start场景跳转到主场景时会发现,GameRoot就没了,这样就不好管理跳转回去,
为此,我们添加这样的函数:
此时即使跳转,GameRoot也不会消失
接下来实现当按下MainPanel的按键时,返回主场景的功能:
做到这里后即可实现按下主场景中的叉键场景跳转,但是出现了一个问题,当再次回到主场景时,多了一个GameRoot
为了防止这种情况,我们可以
然后在每个场景结束时,都弹出所有面板
同理MainScene也这样做:
像这样:
还有一个优化就是,我们可能会在框架以外的地方用到Push或者Pop,所以可以在这里定义一个:
用来存放预制体中该UI的名字以及路径,基础面板含有这个对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIType
{
public string Name { get; private set; }
///
/// UI路径
///
public string Path { get; private set; }
public UIType(string path)
{
Path = path;
Name = path.Substring(path.LastIndexOf('/') + 1);
}
}
所有面板类的基类,当需要新的面板时,首先需要继承该类。
对于一个面板来说,存在四种状态,c
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class BasePanel
{
///
/// UI信息
///
public UIType UIType { get; private set; }
///
/// UI管理工具
///
public UITool UITool { get; private set; }
public PanelManager PanelManager { get; private set; }
public UIManager UIManager { get; private set; }
public BasePanel(UIType uIType)
{
UIType = uIType;
}
public void Initialize(UITool tool)
{
UITool = tool;
}
///
/// 初始化面板管理器
///
///
public void Initialize(PanelManager panelManager)
{
PanelManager = panelManager;
}
public void Initialize(UIManager uiManager)
{
UIManager = uiManager;
}
public virtual void OnEnter() { }
public virtual void OnPause() {
UITool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = false;
}
///
/// UI继续时的操作
///
public virtual void OnResume() {
UITool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = true;
}
public virtual void OnExit() {
UIManager.DestroyUI(UIType);
}
public void Push(BasePanel panel) => PanelManager?.Push(panel);
//注意,上行这种写法和下行一样:
/*public void Push(BasePanel panel)
{
PanelManager?.Push(panel);
}*/
public void Pop() => PanelManager?.Pop();
}
每一个面板下面可能有很多个UI属性,因此每个面板都需要一个UI管理器,它有获取面板下的UI,创建UI,或者销毁UI的功能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 存储所有UI信息,并可以创建或者销毁UI
///
public class UIManager
{
// Start is called before the first frame update
///
/// 存储所有UI信息的字典,每一个UI信息都会对应一个GameObject
///
private Dictionary<UIType, GameObject> dicUI;
public UIManager()
{
dicUI = new Dictionary<UIType, GameObject>();
}
///
/// 获取当前面板下的某个UI对象,如果没有该UI对象,我们则创建一个
///
/// UI信息
///
public GameObject GetSingleUI(UIType type)
{
//在每个面板中,其UI信息都存放在Canva(画布下),画布和画布中的所有信息就组成了一个面板,因此首先需要找到这个画布
GameObject parent = GameObject.Find("Canvas");//在游戏中查找名为Canvas的对象
if (!parent)
{
Debug.LogError("Canvas不存在,请仔细查找有无这个对象");
return null;
}
//如果已经存在这个对象则直接返回该对象,如果不存在则生成一个对象
if (dicUI.ContainsKey(type)) return dicUI[type];
GameObject ui = GameObject.Instantiate(Resources.Load<GameObject>(type.Path), parent.transform);
ui.name = type.Name;
dicUI.Add(type, ui);
return ui;
}
///
/// 销毁一个UI对象
///
/// UI信息
public void DestroyUI(UIType type)
{
if (dicUI.ContainsKey(type))
{
GameObject.Destroy(dicUI[type]);
dicUI.Remove(type);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 面板管理器 用栈来存储UI
///
public class PanelManager
{
///
/// 存储UI面板的栈
///
private Stack<BasePanel> stackPanel;
///
/// UI管理器
///
private UIManager uiManager;
private BasePanel panel;
public PanelManager()
{
stackPanel = new Stack<BasePanel>();
uiManager = new UIManager();
}
///
/// UI的入栈操作,此操作会显示一个面板
///
/// 要显示的面板
public void Push(BasePanel nextPanel)
{
if (stackPanel.Count > 0)
{
panel = stackPanel.Peek();//获取栈顶
panel.OnPause();
}
stackPanel.Push(nextPanel);
GameObject panelGo = uiManager.GetSingleUI(nextPanel.UIType);
nextPanel.Initialize(new UITool(panelGo));
nextPanel.Initialize(this);
nextPanel.Initialize(uiManager);
nextPanel.OnEnter();
}
///
/// 执行面板的出栈操作,此操作会执行面版的OnExit方法
///
public void Pop()
{
if (stackPanel.Count > 0)
{
stackPanel.Pop().OnExit();
//把这个面板给弹出,↓如果这个面板还有的话就继续
}
if (stackPanel.Count > 0) stackPanel.Peek().OnResume();
}
public void PopAll()
{
while (stackPanel.Count > 0) stackPanel.Pop().OnExit();
}
}
上面就是创建一个面板所需要的脚本,接下来我们创建一个实例面板脚本,它需要预制体的路径。
并且我们需要对这个面板进行操作,因此需要重载OnEnter函数。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
///
/// 开始主面板
///
public class StartPanel :BasePanel
{
static readonly string path = "Prefabs/UI/Panel/StartPanel";
public StartPanel():base(new UIType(path)) { }
public override void OnEnter()
{
//base.OnEnter();
UITool.GetOrAddComponentInChildren<Button>("Setting").onClick.AddListener(() =>
{
//点击事件可以写在这里面
Debug.Log("点击了设置按钮");
//PanelManager.Push(new SettingPanel());
Push(new SettingPanel());
});
UITool.GetOrAddComponentInChildren<Button>("Play").onClick.AddListener(() =>
{
//点击事件可以写在这里面
//Debug.Log("点击了设置按钮");
GameRoot.Instance.SceneSystem.SetScene(new MainScene());
});
}
}
在面板里,我们有时需要获取/添加这个面板下的一些组件,或者是获取/添加其子对象的组件,我们可以将其放在BasePanel中实现。
但是我们可以书写一个工具类UITool,将这些函数放在UITool类中实现,然后让BasePanel有UITool类这个子对象,然后只需要让BasePanel调用UITool类中的找组件函数即可实现。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UITool
{
///
/// 当前的活动面板
///
GameObject activePanel;
public UITool(GameObject panel)
{
activePanel = panel;
}
///
/// 给当前的活动面板获取或添加一个组件
///
/// 组件类型
/// 组件
public T GetOrAddComponent<T>() where T : Component
{
if (activePanel.GetComponent<T>() == null)
{
activePanel.AddComponent<T>();
}
return activePanel.GetComponent<T>();
}
///
/// 根据名称查找子对象
///
/// 子对象名称
///
public GameObject FindChildGameObject(string name)
{
Transform[] trans = activePanel.GetComponentsInChildren<Transform>();
foreach (Transform item in trans)
{
if (item.name == name)
{
return item.gameObject;
}
}
Debug.LogWarning($"{activePanel.name}里找不到名为{name}的子对象");
return null;
}
///
/// 根据名称获取一个子对象的组件
///
///
///
///
public T GetOrAddComponentInChildren<T>(string name) where T : Component
{
GameObject child = FindChildGameObject(name);
if (child)
{
if (child.GetComponent<T>() == null) child.AddComponent<T>();
return child.GetComponent<T>();
}
else return null;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SettingPanel : BasePanel
{
static readonly string path = "Prefabs/UI/Panel/SettingPanel";
public SettingPanel() : base(new UIType(path)) { }
public override void OnEnter()
{
//base.OnEnter();
UITool.GetOrAddComponentInChildren<Button>("Setting2").onClick.AddListener(() =>
{
//点击事件可以写在这里面
Debug.Log("点击了设置2按钮");
PanelManager.Push(new SettingPanel2());
});
UITool.GetOrAddComponentInChildren<Button>("Exit").onClick.AddListener(() =>
{
//点击事件可以写在这里面
Debug.Log("点击了退出设置按钮");
PanelManager.Pop();
});
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StartManager : MonoBehaviour
{
PanelManager panelManager;
// Start is called before the first frame update
private void Awake()
{
panelManager = new PanelManager();
}
void Start()
{
panelManager.Push(new StartPanel());
}
// Update is called once per frame
}
/
/
/
//public class UITool
//{
// ///
// /// 当前的活动面板
// ///
// GameObject activePanel;
// public UITool(GameObject panel)
// {
// activePanel = panel;
// }
// ///
// /// 给当前的活动面板获取或添加一个组件
// ///
// /// 组件类型
// /// 组件
// public T GetOrAddComponent() where T : Component
// {
// if (activePanel.GetComponent() == null)
// {
// activePanel.AddComponent();
// }
// return activePanel.GetComponent();
// }
// ///
// /// 根据名称查找子对象
// ///
// /// 子对象名称
// ///
// public GameObject FindChildGameObject(string name)
// {
// Transform[] trans = activePanel.GetComponentsInChildren();
// foreach (Transform item in trans)
// {
// if (item.name == name)
// {
// return item.gameObject;
// }
// }
// Debug.LogWarning($"{activePanel.name}里找不到名为{name}的子对象");
// return null;
// }
//}
/
/ 根据名称获取一个子对象的组件
/
/
/
/
//public T GetOrAddComponentInChildren(string name) where T : Component
//{
// GameObject child = FindChildGameObject(name);
// if (child)
// {
// if (child.GetComponent() == null) child.AddComponent();
// return child.GetComponent();
// }
// else return null;
//}
/
/
SettingPanel(此处复制粘贴StartPanel
//using UnityEngine.UI;
//public class SettingPanel : BasePanel
//{
// static readonly string path = "Prefabs/UI/Panel/SettingPanel";
// public SettingPanel() : base(new UIType(path)) { }
// public override void OnEnter()
// {
// //base.OnEnter();
// //UITool.GetOrAddComponentInChildren
// //{
// //点击事件可以写在这里面
// // Debug.Log("点击了设置按钮");
// // PanelManager.Push(new SettingPanel());
// //})
// UITool.GetOrAddComponentInChildren
// {
// PanelManager.Pop();
// });
// }
// public override void OnExit()
// {
// //base.OnExit();
// UIManager.DestroyUI(UIType);
// }
// ///
// /// 下面这两个函数是为了在点开设置面板时,封面的按钮设置应该关掉,所以此时应该将下面这两个函数放在StartPanel,或者可以直接放在BasePanel里面
// ///
// public override void OnPause()
// {
// UITool.GetOrAddComponent().blocksRaycasts = false;
// }
// public override void OnResume()
// {
// UITool.GetOrAddComponent().blocksRaycasts = false;
// }
//}
/接下来需要 减少设置面板
/SceneState.cs
/
// ///场景状态
//public abstract class SceneState
//{
// ///
// /// 场景进入时执行的操作
// ///
// public abstract void OnEnter();
// ///
// /// 场景退出时执行的操作
// ///
// public abstract void OnExit();
//}
/开始场景
/using UnityEngine.SceneManagement
//public class StartScene : SceneState
//{
// ///
// /// 场景名称
// ///
// readonly string sceneName = "Start";
// PanelManager panelManager;
// public override void OnEnter()
// {
// panelManager = new PanelManager();
// if (SceneManager.GetActiveScene().name != sceneName)
// {
// SceneManager.LoadScene(sceneName);
// SceneManager.sceneLoaded += SceneLoaded;//添加方法的绑定
// }
// else
// {
// panelManager.Push(new StartPanel());
// }
// }
// public override void OnExit()
// {
// SceneManager.sceneLoaded -= SceneLoaded;
// }
// ///
// /// 场景加载完毕后执行的方法
// ///
// ///
// ///
// private void SceneLoaded(Scene scene,LoadSceneMode load)
// {
// Debug.Log($"{sceneName}场景加载完毕");
// }
//}
/MainScene 复制上面的StartScene
/
/SceneSystem.cs
/
//public class SceneSystem
//{
// ///
// /// 场景状态类
// ///
// SceneState sceneState;
// ///
// /// 设置当前场景并进入当前场景
// ///
// ///
// public void SetScene(SceneState state)
// {
// if (sceneState != null) sceneState.OnExit();
// sceneState = state;
// if (sceneState != null) sceneState.OnEnter();
// sceneState?.OnExit();
// sceneState = state;
// sceneState?.OnEnter();
// }
//}
GameRoot.cs
/管理全局的一些东西
//public class GameRoot : MonoBehaviour
//{
// public static GameRoot Instance { get; private set; }
// public SceneSystem SceneSystem { get; private set; }
// private void Awake()
// {
// Instance = this;
// SceneSystem = new SceneSystem();
// }
// private void Start()
// {
// SceneSystem.SetScene(new StartScene());
// }
//}
/MainPanel
/main场景的主面板
/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class SceneState
{
///
/// 场景进入时执行的操作
///
public abstract void OnEnter();
///
/// 场景退出时执行的操作
///
public abstract void OnExit();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 场景的状态管理系统
///
public class SceneSystem
{
///
/// 场景状态类
///
SceneState sceneState;
///
/// 设置当前场景并进入当前场景
///
///
public void SetScene(SceneState state)
{
if (sceneState != null) sceneState.OnExit();
sceneState = state;
if (sceneState != null) sceneState.OnEnter();
//sceneState?.OnExit();
//sceneState = state;
//sceneState?.OnEnter();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartScene :SceneState
{
///
/// 场景名称
///
readonly string sceneName = "Start";
PanelManager panelManager;
public override void OnEnter()
{
panelManager = new PanelManager();
if (SceneManager.GetActiveScene().name != sceneName)
{
SceneManager.LoadScene(sceneName);
SceneManager.sceneLoaded += SceneLoaded;//添加方法的绑定
}
else
{
panelManager.Push(new StartPanel());
}
}
public override void OnExit()
{
SceneManager.sceneLoaded -= SceneLoaded;
panelManager.PopAll();
}
///
/// 场景加载完毕后执行的方法
///
///
///
private void SceneLoaded(Scene scene, LoadSceneMode load)
{
panelManager.Push(new StartPanel());
//GameRoot.Instance.SetAction(panelManager.Push);优化
Debug.Log($"{sceneName}场景加载完毕");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainScene :SceneState
{
// Start is called before the first frame update
///
/// 场景名称
///
readonly string sceneName = "Main";
PanelManager panelManager;
public override void OnEnter()
{
panelManager = new PanelManager();
if (SceneManager.GetActiveScene().name != sceneName)
{
SceneManager.LoadScene(sceneName);
SceneManager.sceneLoaded += SceneLoaded;//添加方法的绑定
}
else
{
panelManager.Push(new StartPanel());
//GameRoot.Instance.SetAction(panelManager.Push);
}
}
public override void OnExit()
{
SceneManager.sceneLoaded -= SceneLoaded;
panelManager.PopAll();
}
///
/// 场景加载完毕后执行的方法
///
///
///
private void SceneLoaded(Scene scene, LoadSceneMode load)
{
panelManager.Push(new MainPanel());
//GameRoot.Instance.SetAction(panelManager.Push);
Debug.Log($"{sceneName}场景加载完毕");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
///
/// Main场景的主面板
///
public class MainPanel : BasePanel
{
static readonly string path = "Prefabs/UI/Panel/MainPanel";
public MainPanel() : base(new UIType(path)) { }
public override void OnEnter()
{
base.OnEnter();
UITool.GetOrAddComponentInChildren<Button>("Quit").onClick.AddListener(() =>
{
//点击事件可以写在这里面
GameRoot.Instance.SceneSystem.SetScene(new StartScene());
//PanelManager.Pop();
Pop();
});
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
///
///管理全局的一些东西
///
public class GameRoot : MonoBehaviour
{
public static GameRoot Instance { get; private set; }
public SceneSystem SceneSystem { get; private set; }
///
/// 显示一个面板
///
public UnityAction<BasePanel> Push { get; private set; }
public void SetAction(UnityAction<BasePanel> push)
{
Push = push;
}
private void Awake()
{
if (Instance == null)
Instance = this;
else
Destroy(gameObject);
SceneSystem = new SceneSystem();
DontDestroyOnLoad(gameObject);
}
private void Start()
{
SceneSystem.SetScene(new StartScene());
}
}