Unity常用基础框架

这篇具有很好参考价值的文章主要介绍了Unity常用基础框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

对常用的模块进行封装,实现部分通用基础框架。文章来源地址https://www.toymoban.com/news/detail-516311.html

单例基类

饿汉式单例基类

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常用基础框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用团结引擎开发Unity 3D射击游戏

           本案例是初级案例,意在引导想使用unity的初级开发者能较快的入门,体验unity开发的方便性和简易性能。       本次我们将使用团结引擎进行开发,帮助想体验团结引擎的入门开发者进行较快的环境熟悉。      本游戏是一个俯视角度的射击游戏。主角始终位于屏幕

    2024年01月19日
    浏览(62)
  • Unity、UE、Cocos游戏开发引擎的区别

    Unity、Unreal Engine(UE)和Cocos引擎是三个常用的游戏开发引擎,它们在功能和特性上有一些区别。以下是它们之间的主要区别: 编程语言:Unity使用C#作为主要的编程语言,开发者可以使用C#脚本进行游戏逻辑编写。Unreal Engine主要使用C++作为编程语言,但也支持蓝图系统,允许

    2024年02月22日
    浏览(53)
  • 【Unity 框架】QFramework v1.0 使用指南 工具篇:05. ResKit 资源管理&开发解决方案 | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏

    Res Kit,是资源管理快速开发解决方案 特性如下: 可以使用一个 API 从 dataPath、Resources、StreammingAssetPath、PersistentDataPath、网络等地方加载资源。 基于引用计数,简化资源加载和卸载。 拥抱游戏开发流程中的不同阶段 开发阶段不用打 AB 直接从 dataPath 加载。 测试阶段支持只需打

    2024年02月01日
    浏览(54)
  • Unity vs Godot :哪个游戏引擎更适合你?

    游戏引擎的选择对开发过程和最终产品质量有着重大影响。近年来,Godot和Unity这两款引擎受到广泛关注。本文将从多个维度对两者进行比较,以期为开发者提供正确的选择建议。 Godot和Unity都有各自的优势,没有绝对的好坏之分。Godot开源免费,上手简单,更适合2D和小型游戏

    2024年01月23日
    浏览(84)
  • 30分钟了解所有引擎组件,132个Unity 游戏引擎组件速通!【收藏 == 学会】

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2024年02月11日
    浏览(61)
  • Unity Physics2D 2d物理引擎游戏 笔记

    2d 材质 里面可以设置 摩擦力 和 弹力 Simulated:是否在当前的物理环境中模拟,取消勾选该框类似于Disable Rigidbody,但使用这个参数更加高效,因为Disable会销毁内部产生的GameObject,而取消勾选Simulated只是禁用。 Kinematic 动力学刚体 动力学刚体不受重力和力的影响,而受用户的

    2023年04月24日
    浏览(108)
  • GODOT游戏引擎简介,包含与unity性能对比测试,以及选型建议

    GODOT,是一个免费开源的3D引擎。本文以unity作对比,简述两者区别和选型建议。由于是很久以前写的ppt,技术原因视频和部分章节丢失了。建议当做业务参考。 GODOT目前为止遇到3个比较重大的机遇,第一个是oprea的合作奖,第二个是用支持c#换来的微软的投资,第三个是虚幻

    2024年02月14日
    浏览(55)
  • Unity和UE4两大游戏引擎,你该如何选择?

    目录 游戏引擎 2 —— 难易区别 编程语言 3 —— 游戏产品 UE4制作的游戏产品  Unity制作的游戏产品  产品类型 5 —— 资源商店 6 —— 人才需求 平均薪资 总结      Unity和UE4都是游戏引擎,所谓游戏引擎就是集成了复杂功能的游戏开发软件,他们帮我们实现了复杂的底层逻

    2023年04月08日
    浏览(59)
  • Unity 开发人员转CGE(castle Game engine)城堡游戏引擎指导手册

    一、简介 2. Unity相当于什么GameObject? 3. 如何设计一个由多种资产、生物等组成的关卡? 4. 在哪里放置特定角色的代码(例如生物、物品)?Unity 中“向 GameObject 添加 MonoBehaviour”相当于什么? 5.Unity子目录相当于什么Assets? 6. 支持哪些模型格式? 7. 支持FBX模型格式吗? 8.

    2024年02月07日
    浏览(72)
  • SuperMap Hi-Fi 3D SDK for Unity制作游戏引擎材质

    kele     在交通,电力,规划等行业中,有的对象常常具有很强的质感,比如金属质感的 钢轨,电力塔;陶瓷材质的绝缘子;玻璃材质的建筑幕墙等,但常规方式的表现效果 往往差强人意。     游戏引擎(Unity3D)中已有丰富的材质资源库,比如玻璃,金属等材质,这

    2024年02月09日
    浏览(80)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包