Unity小框架之单例模式基类

news2025/3/18 5:02:42

                单例模式(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>();

                // 如果场景中没有,自动创建一个新的 GameObject
                if (_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.coloradmin.cn/o/2316986.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

cesium 实现万级管网数据渲染,及pickImageryLayerFeatures原生方法改写

需求背景解决效果getFeatureInfo 需求背景 在用 geoserver 渲染图层时&#xff0c;会自动触发 GetFeatureInfo &#xff0c;与服务器通信&#xff0c;在万级海量数据渲染下&#xff0c;这个性能消耗就可以感受到了 需要考虑的点&#xff1a; 1.通过enablePickFeatures&#xf…

基于金融产品深度学习推荐算法详解【附源码】

深度学习算法说明 1、简介 神经网络协同过滤模型(NCF) 为了解决启发式推荐算法的问题&#xff0c;基于神经网络的协同过滤算法诞生了&#xff0c;神经网络的协同过滤算法可以 通过将用户和物品的特征向量作为输入&#xff0c;来预测用户对新物品的评分&#xff0c;从而解决…

LVS + Keepalived 高可用集群

一、LVSKeepalived 原理 1.1.LVS 负载均衡原理 LVS&#xff08;Linux Virtual Server&#xff09;是一种基于 Linux 内核的负载均衡技术&#xff0c;它通过 IPVS&#xff08;IP Virtual Server&#xff09;模块来实现。LVS 可以将客户端的请求分发到多个后端服务器上&#xf…

PHP与数据库连接常见问题及解决办法

PHP与数据库连接常见问题及解决办法 在现代Web开发中&#xff0c;PHP与数据库的连接是不可或缺的一部分。无论是构建动态网站、内容管理系统&#xff08;CMS&#xff09;还是电子商务平台&#xff0c;PHP与数据库的交互都是核心功能之一。然而&#xff0c;在实际开发过程中&am…

HarmonyOS-应用程序框架基础

应用程序框架与应用模型的区别 应用框架可以看做是应用模型的一种实现方式&#xff0c;开发人员可以用应用模型来描述应用程序的结构和行为的描述&#xff0c;然后使用应用程序框架来实现这些描述。 应用模型 应用模型是一个应用程序的模型&#xff0c;它是一种抽象的描述&a…

使用 Doris 和 LakeSoul

作为一种全新的开放式的数据管理架构&#xff0c;湖仓一体&#xff08;Data Lakehouse&#xff09;融合了数据仓库的高性能、实时性以及数据湖的低成本、灵活性等优势&#xff0c;帮助用户更加便捷地满足各种数据处理分析的需求&#xff0c;在企业的大数据体系中已经得到越来越…

【C语言】函数和数组实践与应用:开发简单的扫雷游戏

【C语言】函数和数组实践与应用&#xff1a;开发简单的扫雷游戏 1.扫雷游戏分析和设计1.1扫雷游戏的功能说明&#xff08;游戏规则&#xff09;1.2游戏的分析与设计1.2.1游戏的分析1.2.2 文件结构设计 2. 代码实现2.1 game.h文件2.2 game.c文件2.3 test.c文件 3. 游戏运行效果4…

国内Mac,nimi安装homebrew完整过程

安装命令&#xff1a; 常规安装脚本&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 极速安装脚本&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.…

C++基础——从C语言快速入门

目录 输入输出 标准输出流 ( cout ) 标准输入流 ( cin ) 标准错误流 ( cerr ) 和标准日志流 ( clog ) 编程示例 基本变量类型 宽字符的用法 climits 如何使用 编程示例 注意事项 流程控制 条件语句 循环语句 跳转语句 函数 函数的基本结构 编程示例 函数的组成…

Windows远程桌面黑屏怎么办?

在使用Windows远程桌面连接另一台电脑时&#xff0c;用户经常会遇到Windows远程桌面黑屏的问题。那么&#xff0c;该如何有效地解决Windows远程桌面黑屏的问题呢&#xff1f;遇到远程桌面连接黑屏的问题时&#xff0c;可以通过在本地组策略编辑器中禁用WDDM图形显示驱动来解决。…

82.HarmonyOS NEXT 性能优化指南:从理论到实践

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT 性能优化指南&#xff1a;从理论到实践 文章目录 HarmonyOS NEXT 性能优化指南&#xff1a;从理论到实践1. 性能优化概述1.1 性能指…

python笔记2

变量&#xff1a;含义 一个容器&#xff0c;计算机当中的存储空间。 可以理解为一个用于标识或引用数据的名字或标签。 作用&#xff1a; 可以通过定义一个变量来给需要使用多次的数据命名&#xff0c;就像一个标签一样。下次需要使用这个数据时&#xff0c;只需要通过这个变…

深度学习 Deep Learning 第1章 深度学习简介

第1章 深度学习简介 概述 本章介绍人工智能&#xff08;AI&#xff09;和深度学习领域&#xff0c;讨论其历史发展、关键概念和应用。解释深度学习如何从早期的AI和机器学习方法演变而来&#xff0c;以及如何有效解决之前方法无法应对的挑战。 关键概念 1. 人工智能的演变 …

解决Windows版Redis无法远程连接的问题

&#x1f31f; 解决Windows版Redis无法远程连接的问题 在Windows系统下使用Redis时&#xff0c;很多用户会遇到无法远程连接的问题。尤其是在配置了Redis并尝试通过工具如RedisDesktopManager连接时&#xff0c;可能会报错“Cannot connect to ‘redisconnection’”。今天&am…

Qt C++ 常用压缩库推荐 快速压缩 解压缩数据

在Qt C中&#xff0c;如果你需要快速压缩和解压缩数据&#xff0c;可以使用以下几种库&#xff1a; 1. zlib 简介: zlib 是一个非常流行的压缩库&#xff0c;支持 DEFLATE 压缩算法。它被广泛用于各种应用程序中&#xff0c;包括Qt。 集成: Qt 本身已经集成了 zlib&#xff0…

架构师面试(十五):熔断设计

问题 某电商平台经常需要在大促运营活动中暂停评论、退款等业务&#xff0c;基于服务治理的设计理念&#xff0c;我们需要对该电商平台微服务系统的【服务熔断】进行设计&#xff0c;对此下面描述中说法正确的有哪几项呢&#xff1f; A. 服务管控系统管理着平台中所有服务之间…

Navicat如何查看密码

近期遇到需要将大部分已存储的navicat数据库转发给其他人&#xff0c;于是乎进行导出文件 奈何对方不用navicat&#xff0c;无法进行文件的导入从而导入链接 搜罗navicat的密码查看&#xff0c;大部分都为php代码解析 以下转载GitHub上看到的一个python代码解析的脚本 这里是对…

力扣143重排链表

143. 重排链表 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的…

HarmonyOS NEXT个人开发经验总结

文章目录 1. 开发环境配置1.1 工具链安装流程1.2 环境配置代码 2. 项目架构设计2.1 分层架构图2.2 模块化配置 3. 核心开发实践3.1 声明式UI开发3.2 分布式数据管理 4. 性能优化策略4.1 性能优化流程图4.2 优化实践代码 5. 安全与权限管理5.1 权限申请流程5.2 安全存储示例 6. …

golang快速上手基础语法

变量 第一种&#xff0c;指定变量类型&#xff0c;声明后若不赋值&#xff0c;使用默认值0 package mainimport "fmt"func main() {var a int //第一种&#xff0c;指定变量类型&#xff0c;声明后若不赋值&#xff0c;使用默认值0。fmt.Printf(" a %d\n"…