目录
学习内容
- 设计模式简介(Design Pattern)
- 单例模式的学习
- 单例模式的优点
学习笔记
设计模式简介(Design Pattern)
- 解决共通的问题
- 归纳相同的解决方案
- 类结构和组装方式
- 高复合度与组合使用
单例模式的学习
下面先以不使用单例模式的情况下为案例,创建一个玩家(Player)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player: MonoBehaviour
{
//取名
string name_ = "player";
//sayHello方法
public void sayHello()
{
Debug.LogError("I am" + name_);
}
}
再创建一个敌人(Enemy)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
//定义游戏体
GameObject player;
Player playerItem;
void Start()
{
//主角游戏体名假定为"Player"
player = GameObject.Find("Player");
playerItem = player.GetComponent<Player>();
}
void Update()
{
Debug.Log(player.transform.position);
//Update实时更新获取组件的消耗过大
//player.GetComponent<Player>().sayHello;
playerItem.sayHello();
}
}
但是我们会发现,
假设我们有多个需要Find我们的主角的游戏体
,那我们在每个脚本中都会定义类似的变量 ,甚至相同的方法。这样会增加我们的工作量,同时代码冗余大,且在后期的维护中会造成不便 。
那我们现在使用三种
单例模式
,来完成这个案例
第一种方式
玩家(Player)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///<summary>
///
///<summary>
public class Player : MonoBehaviour
{
//定义主角类的一个单实例
public static Player instance;
//取名
string name_ = "player";
private void Awake()
{
instance = this;
}
//sayHello方法
public void sayHello()
{
Debug.LogError("I am" + name_);
}
}
敌人(Enemy)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
//定义游戏体
//GameObject player;
//Player playerItem;
void Start()
{
//主角游戏体名假定为"Player"
//player = GameObject.Find("Player");
//playerItem = player.GetComponent<Player>();
}
void Update()
{
Debug.Log(Player.instance.transform.position);
//Update实时更新获取组件的消耗过大
//player.GetComponent<Player>().sayHello;
Player.instance.sayHello();
}
}
这样是不是节省了很多代码 ଘ(੭ˊᵕˋ)੭ !
但是它也是并不是完美的。
假设我们的游戏中有多个单例
,由于在多个单实例的情况下,我们对其进行初始化,但我们不知道哪个Awake会被先执行(不过可以通过编辑器进行设置),所以获取属性可能会出现问题。
第二种方式
玩家(Player)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///<summary>
///
///<summary>
public class Player : MonoBehaviour
{
//定义主角类的一个单实例
static Player instance;
//Instance作为属性
public static Player Instance
{
get
{
if (instance == null)
{
//创建该类
instance = FindObjectOfType<Player>();
}
FindObjectOfType没有找到该类的情况
//if (instance == null)
//{
// GameObject obj = new GameObject();
// obj.AddComponent<Player>();
// //下面两种都行
// //instance = FindObjectOfType<Player>();
// instance = obj.GetComponent<Player>();
//}
return instance;
}
}
//取名
string name_ = "player";
private void Awake()
{
//instance = this;
}
//sayHello方法
public void sayHello()
{
Debug.LogError("I am" + name_);
}
}
将原先的instance私有化,用新定义的属性Instance来获取以及调用。
敌人(Enemy)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
//定义游戏体
//GameObject player;
//Player playerItem;
void Start()
{
//主角游戏体名假定为"Player"
//player = GameObject.Find("Player");
//playerItem = player.GetComponent<Player>();
}
void Update()
{
//Debug.Log(Player.instance.transform.position);
Update实时更新获取组件的消耗过大
player.GetComponent<Player>().sayHello;
//Player.instance.sayHello();
Debug.Log(Player.Instance.transform.position);
Player.Instance.sayHello();
}
}
只要将instance替换成Instance即可
这样就不会像第一种方式那样,去担心调用的时候是否以及被实例化了 。
第三种方式
创建一个
单例模板
SingleTon<T>类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///<summary>
///
///<summary>
public class SingleTon<T> : MonoBehaviour where T:SingleTon<T>
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType(typeof(T)) as T;
if (_instance == null)
{
GameObject obj = new GameObject();
//隐藏实例化的new game object
//obj.hideFlags = HideFlags.HideAndDontSave;
_instance = obj.AddComponent<T>();
}
}
return _instance;
}
}
}
通过
泛型
做到一个模板类里,这样就不需要再之后其他的类中重新写
obj.hideFlags = HideFlags.HideAndDontSave;
开启时,实例会隐藏,且切换场景实例会消失不会保留。
修改玩家(Player)脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///<summary>
///
///<summary>
public class Player : SingleTon<Player>
{
定义主角类的一个单实例
//static Player instance;
Instance作为属性
//public static Player Instance
//{
// get
// {
// if (instance == null)
// {
// //创建该类
// instance = FindObjectOfType<Player>();
// }
// FindObjectOfType没有找到该类的情况
// //if (instance == null)
// //{
// // GameObject obj = new GameObject();
// // obj.AddComponent<Player>();
// // //下面两种都行
// // //instance = FindObjectOfType<Player>();
// // instance = obj.GetComponent<Player>();
// //}
// return instance;
// }
//}
//取名
string name_ = "player";
private void Awake()
{
//instance = this;
}
//sayHello方法
public void sayHello()
{
Debug.LogError("I am" + name_);
}
}
此时Player里面的Instance已经继承了父类,故不需要再次创建,也可以在敌人(Enemy)脚本被调用。
单例模式的优点
- 同时间只存在一个对象
- 快速获取对象的方法
- 适合游戏中单一功能的管理器