一、几种常见的单例模式基类及要点
(1)常规单例
1. 常规单例基类写法一
using UnityEngine;
public class NormalSingleton<T> where T : class,new()
{
private static T _single;
public static T Single
{
get
{
if (_single == null)
{
T t = new T();
if (t is MonoBehaviour)
{
Debug.LogError("请使用Mono单例基类");
return null;
}
_single = t;
}
return _single;
}
}
}
2. 常规单例基类写法二
using UnityEngine;
public class NormalSingleton1<T> where T : class,new()
{
private static T _instance;
public static T GetInstance()
{
if(_instance==null)
{
_instance = new T();
}
return _instance;
}
}
3. 总结
两种方式其实差不多,不过是用属性和方法的区别,个人偏向于写法一感觉属性更优雅些。
(2)Mono类单例
1. Mono类单例基类写法一
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T GetInstance()
{
return instance;
}
private void Awake()
{
instance = this as T;
}
}
2. Mono类单例基类写法二
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonoSingleton2<T> : MonoBehaviour where T:MonoBehaviour
{
private static T _single;
public static T Single
{
get
{
if(_single==null)
{
_single = FindObjectOfType<T>();
{
if(_single==null)
{
Debug.Log("场景中未找到类的对象,类名为:" + typeof(T).Name);
}
}
}
return _single;
}
}
private void Awake()
{
if(_single==null)
{
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
3. Mono类单例基类写法三
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonoSingleton3<T> : MonoBehaviour where T :MonoBehaviour,new()
{
private static T _single;
public static T GetSingle()
{
if(_single == null)
{
GameObject obj = new GameObject();
obj.name = typeof(T).Name;
DontDestroyOnLoad(obj);
_single = obj.AddComponent<T>();
}
return _single;
}
}
4. 总结
前两者均需要将脚本挂在物体上,写法三可以实现自动生成同名称的空物体,写法二和三均用到了反射的知识点,写法三逻辑清晰巧妙,
通过判断单例对象是否为空来决定是否启动不销毁函数逻辑,保证所有场景中仅有一个实例,而写法二是采用销毁的方式不够优雅,建议使用写法三。
(3)使用方式和好处
使用方式:让需要实现单例的类继承相应的单例模式基类。
好处: 通过继承的方式,省去对所需类实现单例的时间。
二、缓存池模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolData
{
public GameObject fatherObj;
public List<GameObject> poolList;
public static Transform gunPoint = GameObject.Find("gunPoint").transform;
public PoolData(GameObject obj,GameObject poolObj)
{
fatherObj = new GameObject(obj.name);
fatherObj.transform.parent = poolObj.transform;
poolList = new List<GameObject>() { };
PushObj(obj);
}
public void PushObj(GameObject obj)
{
poolList.Add(obj);
obj.transform.parent = fatherObj.transform;
obj.SetActive(false);
}
public GameObject GetObj()
{
GameObject obj = null;
obj = poolList[0];
poolList.RemoveAt(0);
obj.transform.parent = gunPoint;
obj.SetActive(true);
obj.transform.parent = null;
return obj;
}
}
public class PoolMgrPro : NormalSingleton<PoolMgrPro>
{
public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
public GameObject poolObj = null;
public GameObject GetObj(string name)
{
GameObject obj = null;
if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0)
{
obj = poolDic[name].GetObj();
}
else
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name),PoolData.gunPoint);
obj.name = name;
}
return obj;
}
public void PushObj(string name, GameObject obj)
{
if (poolObj == null)
{
poolObj = new GameObject("Pool");
}
if (poolDic.ContainsKey(name))
{
poolDic[name].PushObj(obj);
}
else
{
poolDic.Add(name, new PoolData(obj, poolObj));
}
}
public void Clear()
{
poolDic.Clear();
poolObj = null;
}
}
使用方式
特殊情境:针对子弹火花弹痕效果设计的缓存池,在对应预制体身上挂载延迟函数,在角色开火函数中调用取出对象池,
在预制体身上延迟调用存入对象池,也就是存回对应的List中,可以有效提高性能效率。