❤ 一、前言
今天,同事问我:“在Unity中怎么用单例呀”
然后我就把我写的代码甩过去了。
同事:“哦,原来是这么用的,你来给我演示一下吧”
❤ 二、单例模式介绍
首先要了解,单例模式是一种常用的软件设计模式,定义是单例对象的类只能允许一个实例存在,在许多时候整个系统只需要拥有一个全局对象,有利于协调系统整体的行为。
比如,服务器程序,将配置信息存放到一个文件中,然后使用一个单例对象进行读取,其他服务进程中的其他对象再通过这个单例去获取这些配置信息,简化了配置管理。
单例模式的结构图如下图所示:
那么,单例模式在Unity中又有那些应用场景呢:
1、在使用Unity开发项目的时候,经常会遇到需要一个管理类来管理一些全局的变量和方法,比如GameManager用于记录各种需要在整个游戏中用到数据。
2、一些需要从外部文档读取的数据,在其他脚本对象也需要用到的使用,也可以使用单例对象进行读取,其他对象再通过这个单例对象去获取数据。
假设有以下需求:
1、整个项目中有且只有一个DataManager单例对象 2、在DataManager单例对象中,需要去读取文档的数据,并且保存下载 3、在切换场景的时候DataManager对象不能被销毁 4、在不同的脚本中可以读取到DataManager单例对象读取的值
下面我们就来一步步实现单例对象下的DataManager。
❤ 三、实现单例模式的DataManager
❤ 3-1、定义单例对象
我们定义一个DataManager对象,继承于MonoBehaviour,具体代码如下:
代码语言:javascript复制using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DataManager : MonoBehaviour
{
public static DataManager Instance;
private void Awake()
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
1、使用静态的DataManager属性Instance保证了它可以通过类进行访问,而不是通过实例化访问 2、继承MonoBehaviour类的实例是由Unity进行创建,不能通过构造函数创建 3、在Awake函数里面对Instance进行赋值,保证了这个属性可以第一时间初始化 4、使用DontDestroyOnLoad可以使这个挂载脚本的游戏对象在切换场景中也不会被销毁 5、DontDestroyOnLoad的参数使用(gameObject),这样切换场景中游戏对象不会被销毁,使用this只能保证当前脚本不会被销毁,但是对象销毁了,这个脚本也没有了。
创建完这个对象,在场景中也新建一个DataManager对象,将这个脚本拖到这个对象上:
❤ 3-2、单例对象去读取数据保存下来
代码如下:
代码语言:javascript复制using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class DataManager : MonoBehaviour
{
public static DataManager Instance;
string m_JsonContent;//临时文档数据
RootData m_JsonData;//临时接收JSON解析数据
//数据保存到这个List里面,其他脚本就可以调用到了
public List<StationAllInfo> m_StationsAllInfo = new List<StationAllInfo>();
private void Awake()
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
private void Start()
{
ReadJSONData();
}
void ReadJSONData()
{
string pathstations = Application.streamingAssetsPath "/地铁站.json";
using (StreamReader SR = new StreamReader(pathstations))
{
m_JsonContent = SR.ReadToEnd();
Debug.Log(m_JsonContent);
SR.Close();
SR.Dispose();
//保存JSON数据
m_JsonData = JsonUtility.FromJson<RootData>(m_JsonContent);
}
for (int i = 0; i < m_JsonData.GisJosnDatas.Count; i )
{
if (m_JsonData.GisJosnDatas[i].properties.LINE_ID != "")//剔除无用的信息,保存有用的信息
{
StationAllInfo item = new StationAllInfo();
item.X = m_JsonData.GisJosnDatas[i].properties.X;
item.Y = m_JsonData.GisJosnDatas[i].properties.Y;
item.STACODE = m_JsonData.GisJosnDatas[i].properties.STACODE;
item.S_NAME = m_JsonData.GisJosnDatas[i].properties.S_NAME;
item.LINE_ID = m_JsonData.GisJosnDatas[i].properties.LINE_ID;
//05 换成 5 (比如05号线 换成5号线)
if (m_JsonData.GisJosnDatas[i].properties.LINE_ID.Substring(0, 1) == "0")
{
item.LINE_NAME = m_JsonData.GisJosnDatas[i].properties.LINE_ID.Substring(1, 1) "号线";
}
else
{
switch (m_JsonData.GisJosnDatas[i].properties.LINE_ID)//对字母缩写的站名进行处理
{
case "fs":
item.LINE_NAME = "房山线";
break;
case "bt":
item.LINE_NAME = "八通线";
break;
case "yz":
item.LINE_NAME = "亦庄线";
break;
case "cp":
item.LINE_NAME = "昌平线";
break;
case "jc":
item.LINE_NAME = "机场线";
break;
default:
item.LINE_NAME = m_JsonData.GisJosnDatas[i].properties.LINE_ID "号线";
break;
}
}
item.STYPE = m_JsonData.GisJosnDatas[i].properties.STYPE;
m_StationsAllInfo.Add(item);
}
}
}
}
❤ 3-3、在其他脚本中使用数据
我们新建一个UseData.cs脚本去使用数据:
代码语言:javascript复制using UnityEngine;
public class UseData : MonoBehaviour
{
private void Update()
{
//点击键盘W 显示数据
if (Input.GetKeyDown(KeyCode.W))
{
ShowData();
}
}
private void ShowData()
{
for (int i = 0; i < DataManager.Instance.m_StationsAllInfo.Count; i )
{
Debug.Log(DataManager.Instance.m_StationsAllInfo[i].X " "
DataManager.Instance.m_StationsAllInfo[i].Y " "
DataManager.Instance.m_StationsAllInfo[i].STACODE " "
DataManager.Instance.m_StationsAllInfo[i].S_NAME " "
DataManager.Instance.m_StationsAllInfo[i].LINE_ID " "
DataManager.Instance.m_StationsAllInfo[i].LINE_NAME " "
DataManager.Instance.m_StationsAllInfo[i].STYPE " ");
}
}
}
将这个脚本拖到场景中的对象上:
运行程序,敲击键盘W:
所有的值,都从单例对象DataManager的InStance的m_StationsAllInfo数组中读取出来了。
❤ 3-4、切换场景读取数据
我们新建两个场景:Index和Next,然后Index场景中的对象上挂载脚本ChangeScenes.cs脚本,代码如下:
代码语言:javascript复制using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ChangeScenes : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
SceneManager.LoadScene(1);
}
}
}
也就是切换场景,然后在Index场景中新建一个DataManager对象,将DataManager脚本拖上去。
然后Next场景中随便找一个对象挂载UseData脚本,场景中不需要创建DataManager对象
然后将两个场景加到Build Setting中:
运行程序:
切换到Next场景,DataManager对象也存在:
敲击键盘W:
所有的值,都从上一个场景中的DataManager对象的挂载脚本的单例对象DataManager.cs的InStance的m_StationsAllInfo数组中读取出来了。
❤ 四、后言
这是Unity使用单例的简单应用,最主要的几个知识点是;
1、对象切换场景不销毁
2、单例对象的静态属性
3、数据的保存
4、其他脚本用单例对象的数据的方法
结束,好好学习加油吧!