首先简单的说一下什么是单例,单例就是在整个程序的运行期间,存在且只存在一个的管理者,如你制作一个售卖系统,关于交易资金,你是希望有多个部分模块来管理这一个功能还是说仅通过一个交易单例来同意管理呢,如在制作游戏中,你是希望有多个敌人的管理者还是说,不管什么类型的敌人,全部都交给一个EnemyManager来生成与销毁呢?,那么整个交易单例和EnemyManager就是前面提到的单例,所谓的单例模式也就是使用了单例的情况,比如你使用单例来制作了EnemyManager ,那么实际上你就已经在使用单例模式了!
使用单例模式需要注意的问题,上面已经说过,在整个程序的运行期间,都要保持唯一行,那么如何保持唯一性就成了重中之重, 先上代码
using UnityEngine;
public class SingletonComponent2<T> : MonoBehaviour where T:SingletonComponent2<T> // 这个 类型 where T 是一个基类约束,什么意思呢,当
//使用SingletonComponent 的时候,其泛型的类型基类必须是 Monobehaviour ,这个约束必须在所有约束的前面
{
//1.首先明确,在一个游戏的运行期间,最好只有一个事件通知系统,它负责所有事件的注册,分发,处理,以及删除,依据这个理由,
//最好做成单例,只有当游戏释放的时候才销毁他
//2. 又因为,注册这个的类型可能多种多样,因此使用泛型来代替
//实现单例优先要一个静态的protect、private的字段 ,,一般来说以instance命名 为什么要是静态的是因为静态的属性或字段,只有在程序刚创建的时候创建一次
private static T __Instance2;
private bool _alive = true;
//还需要有一个静态的受保护的属性获取方法,这里不能是private的,因为需要被继承的子类所用
protected static SingletonComponent2<T> _Instance2
{
get
{
//首先要判断这个单例的对象是否已经被实现
if(!__Instance2)
{
T[] managers = GameObject.FindObjectOfType(typeof(T)) as T[];
if (managers != null)
{
if (managers.Length == 1) //说明当前的这个管理者已经存在了
{
__Instance2 = managers[0];
return __Instance2;
}
else if (managers.Length > 1)
{
Debug.Log("在场景中,单例应该只存在一个");
for (int i = 0; i < managers.Length; i++)
{
T manager = managers[i];
Destroy(manager);
}
}
}
GameObject go = new GameObject(typeof(T).Name, typeof(T));
__Instance2 = go.GetComponent<T>();
DontDestroyOnLoad(__Instance2.gameObject);
}
return __Instance2;
}
set
{
__Instance2 = value as T;
}
}
public static bool IsAlive
{
get
{
if (__Instance2 == null)
return false;
return __Instance2._alive;
}
}
}
首先为什么要使用泛型,在一个程序中,可能有各种各样的管理者,如,在游戏追踪,可能有BagManager,可能有BagData ,这些都是在整个游戏的运行期间希望其一直存在且只存在一份的,所以需要用泛型来匹配不同的类型
另外在这里面还可能涉及到多线程的问题,所以还可加锁来保证单例的线程安全
使用方法,上面只是一个基类,实际上我们真正使用的并不是这个基类,而是他的子类 ,代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyManagerSingletonComponent2 : SingletonComponent2<EnemyManagerSingletonComponent2>
{
public static EnemyManagerSingletonComponent2 Intance
{
get
{
return (EnemyManagerSingletonComponent2)_Instance2; //这里面的都是其父类的字段
}
set
{
_Instance2 = value;
}
}
private bool _alive = true;
private void OnDestroy()
{
_alive = false;
}
private void OnApplicationQuit()
{
_alive = false;
}
public static bool Alive
{
get
{
return Intance._alive;
}
}
List<GameObject> enemys = new List<GameObject>();
public void CreatEnemy2(GameObject prefab,Transform a)
{
string[] names = { "tom", "Dick" };
GameObject go = GameObject.Instantiate(prefab,a);
go.name = names[Random.RandomRange(0, names.Length)];
enemys.Add(go);
Debug.Log("创建");
}
public void KillEnemys()
{
for(int i = 0; i < enemys.Count;i++)
{
GameObject.Destroy(enemys[i]);
enemys.RemoveAt(i);
Debug.Log("销毁");
}
}
}
alive属性是为了防止当游戏退出的时候,单例如果已经呗销毁,在使用类里面又调用了单例的某些方法,这样可能会导致单例的数据没有清理,存在残留。
测试代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public GameObject go;
public GameObject parent;
// Start is called before the first frame update
void Start()
{
EnemyManagerSingletonComponent2.Intance.CreatEnemy2(go, parent.transform);
}
// Update is called once per frame
private void OnDestroy()
{
if (EnemyManagerSingletonComponent2.Alive)
{
EnemyManagerSingletonComponent2.Intance.KillEnemys();
Debug.Log("消除");
}
}
}
当使用EnemyManagerSingletonComponent2.Intance.CreatEnemy2(go, parent.transform); 的时候,实际上已经将EnemyManagerSingletonComponent2充当泛型的类型传递到基类里面去,那么这个时候EnemyManagerSingletonComponent2.Intance 就已经代表了在游戏中唯一存在的这个敌人管理者。
只是为了记录一下学习经历,