学习B站简问游戏开发的代码框架入门,自己修改了一点。
具体控件了解,需要时查书
主要使用了单例模式
Panel部分
一个父类BasePanel,一个UIType。一个panel存储一个界面,比如开始界面,登录界面,设置界面的UI预制件。UIType记录预制件的名称和文件路径
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BasePanel
{
public UIType uiType;
// UI对应的object
public GameObject ActiveObj;
public BasePanel(UIType uiType)
{
this.uiType = uiType;
}
public virtual void OnStart()
{
Debug.Log($"{uiType.Name}开始使用");
UIMethod.Instance.GetOrAddComponent<CanvasGroup>(ActiveObj).interactable = true;
}
public virtual void OnEnable()
{
UIMethod.Instance.GetOrAddComponent<CanvasGroup>(ActiveObj).interactable = true;
}
public virtual void OnDisable()
{
UIMethod.Instance.GetOrAddComponent<CanvasGroup>(ActiveObj).interactable = false;
}
public virtual void OnDestroy()
{
UIMethod.Instance.GetOrAddComponent<CanvasGroup>(ActiveObj).interactable = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class StartPanel : BasePanel
{
private static string name = "StartPanel";
private static string path = "Panel/StartPanel";
public static readonly UIType startUIType = new UIType(name, path);
public StartPanel(): base(startUIType)
{
}
public override void OnDestroy()
{
base.OnDestroy();
}
public override void OnDisable()
{
base.OnDisable();
}
public override void OnEnable()
{
base.OnEnable();
}
public override void OnStart()
{
base.OnStart();
Scene2 scene2 = new Scene2();
UIMethod.Instance.GetOrAddSingleComponentInChild<Button>(ActiveObj, "StartButton").onClick.AddListener(() => SceneController.Instance.LoadScene(scene2.sceneName, scene2));
UIMethod.Instance.GetOrAddSingleComponentInChild<Button>(ActiveObj, "StartButton").GetComponentInChildren<TextMeshProUGUI>().text = "Load Scene2";
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIType
{
// 自己定义的一种数据结构,不需要挂载mono
private string name;
private string path;
public string Name { get => name; }
public string Path { get => path; }
// 构造函数
/// <summary>
/// 获得UI信息
/// </summary>
/// <param name="uiName">此UI对于Panel的名称</param>
/// <param name="uiPath">此UI对于Panel的路径</param>
public UIType(string uiName, string uiPath)
{
name = uiName;
path = uiPath;
}
}
Scene部分
创建,控制场景的加载等。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 场景的父类
/// </summary>
public abstract class SceneBase
{
/// <summary>
/// 进入场景时执行的方法
/// </summary>
public abstract void EnterScene();
/// <summary>
/// 退出场景时执行的方法
/// </summary>
public abstract void ExitScene();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 控制场景切换
/// </summary>
public class SceneController
{
/// <summary>
/// scene名称——scene场景
/// </summary>
public Dictionary<string, SceneBase> dictionaryScene;
private static SceneController instance;
public static SceneController Instance
{
get
{
if (instance == null)
{
Debug.LogWarning("SceneController实例不存在,创建");
instance = new SceneController();
}
return instance;
}
}
private SceneController()
{
dictionaryScene = new Dictionary<string, SceneBase>();
}
/// <summary>
/// 加载场景
/// </summary>
/// <param name="sceneName">场景名称</param>
/// <param name="scene">场景</param>
public void LoadScene(string sceneName, SceneBase scene)
{
// 记录场景信息
if (!dictionaryScene.ContainsKey(sceneName))
{
dictionaryScene.Add(sceneName, scene);
}
// 退出当前激活状态的场景
if (dictionaryScene.ContainsKey(SceneManager.GetActiveScene().name))
{
dictionaryScene[SceneManager.GetActiveScene().name].ExitScene();
}
else
{
Debug.LogWarning($"SceneDictionary中不存在{SceneManager.GetActiveScene().name}");
}
// 弹出旧场景所有panel
UIManager.Instance.PopAll();
// 加载新指定场景
SceneManager.LoadScene(sceneName);
scene.EnterScene();
}
}
public class Scene1 : SceneBase
{
public readonly string sceneName = "Scene1";
public override void EnterScene()
{
Debug.Log($"Enter Scene: {sceneName}");
}
public override void ExitScene()
{
Debug.Log($"Exit Scene: {sceneName}");
}
}
UIMethod和UIManager
对UI物体的一些操作,比如界面之间的跳转,利用栈结构实现。组件的查找添加等。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIManager
{
// UIManager静态实例指针
private static UIManager instance;
public static UIManager Instance
{
get
{
if (instance == null)
{
// 如果实例为空
Debug.LogWarning("UIManager实例不存在,创建");
instance = new UIManager();
}
return instance;
}
}
/// <summary>
/// 存储panel ui的栈
/// </summary>
public Stack<BasePanel> uiStack;
/// <summary>
/// 栈中的panel名称——panel物体信息
/// </summary>
public Dictionary<string, GameObject> uiDictionary;
/// <summary>
/// 当前场景canvas
/// </summary>
public GameObject canvasObj;
private UIManager()
{
uiStack = new Stack<BasePanel>();
uiDictionary = new Dictionary<string, GameObject>();
}
public GameObject GetSingleObject(UIType uiType)
{
// 如果dictionary中存在,直接返回
if (uiDictionary.ContainsKey(uiType.Name))
return uiDictionary[uiType.Name];
// 如果canvas为空,那么获取
if(canvasObj == null)
{
canvasObj = UIMethod.Instance.FindCanvas();
}
// 如果canvas不为空,那么在canvas中创建该uiType对应物体
GameObject uiPrefab = Resources.Load<GameObject>(uiType.Path);
if (uiPrefab == null)
{
// 创建失败,物体不存在
Debug.LogError("UI prefab not found at path: " + uiType.Path);
return null;
}
return GameObject.Instantiate<GameObject>(uiPrefab, canvasObj.transform);
}
public void Push(BasePanel basePanel)
{
Debug.Log($"Push {basePanel.uiType.Name} into uiStack");
// 如果uiStack不为空,那么禁用、不显示现在栈顶UI
if(uiStack.Count > 0)
{
Debug.Log($"禁用{uiStack.Peek().ActiveObj.name}");
uiStack.Peek().OnDisable();
}
// 获取basePanel记录的uiObj
GameObject uiObject = GetSingleObject(basePanel.uiType);
// 记录新的UI元素
uiDictionary.Add(basePanel.uiType.Name, uiObject);
basePanel.ActiveObj = uiObject;
if(uiStack.Count == 0 || uiStack.Peek().uiType.Name != basePanel.uiType.Name)
{
uiStack.Push(basePanel);
}
Debug.Log($"启用{uiObject.name}");
basePanel.OnStart();
}
public void Pop()
{
// 首先栈不能为空
if(uiStack.Count > 0)
{
// ————这部分是出栈————
// 首先调用栈顶UI的disable方法和destroy方法
BasePanel peekUI = uiStack.Peek();
peekUI.OnDisable();
peekUI.OnDestroy();
// 销毁UI Object
Debug.Log($"销毁面板{peekUI.uiType.Name}");
GameObject.Destroy(uiDictionary[peekUI.uiType.Name]);
uiDictionary.Remove(peekUI.uiType.Name);
// 从uiStack中出栈
uiStack.Pop();
// ————出栈结束————
// 如果UI栈不为空,那么启用顶层UI
if (uiStack.Count > 0)
{
uiStack.Peek().OnEnable();
}
}
}
public void PopAll()
{
// 栈不能为空
while (uiStack.Count > 0)
{
// ————这部分是出栈————
// 首先调用栈顶UI的disable方法和destroy方法
BasePanel peekUI = uiStack.Peek();
peekUI.OnDisable();
peekUI.OnDestroy();
// 销毁UI Object
GameObject.Destroy(uiDictionary[peekUI.uiType.Name]);
uiDictionary.Remove(peekUI.uiType.Name);
// 从uiStack中出栈
uiStack.Pop();
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIMethod
{
private static UIMethod instance;
public static UIMethod Instance
{
get
{
if (instance == null)
{
Debug.LogWarning("UIMethod 不存在,创建");
instance = new UIMethod();
}
return instance;
}
}
/// <summary>
/// 获得当前场景中的canvas,仅针对单canvas场景
/// </summary>
/// <returns>canvas object</returns>
public GameObject FindCanvas()
{
Canvas canvas = GameObject.FindObjectOfType<Canvas>();
if (canvas == null)
{
Debug.LogError("未在当前sense中找到canvas");
return null;
}
return canvas.gameObject;
}
/// <summary>
/// 查找panel中某个name的物体
/// </summary>
/// <param name="panel">需要查找的UI panel</param>
/// <param name="childName">子物体名称</param>
/// <returns></returns>
public GameObject FindObjectInChildWithName(GameObject panel, string childName)
{
Transform[] childTransforms = panel.transform.GetComponentsInChildren<Transform>();
foreach (Transform child in childTransforms)
{
if (child.gameObject.name == childName)
{
return child.gameObject;
}
}
Debug.LogError($"未在 {panel.name} 中查找到子物体 {childName}");
return null;
}
/// <summary>
/// 获取ui object中的组件,如果没有就添加一个
/// </summary>
/// <typeparam name="T">组件类型</typeparam>
/// <param name="uiObject">ui 物体</param>
/// <returns></returns>
public T GetOrAddComponent<T>(GameObject uiObject) where T : Component
{
T component = uiObject.GetComponent<T>();
if (component == null)
{
Debug.LogWarning($"{uiObject.name}上不存在目标组件,已自动添加");
component = uiObject.AddComponent<T>();
}
return component;
}
/// <summary>
/// 获取指定名称的子物体的某组件
/// </summary>
/// <typeparam name="T">组件类型</typeparam>
/// <param name="uiObject">母物体</param>
/// <param name="childName">子物体名称</param>
/// <returns></returns>
public T GetOrAddSingleComponentInChild<T>(GameObject uiObject, string childName) where T : Component
{
Transform[] childTransforms = uiObject.GetComponentsInChildren<Transform>();
foreach (Transform child in childTransforms)
{
if(child.gameObject.name == childName)
{
// 获取指定子物体的指定组件
T component = child.gameObject.GetComponent<T>();
if (component == null)
{
Debug.LogWarning($"{child.gameObject.name}上不存在目标组件,已自动添加");
child.gameObject.AddComponent<T>();
}
// 返回该组件
return component;
}
}
// 未找到指定子物体
Debug.LogWarning($"{uiObject.name}中未找到{childName}子物体");
return null;
}
}