Unity小框架之单例模式基类

server/2025/3/18 17:01:44/

                单例模式(Singleton Pattern)是一种常用的创建型设计模式,其核心目标是确保一个类只有一个实例,并提供一个全局访问点。它常用于需要控制资源访问、共享配置或管理全局状态的场景(如数据库连接池、日志管理器、应用配置等)。        

单例模式的核心思想

  1. 私有构造函数:防止外部通过 new 创建多个实例。
  2. 静态私有实例:类内部持有唯一的实例。
  3. 全局访问方法:提供一个静态方法(如 getInstance())获取唯一实例。

        下面来介绍一下在C#和unity中实现的单例模式基类,你某些需要进行单例模式化的脚本,就可以继承这个基类然后就实现了自己的单例化,那你就可以在其他地方进行使用了。

一、最基本的单例基类

代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;//单例模式基类模块//1.C#泛型的知识
//2.设计模式中 单例模式的知识
public class BaseManager <T> where T : new()
{//单例模式private static T instance;public static T GetInstance(){if (instance == null){instance = new T();}return instance;}
}

使用方法:

例如下面这个脚本,我们创建了一个NewBehaviourScript的脚本,然后直接继承单例模式基类,如果其他地方需要调用,就直接使用就行

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class NewBehaviourScript : BaseManager<NewBehaviourScript>
{   void Start(){Debug.Log(NewBehaviourScript.GetInstance());}
}

再来一个示例:

// 子类继承 BaseManager,并满足 new() 约束
public class GameManager : BaseManager<GameManager>
{// 必须有一个公共无参构造函数public GameManager() {Debug.Log("GameManager Created");}public void Init(){Debug.Log("GameManager Initialized");}
}// 使用方式
void Start()
{
//可以在你项目中的任意一个地方进行使用GameManager manager = GameManager.GetInstance();manager.Init();// 问题:外部仍然可以 new GameManager(),破坏单例!GameManager another = new GameManager(); // 这是允许的 但是你自己选择可以不实现 后面我们还有保护措施 使得外部不能实例化
}

二、继承了Mono的单例模式基类

继承了Mono那么我们就可以使用Unity的生命周期函数了

代码:

public class SingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{private static T _instance;// 使用属性替代 GetInstance(),更符合 C# 习惯public static T Instance{get{// 如果实例不存在,尝试查找或创建if (_instance == null){_instance = FindObjectOfType<T>();// 如果场景中没有,自动创建一个新的 GameObjectif (_instance == null){GameObject obj = new GameObject(typeof(T).Name);_instance = obj.AddComponent<T>();}}return _instance;}}protected virtual void Awake(){// 如果实例已存在且不是当前对象,销毁自身if (_instance != null && _instance != this){Destroy(gameObject);return;}// 初始化实例_instance = this as T;// 按需设置跨场景保留DontDestroyOnLoad(gameObject); }
}

还有个简单的版本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;//C#泛型的知识
//设计模式中 单例模式的知识//继承了MonoBehaviour的 单例模式对象 需要我们自己保证它的唯一性
public class SingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{private static T instance;public static T GetInstance(){//继承了MonoBehaviour的类,不能直接new//只能通过拖动到对象上 或者通过加脚本的api AddComponent//U3d内部会帮助我们直接实例化return instance;}protected virtual void Awake(){instance = this as T;}
}

请注意:继承了这个单例模式基类的话,是不能够自己去new的你只能拖拽到物体身上。

示例:

这样改进是为了让我们在没有继承Mono的时候,仍然能使用生命周期函数

public class AudioManager : SingletonMono<AudioManager>
{public void PlaySound(string clipName){Debug.Log("Playing: " + clipName);}
}// 使用方式
void Start()
{AudioManager.Instance.PlaySound("BackgroundMusic");
}

示例:

using UnityEngine;// 继承 SingletonMono,并指定自身为泛型类型 T
public class SoundManager : SingletonMono<SoundManager>
{// 自定义音频方法public void PlaySound(string clipName){Debug.Log("播放音效: " + clipName);}// 初始化音频资源(在 Awake 中调用)protected override void Awake(){base.Awake(); // 调用基类的 Awake 方法,确保单例赋值Debug.Log("SoundManager 初始化完成");}
}

创建这样一个空物体,挂在脚本后,其他的类里面才能使用

使用:

public class PlayerController : MonoBehaviour
{private void Start(){// 获取 SoundManager 实例并调用方法SoundManager.Instance.PlaySound("跳跃音效");}private void Update(){// 直接通过 Instance 属性访问if (Input.GetKeyDown(KeyCode.Space)){SoundManager.Instance.PlaySound("射击音效");}}
}

三、继承了mono并且已经自己实例化的

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SingletonAutoMono<T> : MonoBehaviour where T : MonoBehaviour
{private static T instance;public static T GetInstance(){if (instance == null){GameObject obj = new GameObject();//设置对象的名字为脚本名字obj.name = typeof(T).ToString();//让这个单例模式对象过场景不移除//因为 单例模式对象 往往是存在于整个程序生命周期中的DontDestroyOnLoad(obj);instance = obj.AddComponent<T>();}return instance;}}

使用示例:

在继承了这个类的脚本里面直接使用内部的函数即可

public class NetworkManager : SingletonAutoMono<NetworkManager>
{public void Connect(string serverIP){Debug.Log($"连接到服务器: {serverIP}");}protected override void Awake(){base.Awake(); // 调用基类 Awake 确保单例初始化Debug.Log("网络管理器已初始化");}
}// 使用方式
void Start()
{NetworkManager.Instance.Connect("127.0.0.1");
}

注意事项

  1. 手动挂载与自动创建的冲突

    • 如果手动在场景中挂载脚本,需确保只有一个实例。
    • 优化后的代码会优先使用手动挂载的实例。
  2. 跨场景行为

    • 若需某个单例仅在特定场景存在,移除 DontDestroyOnLoad

http://www.ppmy.cn/server/176008.html

相关文章

(vue)elementUi中el-upload上传附件之后 点击附件可下载

(vue)elementUi中el-upload上传附件之后 点击附件可下载 handlePreview(file) {console.log(file)const fileUrl https://.../zzy/ file.urlconst a document.createElement(a)a.href fileUrla.download file.namea.style.display none// a.setAttribute(download, file.…

【k8s003】k8s与docker的依赖关系

‌一、早期版本对应关系&#xff08;Kubernetes 1.20 之前&#xff09;‌ ‌Kubernetes 1.13–1.19‌ ‌支持的 Docker 版本范围‌&#xff1a;1.13.1 至 19.03.x‌ ‌说明‌&#xff1a;此阶段 Kubernetes 直接依赖 Docker 作为默认容器运行时&#xff0c;需严格匹配版本以避免…

Linux中安装MySQL

检查是否有MySQL服务并卸载 检查并卸载 在安装MySQL数据库之前&#xff0c;我们需要先检查一下当前Linux系统中&#xff0c;是否安装的有MySQL的相关服务&#xff08;很多linux安装完毕之后&#xff0c;自带了低版本的mysql的依赖包&#xff09;&#xff0c;如果有&#xff0c…

【网络安全 | 漏洞挖掘】$15,000——通过持久token获取个人身份信息(PII)

未经许可,不得转载。 文章目录 绕侧攻击应用程序发现注册流程中的异常token调查token泄露Google Dorking 登场Wayback Machine 的作用影响分析绕侧攻击应用程序 某金融服务平台提供了测试凭据,允许直接登录测试环境。主应用程序包含数百个功能和端点,因此在测试过程中花费了…

课程分享 | 智能网联汽车网络安全测试框架

汽车智能化带来巨大网络安全风险 据公安部2024年1月统计&#xff0c;我国汽车保有量已达3.36亿辆&#xff0c;全国有94座城市汽车保有量超过100万辆。 与此同时&#xff0c;智能网联汽车的发展势头迅猛。2023年我国搭载组合驾驶辅助系统的智能网联乘用车新车销售约950万辆&…

鸿蒙路由 HMrouter 配置及使用一

1、学习链接 HMRouter地址 https://gitee.com/hadss/hmrouter/blob/dev/HMRouterLibrary/README.md 2、工程配置 下载安装 ohpm install hadss/hmrouter 添加编译插件配置 在工程目录下的build-profile.json5中&#xff0c;配置useNormalizedOHMUrl属性为true (我这项目创…

go语言学习教程推荐,零基础到做项目

一、基础入门阶段 官方教程&#xff08;免费&#xff09; • A Tour of Go&#xff1a;交互式入门教程&#xff0c;边学边练 • Go by Example&#xff1a;通过300代码片段学习语法 入门书籍 • &#x1f4d8;《Go语言圣经》中文版&#xff08;免费在线阅读&#xff09;&#…

笔记:代码随想录算法训练营day55:LeetCode42. 接雨水、84.柱状图中最大的矩形

学习资料&#xff1a;代码随想录 42. 接雨水 力扣题目链接 暴力解法超时了&#xff0c;直接从双指针开始 双指大概思路为创立两个数组记录两侧的最大值&#xff0c;这里的最大值是真正的最大的值&#xff0c;而不是最近的那个比较大的值&#xff0c;即所谓的按列计算&#…