Unity通用框架

介绍

对常用的模块进行封装,实现部分通用基础框架。

单例基类

饿汉式单例基类

public class SingletonBase<T> where T : new()
{
    private static T _instance;
    public static T Instance
    {
        get {
            if (_instance == null) {
                _instance = new T();
            }
            return _instance;
        }
    }
}

饿汉式Mono单例基类

public class SingletonMonoBase<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;

    public static T Instance => _instance;

    // 子类继承并重写Awake,调用父级Awake,即可赋值Instance
    protected virtual void Awake() {
        _instance = this as T;
    }
}

全局公共Mono

mono模块

public class MonoModule : MonoBehaviour
{
    public event UnityAction MonoEvent;

    // Start is called before the first frame update
    void Start() {
        GameObject.DontDestroyOnLoad(this);
    }

    // Update is called once per frame
    void Update() {
        MonoEvent?.Invoke();
    }

    public void AddEvent(UnityAction func) {
        MonoEvent += func;
    }

    public void RemoveEvent(UnityAction func) {
        MonoEvent -= func;
    }
    
    private void OnApplicationQuit() {
        // 退出时释放并销毁,以免MissingReferenceException
        MonoMgr.Instance.DestroyRef();
        DestroyImmediate(this.gameObject);
    }
    
}

mono管理器

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;

public class MonoMgr : SingletonBase<MonoMgr>
{
    private MonoModule _monoModule;
    public MonoMgr() {
        // 只在MonoModule不存在时创建新的MonoModule对象
        _monoModule = Object.FindObjectOfType<MonoModule>();
        if (_monoModule == null)
        {
            GameObject gameObject = new GameObject("MonoModule");
            _monoModule = gameObject.AddComponent<MonoModule>();
            Debug.Log("不存在");
        }
        else
        {
            Debug.Log("存在");
        }
    }

    public void AddEvent(UnityAction func) {
        _monoModule.AddEvent(func);
    }

    public void RemoveEvent(UnityAction func) {
        _monoModule.RemoveEvent(func);
    }

    public Coroutine StartCoroutine(IEnumerator enumerator) {
        return _monoModule.StartCoroutine(enumerator);
    }

    public void StopCoroutine(IEnumerator enumerator) {
        _monoModule.StopCoroutine(enumerator);
    }
    
    public void StopCoroutine(Coroutine coroutine) {
        _monoModule.StopCoroutine(coroutine);
    }

    public void StopAllCoroutine() {
        _monoModule.StopAllCoroutines();
    }

    public void DestroyRef() {
        MonoMgr.Instance = null;
        _monoModule = null;
    }
}

通用简易有限状态机FSM

状态机

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum StateEnum
{
    Idle,
    Walk,
    Jump,
    JumpDown,
    JumpAtk,
    Dash,
    Atk,
    Skill,

    EnemyIdle,
    EnemyWalk,
    EnemyDetectedPlayer,
    EnemyChargePlayer,
    EnemyEvadePlayer,
    EnemyHit,
    EnemyAtk,
    EnemyDead,

    MageTeleport
}

[System.Serializable]
public class FsmSystem
{
    /// <summary>
    /// 下次状态ID
    /// </summary>
    public StateEnum NextStateID { get; private set; }

    /// <summary>
    /// 当前状态ID
    /// </summary>
    public StateEnum CurrentStateID { get; private set; }

    /// <summary>
    /// 当前状态
    /// </summary>
    public IState CurrentState { get; private set; }

    public Dictionary<StateEnum, IState> statesDict = new();

    /// <summary>
    /// 添加状态
    /// </summary>
    /// <param name="stateId"></param>
    /// <param name="state"></param>
    public void AddState(StateEnum stateId, IState state) {
        if (!statesDict.TryAdd(stateId, state)) // 键若存在,则覆盖
        {
            statesDict[stateId] = state;
        }
    }

    /// <summary>
    /// 切换状态
    /// </summary>
    /// <param name="stateID">状态ID,即枚举中的状态类型</param>
    public void SwitchState(StateEnum stateID) {
        NextStateID = stateID;
        if (CurrentState != null)
        {
            CurrentState.OnExit();
        }

        CurrentStateID = stateID;
        CurrentState = statesDict[stateID];
        CurrentState.OnEnter();
    }

    public void OnUpdate() {
        CurrentState.OnUpdate();
    }

    public void OnPhysicsUpdate() {
        CurrentState.OnPhysicsUpdate();
    }
}

状态接口

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IState
{
    void OnEnter();

    void OnUpdate();

    void OnPhysicsUpdate();

    void OnExit();

}

对象池

对象池

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BufferPool
{
    /// <summary>
    /// 缓存池中的对象的父物体,防止物体过多显示在Hierarchy中而杂乱
    /// </summary>
    private GameObject _poolParentObj;

    /// <summary>
    /// 对象list
    /// </summary>
    private readonly List<GameObject> _gameObjList;
    private readonly GameObject _gameObject;
    private readonly bool _isCreateParent;
    private readonly Transform _targetParentObj;

    /// <summary>
    /// 初始化缓存池,并且指定缓存池的目标父物体
    /// </summary>
    /// <param name="gameObj">需要放入缓存池的对象</param>
    /// <param name="targetParentObj">指定缓存池对象的父物体</param>
    /// <param name="isCreateParent">是否创建缓存池中对象的父物体</param>
    public BufferPool(GameObject gameObj, Transform targetParentObj = null, bool isCreateParent = true) {
        this._gameObject = gameObj;
        this._gameObjList = new List<GameObject>();
        this._isCreateParent = isCreateParent;
        this._targetParentObj = targetParentObj;

        if (isCreateParent)
        {
            // 创建缓存池中对象的父物体
            this._poolParentObj = new GameObject(gameObj.name);
            //_poolParentObj.transform.SetPositionAndRotation(Vector3.zero, Quaternion.Euler(Vector3.zero));
        }

        if (targetParentObj != null && isCreateParent)
            // 将缓存池整体的父物体设为指定物体
            _poolParentObj.transform.parent = targetParentObj;
    }

    /// <summary>
    /// 将对象放回缓存池
    /// </summary>
    /// <param name="currentGameObj">目标缓存池对象</param>
    public void PutToPool(GameObject currentGameObj) {
        _gameObjList.Add(currentGameObj);
        currentGameObj.SetActive(false);
        //currentGameObj.transform.SetParent(null);
    }

    /// <summary>
    /// 从缓存池获取对象
    /// </summary>
    /// <returns>缓存对象</returns>
    public GameObject GetToPool(bool isActiveObj) {
        GameObject gameObj = null;
        // 当前对象list是否有对象,有则取并从list移除,无则创建
        if (_gameObjList.Count > 0)
        {
            gameObj = _gameObjList[0];
            _gameObjList.RemoveAt(0);
        }
        else
        {
            gameObj = GameObject.Instantiate<GameObject>(this._gameObject);
        }

        // 是否获取时激活
        if (isActiveObj)
            gameObj.SetActive(true);
        
        // 是否创建缓存池中对象的父物体
        if (this._isCreateParent)
            gameObj.transform.SetParent(this._poolParentObj.transform);
        else
            gameObj.transform.SetParent(this._targetParentObj);
        return gameObj;
    }
}

对象池管理器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BufferPoolMgr : SingletonBase<BufferPoolMgr>
{
    /// <summary>
    /// key: 缓存池的对象名
    /// value: 对应的缓存池对象
    /// 存储多个缓存池
    /// </summary>
    private readonly Dictionary<string, BufferPool> _poolDic = new Dictionary<string, BufferPool>();

    /// <summary>
    /// 初始化单个对象池并放入字典,让管理器来管理
    /// </summary>
    /// <param name="poolName">缓存池对象名</param>
    /// <param name="gameObject">缓存池对象</param>
    /// <param name="targetParentObj">缓存池对象在Hierarchy的父节点</param>
    public void Init(string poolName, GameObject gameObject, Transform targetParentObj = null, bool isCreateParent = true) {
        // if (!_poolDic.ContainsKey(poolName)) { // 判断是否存在
            _poolDic[poolName] = new BufferPool(gameObject, targetParentObj, isCreateParent);
        // }
    }

    /// <summary>
    /// 从指定的缓存池中获取对象
    /// </summary>
    /// <param name="poolName">缓存池的名称</param>
    /// <param name="isActiveObj">是否激活对象,是-在获取时同时激活对象,否则交给外部激活</param>
    /// <returns>缓存池对象</returns>
    public GameObject GetToPools(string poolName,bool isActiveObj = true) {
        GameObject gameObj = null;
        gameObj = _poolDic[poolName].GetToPool(isActiveObj); // 直接获取对象,缓存池类中会有空判断
        return gameObj;
    }

    /// <summary>
    /// 将对象放入指定对象池
    /// </summary>
    /// <param name="poolName">指定缓存池</param>
    /// <param name="gameObject">使用完的缓存池对象</param>
    public void PutToPools(string poolName, GameObject gameObject) {
        if (_poolDic.ContainsKey(poolName)) { // 判断这个缓存池是否存在,不存在则Init创建
            _poolDic[poolName].PutToPool(gameObject);
            //Debug.Log($"key:{poolName}存在,放入池中");
        }
        else {
            this.Init(poolName, gameObject);
        }
    }

    /// <summary>
    /// 清除缓存池
    /// </summary>
    public void ClearPool() {
        this._poolDic.Clear();
    }
}

UI管理器

UI基类

using System.Collections.Generic;
using TMPro;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.EventSystems;

/// <summary>
/// UI Panel基类
/// 提供了快速查找和获取UI组件的功能,方便面板的显示和隐藏。
/// </summary>
public class UIPanelBase : MonoBehaviour
{
    /// <summary>
    /// 找到自身的所有的子控件
    /// 定义了一个Dictionary类型的变量,根据控件名称存储对应的控件组件,
    /// 其中控件组件以List类型存储,是为了允许同一个控件名对应多个控件组件,比如同一个面板有多个按钮。
    /// </summary>
    private Dictionary<string, List<UIBehaviour>> _controlDict = new Dictionary<string, List<UIBehaviour>>();

    protected virtual void Awake() {
        // 获取所有子控件
        FindControlsInChildren<Text>();
        FindControlsInChildren<Image>();
        FindControlsInChildren<RawImage>();
        FindControlsInChildren<Button>();
        FindControlsInChildren<Slider>();
        FindControlsInChildren<Toggle>();
        FindControlsInChildren<ScrollRect>();
        FindControlsInChildren<TextMeshProUGUI>();
        FindControlsInChildren<TMP_Dropdown>();
        FindControlsInChildren<TMP_InputField>();
    }
    
    
    /// <summary>
    /// 在子对象中查找指定类型的控件,并将它们添加到控件字典中
    /// 该方法通过泛型参数T来查找指定类型的控件,并将它们添加到控件字典中。
    /// 在查找控件时使用了GetComponentsInChildren方法,它会在所有子对象中查找指定类型的控件。
    /// 如果控件名称在控件字典中已存在,则将该控件添加到对应的控件列表中,否则创建一个新的控件列表,并将该控件添加到列表中。
    /// </summary>
    /// <typeparam name="T">控件类型</typeparam>
    private void FindControlsInChildren<T>() where T : UIBehaviour
    {
        T[] controls = GetComponentsInChildren<T>(true);
        foreach (T control in controls)
        {
            string controlName = control.gameObject.name;
            //在字典_controlDict中尝试通过控件名称(controlName)获取对应的列表(value),
            //如果存在该名称的列表,则将该控件(control)添加到该列表中
            if (_controlDict.TryGetValue(controlName, out var value))
            {
                value.Add(control);
            }
            else
            {
                _controlDict.Add(controlName, new List<UIBehaviour>() { control });
            }
            
            //如果是按钮控件
            if(control is Button)
            {
                (control as Button).onClick.AddListener(()=>
                {
                    OnClick(controlName);
                });
            }
            //如果是单选框或者多选框
            else if(control is Toggle)
            {
                (control as Toggle).onValueChanged.AddListener((value) =>
                {
                    OnValueChanged(controlName, value);
                });
            }
        }
    }
    
    /// <summary>
    /// 获取指定名称的控件
    /// 该方法用于根据控件名称和控件类型获取对应的控件对象。
    /// 首先判断控件字典中是否包含指定的控件名称,如果包含则遍历该名称对应的控件列表,
    /// 找到第一个类型符合的控件对象,然后返回该对象。如果找不到符合要求的控件,则返回null。
    /// </summary>
    /// <typeparam name="T">控件类型</typeparam>
    /// <param name="controlName">控件名称</param>
    /// <returns>指定名称的控件,如果不存在则返回null</returns>
    public T GetControl<T>(string controlName) where T : UIBehaviour
    {
        if (_controlDict.ContainsKey(controlName))
        {
            List<UIBehaviour> controls = _controlDict[controlName];
            foreach (UIBehaviour control in controls)
            {
                if (control is T t)
                {
                    return t;
                }
            }
        }

        return null;
    }

    protected virtual void OnClick(string btnName)
    {

    }

    protected virtual void OnValueChanged(string toggleName, bool value)
    {

    }
    
    /// <summary>
    /// 显示自己
    /// </summary>
    public virtual void Show()
    {
        gameObject.SetActive(true);
    }

    /// <summary>
    /// 隐藏自己
    /// </summary>
    public virtual void Hide()
    {
        gameObject.SetActive(false);
    }
}

UI管理器

using System.Collections.Generic;
using System.Resources;
using UnityEngine;
using UnityEngine.Events;

/// <summary>  
/// UI层级枚举  
/// </summary>
public enum UILayer_Enum
{
    Bottom, // 底层
    Middle, // 中间层
    Top, // 顶层
    System // 系统层
}

/// <summary>
/// UI管理器
/// </summary>
public class UIManager : SingletonBase<UIManager>
{
    /// <summary>
    /// 存储所有已经创建的UI面板的字典
    /// </summary>
    public Dictionary<string, UIPanelBase> panelDict = new Dictionary<string, UIPanelBase>();
    
    // UI层级的父级Transform
    /// <summary>
    /// 底层父物体
    /// </summary>
    private Transform _bottomLayer;
    /// <summary>
    /// 中层父物体
    /// </summary>
    private Transform _middleLayer;
    /// <summary> 
    /// 顶层父物体
    /// </summary>
    private Transform _topLayer;
    /// <summary>
    /// 系统层父物体 
    /// </summary>
    private Transform _systemLayer;
    
    /// <summary>
    /// 记录UI的Canvas父对象,方便外部使用
    /// </summary>
    public RectTransform canvas;

    public UIManager() {
        // 创建Canvas,场景切换时不销毁
        GameObject canvasObj = Resources.Load<GameObject>("UI/Canvas");
        canvas = canvasObj.transform as RectTransform;
        GameObject.DontDestroyOnLoad(canvasObj);
        
        //找到各层级
        _bottomLayer = canvas.Find("Bottom");
        _middleLayer = canvas.Find("Middle");
        _topLayer = canvas.Find("Top");
        _systemLayer = canvas.Find("System");
        
        //创建EventSystem,场景切换时不销毁
        GameObject eventSystem = Resources.Load<GameObject>("UI/EventSystem");
        GameObject.DontDestroyOnLoad(eventSystem);
    }

    /// <summary>
    /// 根据层级枚举获取对应层级的父物体
    /// </summary>
    /// <param name="layerEnum">层级</param>
    /// <returns>对应层级的父物体</returns>
    public Transform GetLayerFather(UILayer_Enum layerEnum) {
        switch (layerEnum)
        {
            case UILayer_Enum.Bottom:
                return _bottomLayer;

            case UILayer_Enum.Middle:
                return _middleLayer;

            case UILayer_Enum.Top:
                return _topLayer;

            case UILayer_Enum.System:
                return _systemLayer;
        }

        return null;
    }

    /// <summary>
    /// 显示/创建UI面板
    /// 传入面板名“panelName ',UI层级“layer`和一个可选的回调函数`callBack"。
    /// 如果已经加载过该面板,则直接显示面板;否则,异步加载面板预设体并显示。
    /// </summary>
    /// <typeparam name="T">面板脚本类型</typeparam>
    /// <param name="panelName">面板名称</param>
    /// <param name="layerEnum">显示在哪一层级</param> 
    /// <param name="callBack">面板预制体创建成功后的回调</param>
    public void ShowPanel<T>(string panelName, UILayer_Enum layerEnum = UILayer_Enum.Middle, UnityAction<T> callBack = null) where T : UIPanelBase 
    {
        // 如果已经存在该面板,则直接显示
        if (panelDict.ContainsKey(panelName))
        {
            // 处理面板创建完成后的回调逻辑
            panelDict[panelName].Show();
            callBack?.Invoke(panelDict[panelName] as T);
            return;
        }

        //异步加载面板预制体
        GameObject obj = Resources.Load<GameObject>("UI/" + panelName);

        
        // 在回调中设置新加载的面板
        // 设置父对象及其相对位置和大小
        Transform parent = GetLayerFather(layerEnum);
        obj.transform.SetParent(parent);
        obj.transform.localPosition = Vector3.zero;
        obj.transform.localScale = Vector3.one;
        (obj.transform as RectTransform).offsetMax = Vector2.zero;
        (obj.transform as RectTransform).offsetMin = Vector2.zero;
        
        // 得到预制体上的UIPanelBase脚本
        T panel = obj.GetComponent<T>();
        
        // 执行面板创建完成后的回调
        callBack?.Invoke(panel);
        
        // 显示面板
        panel.Show();
        
        // 存入面板字典
        panelDict.Add(panelName, panel);
    }


    /// <summary>
    /// 隐藏面板,根据传入的isDestroy判断是否销毁面板
    /// </summary>
    /// <param name="panelName">要隐藏的面板的名称</param>
    /// <param name="isDestroy">是否销毁面板</param>
    public void HidePanel(string panelName,bool isDestroy = true) {
        // 检查面板是否存在
        if (panelDict.ContainsKey(panelName))
        {
            // 调用面板的 HideMe() 方法进行隐藏
            panelDict[panelName].Hide();
            
            // 是否销毁面板
            if (isDestroy)
            {
                GameObject.Destroy(panelDict[panelName].gameObject);
                // 从字典中移除该面板
                panelDict.Remove(panelName);
            }
        }
    }

    /// <summary>
    /// 获取已显示的面板
    /// </summary>
    /// <typeparam name="T">面板脚本类型,必须是 UIPanelBase 的子类</typeparam>
    /// <param name="name">要获取的面板的名称</param>
    /// <returns>面板脚本,返回指定类型的 UIPanelBase 对象</returns>
    public T GetPanel<T>(string name) where T : UIPanelBase {
        // 检查面板是否存在
        if (panelDict.ContainsKey(name))
            // 如果面板存在,将其转换为 T 类型并返回
            return panelDict[name] as T;
        
        // 如果面板不存在,返回 null
        return null;
    }
}

AB包管理器

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

public class AssetBundleManager : SingletonMonoBase<AssetBundleManager>
{
    /// <summary>
    /// 存储所有加载过的资源包
    /// </summary>
    private Dictionary<string, AssetBundle> assetBundleDict = new Dictionary<string, AssetBundle>();


    /// <summary>
    /// 主资源包,只加载一次
    /// </summary>
    private AssetBundle _mainAssetBundle;
    /// <summary>
    /// 依赖配置文件
    /// </summary>
    private AssetBundleManifest _manifest;

    /// <summary>
    /// 资源包存放路径
    /// </summary>
    private string AssetBundlePath => Application.streamingAssetsPath + "/";

    /// <summary>
    /// 主资源包名称,根据平台不同而不同
    /// </summary>
    private string MainAssetBundleName
    {
        get
        {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else
            return "PC";
#endif
        }
    }

    #region 同步加载资源
    /// <summary>
    /// 同步加载资源,三种方法重载
    /// </summary>
    /// <param name="assetBundleName">资源包名称</param>
    /// <param name="resourceName">资源名称</param>
    public Object LoadResource(string assetBundleName, string resourceName) {
        LoadAssetBundle(assetBundleName);
        Object obj = assetBundleDict[assetBundleName].LoadAsset(resourceName);
        if (obj is GameObject)
            return Object.Instantiate(obj);
        else
            return obj;
    }

    public Object LoadResource(string assetBundleName, string resourceName, Type type) {
        LoadAssetBundle(assetBundleName);
        Object obj = assetBundleDict[assetBundleName].LoadAsset(resourceName, type);
        if (obj is GameObject)
            return Object.Instantiate(obj);
        else
            return obj;
    }

    public T LoadResource<T>(string assetBundleName, string resourceName) where T : Object {
        LoadAssetBundle(assetBundleName);
        T t = assetBundleDict[assetBundleName].LoadAsset<T>(resourceName);
        if (t is GameObject)
            return Object.Instantiate(t);
        else
            return t;
    }

    #endregion

    #region 异步加载资源
    /// <summary>
    /// 异步加载资源,三种方法重载 
    /// 这个函数用于异步加载资源,参数包括资源包名称、资源名称和回调函数。
    /// 它使用了协程并调用了另一个函数ReallyLoadResourceAsync来实现异步加载。
    /// 该函数使用StartCoroutine方法来开启一个协程,并传入了参数assetBundleName,resourceName和callback。
    /// </summary>
    /// <param name="assetBundleName">资源包名称</param>
    /// <param name="resourceName">资源名称</param>
    /// <param name="callback">回调函数</param>
    /// <returns></returns>
    public void LoadResourceAsync(string assetBundleName, string resourceName, Action<Object> callback) {
        // 调用私有方法 ReallyLoadResourceAsync 来完成异步加载资源的操作
        StartCoroutine(ReallyLoadResourceAsync(assetBundleName, resourceName, callback));
    }

    /// <summary>
    /// 用于实际的异步加载资源操作。它使用yield return StartCoroutine方法来等待资源包加载完成。
    /// 然后,它使用LoadAssetAsync方法异步加载资源,并等待请求完成。
    /// 最后,如果资源是GameObject,将其实例化并传递给回调函数,否则直接传递资源。
    /// </summary>
    /// <param name="assetBundleName">资源包名称</param>
    /// <param name="resourceName">资源名称</param>
    /// <param name="callback">回调函数</param>
    /// <returns></returns>
    private IEnumerator ReallyLoadResourceAsync(string assetBundleName, string resourceName, Action<Object> callback) {
        // 调用 LoadAssetBundleAsync 异步加载 AssetBundle
        yield return StartCoroutine(LoadAssetBundleAsync(assetBundleName));
        // 加载 AssetBundle 中的资源
        AssetBundleRequest request = assetBundleDict[assetBundleName].LoadAssetAsync(resourceName);
        // 等待资源加载完成
        yield return request;
        // 如果加载的资源是 GameObject,则实例化一个新的 GameObject,并传给回调函数
        // 否则直接将加载的资源传给回调函数
        if (request.asset is GameObject)
            callback(Object.Instantiate(request.asset));
        else
            callback(request.asset);
    }

    /// <summary>
    /// LoadResourceAsync的重载,接受一个类型参数
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <param name="resourceName"></param>
    /// <param name="type"></param>
    /// <param name="callback"></param>
    public void LoadResourceAsync(string assetBundleName, string resourceName, System.Type type,
        Action<Object> callback) {
        // 调用私有方法 ReallyLoadResourceAsync 来完成异步加载资源的操作
        StartCoroutine(ReallyLoadResourceAsync(assetBundleName, resourceName, type, callback));
    }

    /// <summary>
    /// ReallyLoadResourceAsync重载
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <param name="resourceName"></param>
    /// <param name="type"></param>
    /// <param name="callback"></param>
    /// <returns></returns>
    private IEnumerator ReallyLoadResourceAsync(string assetBundleName, string resourceName, System.Type type,
        Action<Object> callback) {
        // 调用 LoadAssetBundleAsync 异步加载 AssetBundle
        yield return StartCoroutine(LoadAssetBundleAsync(assetBundleName));
        // 加载 AssetBundle 中指定类型的资源
        AssetBundleRequest request = assetBundleDict[assetBundleName].LoadAssetAsync(resourceName, type);
        // 等待资源加载完成
        yield return request;
        // 如果加载的资源是 GameObject,则实例化一个新的 GameObject,并传给回调函数
        // 否则直接将加载的资源传给回调函数
        if (request.asset is GameObject)
            callback(Object.Instantiate(request.asset));
        else
            callback(request.asset);
    }

    /// <summary>
    /// LoadResourceAsync 重载
    /// 这个函数使用了泛型来加载资源,它还接受了类型参数。它也使用了协程,并调用了另一个函数ReallyLoadResourceAsync T。
    /// 该函数使用了StartCoroutine方法来开启一个协程,并传入了参数assetBundleName,resourceName和callback。
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <param name="resourceName"></param>
    /// <param name="callback"></param>
    /// <typeparam name="T"></typeparam>
    public void LoadResourceAsync<T>(string assetBundleName, string resourceName, Action<T> callback) where T : Object {
        // 调用私有方法 ReallyLoadResourceAsync 来完成异步加载资源的操作
        StartCoroutine(ReallyLoadResourceAsync<T>(assetBundleName, resourceName, callback));
    }

    /// <summary>
    /// ReallyLoadResourceAsync 泛型重载
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <param name="resourceName"></param>
    /// <param name="callback"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    private IEnumerator ReallyLoadResourceAsync<T>(string assetBundleName, string resourceName, Action<T> callback)
        where T : Object {
        // 调用 LoadAssetBundleAsync 异步加载 AssetBundle
        yield return StartCoroutine(LoadAssetBundleAsync(assetBundleName));
        // 加载 AssetBundle 中指定类型的资源
        AssetBundleRequest request = assetBundleDict[assetBundleName].LoadAssetAsync<T>(resourceName);
        // 等待资源加载完成
        yield return request;
        // 如果加载的资源是 GameObject,则实例化一个新的 GameObject,并传给回调函数
        // 否则直接将加载的资源传给回调函数
        if (request.asset is GameObject)
            callback(GameObject.Instantiate(request.asset) as T);
        else
            callback(request.asset as T);
    }

    #endregion

    /// <summary>
    /// 同步加载资源包
    /// </summary>
    /// <param name="assetBundleName">资源包名称</param>
    private void LoadAssetBundle(string assetBundleName) {
        if (_mainAssetBundle == null)
        {
            _mainAssetBundle = AssetBundle.LoadFromFile(AssetBundlePath + MainAssetBundleName);
            _manifest = _mainAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        string[] dependencies = _manifest.GetAllDependencies(assetBundleName);
        for (int i = 0; i < dependencies.Length; i++)
        {
            if (!assetBundleDict.ContainsKey(dependencies[i]))
            {
                assetBundleDict.Add(dependencies[i], AssetBundle.LoadFromFile(AssetBundlePath + dependencies[i]));
            }
        }

        if (!assetBundleDict.ContainsKey(assetBundleName))
        {
            assetBundleDict.Add(assetBundleName, AssetBundle.LoadFromFile(AssetBundlePath + assetBundleName));
        }
    }

    /// <summary>
    /// 异步加载资源包
    /// </summary>
    /// <param name="assetBundleName">资源包名称</param>
    /// <returns></returns>
    private IEnumerator LoadAssetBundleAsync(string assetBundleName) {
        if (_mainAssetBundle == null)
        {
            AssetBundleCreateRequest abCreateRequest =
                AssetBundle.LoadFromFileAsync(AssetBundlePath + MainAssetBundleName);
            yield return abCreateRequest;
            _mainAssetBundle = abCreateRequest.assetBundle;

            AssetBundleRequest abRequest = _mainAssetBundle.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
            yield return abRequest;
            _manifest = abRequest.asset as AssetBundleManifest;
        }

        string[] dependencies = _manifest.GetAllDependencies(assetBundleName);
        for (int i = 0; i < dependencies.Length; i++)
        {
            if (!assetBundleDict.ContainsKey(dependencies[i]))
            {
                AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(AssetBundlePath + dependencies[i]);
                yield return request;
                assetBundleDict.Add(dependencies[i], request.assetBundle);
            }
        }

        if (!assetBundleDict.ContainsKey(assetBundleName))
        {
            AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(AssetBundlePath + assetBundleName);
            yield return request;
            assetBundleDict.Add(assetBundleName, request.assetBundle);
        }
    }

    /// <summary>
    /// 卸载单个资源包
    /// </summary>
    /// <param name="assetBundleName">资源包名称</param>
    public void UnloadAssetBundle(string assetBundleName) {
        if (assetBundleDict.ContainsKey(assetBundleName))
        {
            assetBundleDict[assetBundleName].Unload(false);
            assetBundleDict.Remove(assetBundleName);
        }
    }

    /// <summary>
    /// 卸载所有资源包
    /// </summary>
    public void Clear() {
        AssetBundle.UnloadAllAssetBundles(false);
        assetBundleDict.Clear();
        _mainAssetBundle = null;
        _manifest = null;
    }
}

特效管理器

/// <summary>
/// 特效管理器,配合缓存池,将特效生成到目标位置,特效播放结束后收回到缓存池中
/// </summary>
public class EffectMgr1: MonoBehaviour
{
    /// <summary>
    /// 偏移位置,挂载到特效上设置即可
    /// </summary>
    public Vector2 offsetPos;
    
    private Animator _animator;
    
    private string _poolName = null;
    
    /// <summary>
    /// updateEvent
    /// </summary>
    public UnityAction updateHandler;
    
    protected virtual void Awake() {
        _animator = this.GetComponent<Animator>();
    }

    protected virtual void Update() {
        updateHandler?.Invoke();
        
        if (this._poolName is not null)
        {
            if (_animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1)
            {
                BufferPoolMgr.Instance.PutToPools(this._poolName, this.gameObject);
            }
        }
    }

    public virtual void Init(Vector2 targetPos) {
        this.transform.position = targetPos + offsetPos;
        this.gameObject.SetActive(true);
    }
    
    /// <summary>
    /// 指定特效生成的位置,并激活
    /// </summary>
    /// <param name="targetPos">目标位置</param>
    /// <param name="poolName">缓存池名称</param>
    public virtual void Init(Vector2 targetPos, string poolName) {
        this._poolName = poolName;
        this.transform.position = targetPos + offsetPos;
        this.gameObject.SetActive(true);
    }
}

音效管理器

   public class AudioManager : SingletonMonoBase<AudioManager>
    {
        // 音效依附对象
        private GameObject _audioSourceObj = null;
        // 音效字典
        private Dictionary<string, AudioSource> _audioDict = new();

        private void Start() {
            _audioSourceObj = this.gameObject;
        }

        /// <summary>
        /// 播放音效
        /// </summary>
        public void PlayAudio(string audioClipPath, string audioName, bool isLoop = false, float volume = 1, UnityAction<AudioSource> callBack = null)
        {
            if(_audioSourceObj == null)
            {
                _audioSourceObj = new GameObject();
                _audioSourceObj.name = "Sound";
            }

            if (_audioDict.TryGetValue(audioName, out var value))
            {
                value.Play();
                return;
            }
            
            // 资源加载并初始化
            var audioClip = AssetDatabase.LoadAssetAtPath<AudioClip>($"Assets/Arts/AudioClip/{audioClipPath}/{audioName}.wav");
            AudioSource audioSource = _audioSourceObj.AddComponent<AudioSource>();
            audioSource.clip = audioClip;
            audioSource.loop = isLoop;
            audioSource.volume = volume;
            audioSource.Play();
            _audioDict.Add(audioName, audioSource);
        }

        /// <summary>
        /// 改变音效声音大小
        /// </summary>
        /// <param name="audioName"></param>
        /// <param name="volume"></param>
        public void ChangeSoundValue(string audioName, float volume)
        {
            _audioDict[audioName].volume = volume;
        }

        /// <summary>
        /// 停止音效
        /// </summary>
        public void PauseAudio(string audioName)
        {
            if( _audioDict.TryGetValue(audioName, out var value) )
            {
                value.Stop();
            }
        }
    }


洗牌工具类

public class ShuffleQueue<T>
{
    //存储元素
    private Queue<T> _sequence;
    // 存储上次打乱顺序的元素
    private List<T> _lastShuffled;
    private Random _random;

    // 构造函数,接受一个 IEnumerable<T> 类型的参数来初始化队列
    public ShuffleQueue(IEnumerable<T> data) {
        _sequence = new Queue<T>(data);
        _lastShuffled = new List<T>();
        _random = new Random();
    }

    // 定义一个 GetNext 方法用于取出并删除队首元素
    public T GetNext() {
        // 如果队列为空,则调用 Shuffle 方法来打乱顺序并重新填充队列
        if (_sequence.Count == 0)
            Shuffle();
        // 取出并删除队首元素
        T next = _sequence.Dequeue();
        // 将取出的元素添加到上次打乱顺序的元素列表中
        _lastShuffled.Add(next);
        return next;
    }

    // 定义一个 Shuffle 方法用于打乱顺序并重新填充队列
    private void Shuffle() {
        // 获取上次打乱顺序的元素列表的元素个数
        int count = _lastShuffled.Count;
        // 获取上次打乱顺序的末尾元素
        T lastElement = _lastShuffled[count - 1];

        // 使用 Knuth-Durstenfeld Shuffle算法来随机打乱临时列表中的元素顺序
        do
        {
            for (int i = count - 1; i > 0; i--)
            {
                int randomIndex = _random.Next(0, count);
                // 使用元组进行析构交换元素
                (_lastShuffled[randomIndex], _lastShuffled[i]) = (_lastShuffled[i], _lastShuffled[randomIndex]);
            }
            // 检查随机打乱后的首位元素是否与上次打乱顺序的末尾元素相同,如果相同,则重新打乱顺序
        } while (_lastShuffled[0].Equals(lastElement));

        // 将临时列表中的元素依次添加到队列中
        foreach (T item in _lastShuffled)
        {
            _sequence.Enqueue(item);
        }

        // 清空上次打乱顺序的元素列表
        _lastShuffled.Clear();
    }
}

转载来源:Unity常用基础框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值