游戏开发中的工具类之单例模式的复用

           U3D游戏开发----工具类之单例模式的复用

1.我理解的单例模式

    在游戏中,我们在很多地方,比如多个类或者说是子系统中需要调用某个类的方法,但是这个类不是静态类,我们怎么调用呢?我们是不是每次都得去创建这个类的实例化对象,new这个类呢?但是如果我们这么做了,这是不是相当于增加了cpu的内存开销,如果说我们这个类相当于。Net里面的工具箱的话,我们每次去用这个工具箱的时候都去new这个工具箱,那么界面上就会出现好多窗口,这样对cpu来说造成了极大的内存浪费,我们只需要这个工具箱在界面上只出现一次就够了,是不是?但是怎么去确保每次创建的这个对象是同一个呢?单例模式应运而生,单例模式就是天生用来解决这种问题的,单例模式对这个问题的解决方法是:强迫通过这个类来访问,这个类中存储有一个静态内部对象。通过单例模式解决了两个基本的问题:全局访问和实例化控制。公共静态实例为访问实例提供了一个全局的访问点。

2.单例模式的分类

    通过学习前人的总结,单例模式呢,分为饿汉式单例和懒汉式单例。下面呢我分别把饿汉式单例和懒汉式单例写一下:

   饿汉式单例:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class Singleton :MonoBehaviour {

    //因为使用的静态,而静态成员变量呢是优先于对象存在的,

    //所以它是在这个类加载时就占用了内存的

    private static Singleton m_instance = new Singleton();

 

    public static Singleton Instance

    {

        get {return m_instance; }

    }

}   

懒汉式单例:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class Singleton :MonoBehaviour {

   

    private static Singleton m_instance = null;

 

    public static Singleton Instance

    {

        get {

            //如果当前对象为null,重新new 一个对象

            if(m_instance==null)

            {return m_instance =new Singleton(); }

            return m_instance;

        }

    }

 

    private void Awake()

    {

        //初始化把该类的引用给m_instance

        m_instance =this;

    }

}

 

3.饿汉式单例和懒汉式单例有什么区别呢?

   由于饿汉式单例,也就是静态初始化的的方式,它是类一加载就实例化的对象,所以会提前占用系统资源,而懒汉式单例,是指要在第一次被引用时,才会将自己实例化,所以具体使用哪一种,取决于自己项目中的实际需求。

5.在游戏开发中呢,哪些地方会用到单例模式呢?

单例模式呢,通常适用于你通常会采用全局变量或指向单个类实例的对象,通常趋向于管理员类型的类,这个类只需要一个实例,可以成为单例类型的类有:管理应用程序声音的类,管理资源加载的类,管理应用程序界面的类等。

那么在很多地方都需要用到单例,而它的模式都差不多,每次都得重新把逻辑写一遍,是否复用的一种呢?为此我写了一个单例的泛型类,应用于unity中游戏组件的管理类。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public abstract class MonoSingleton <T>:MonoBehaviour where T:MonoSingleton<T>{

    //预设一个实例的引用,用来保持对本类对象唯一的引用

    private static T m_instace=null;

    //创建对象的接口--全局创建点,在项目中的任何一个地方用到该类型的对象时调用 MonoSingle<T>().Instance

    public static T Instance

    {

        get{

            if (m_instace == null)

            {

                //在场景中查找是否有该类型对象

                m_instace = FindObjectOfType(typeof(T))as T;

                if (m_instace == null)

                {

                    //创建一个空物体,并挂载该对象,然后从物体上取得该对象

                    m_instace =new GameObject("SingletonOf" +typeof(T).ToString(),typeof(T)).GetComponent<T>();

                    m_instace.Init();

                }

            }

            return m_instace;

        }

    }

 

    private void Awake()

    {

        if(m_instace==null)

        { m_instace =this as T; }

    }

    //如果本对象在使用之前需要做一个初始化操作,请在子类中重写

    public virtual void Init() { }

    //应用结束时将本对象设为null,以便垃圾回收来释放

    private void OnApplicationQuit()

    {

        m_instace =null;

    }

}

那么这个类怎么使用呢?比如我写的AudioManager只需要继承MonoSingleton<自己的类名>就可以了,省去了写重复代码的时间。

 

public class AudioManager :MonoSingleton<AudioManager> {

 

    private AudioSource bgMusicSource;

 

    private Dictionary<AudioType,string> audioDic;

 

    public override void Init()

    {

        if (bgMusicSource == null)

        {

            bgMusicSource =this.gameObject.AddComponent<AudioSource>();

        }

        audioDic =new Dictionary<AudioType,string>();

        AddAudioPath(AudioType.BGM,"BGM");

        AddAudioPath(AudioType.BossAttack,"BossAttack");

        AddAudioPath(AudioType.BossDeath,"BossDeath");

        AddAudioPath(AudioType.ButtonClick,"ButtonClick");

        AddAudioPath(AudioType.GameOver,"GameOver");

        AddAudioPath(AudioType.GameVictory,"GameVictory");

        AddAudioPath(AudioType.ItemPickup,"ItemPickup");

        AddAudioPath(AudioType.MonterDeath,"MonterDeath");

        AddAudioPath(AudioType.PlayerGunShot,"PlayerGunShot");

        AddAudioPath(AudioType.PlayerSwordSwing,"PlayerSwordSwing");

        AddAudioPath(AudioType.Thunderstorm,"Thunderstorm");

    }

 

    private void AddAudioPath(AudioType audioType,string path)

    {

       if(!audioDic.ContainsKey(audioType))

        { audioDic.Add(audioType,"Sound/"+path); }

    }

 

    private void RemoveAudioPath(AudioType audioType)

    {

        if(audioDic.ContainsKey(audioType))

        {

            audioDic.Remove(audioType);

        }

    }

 

    private string GetOutputPath(AudioType audioType)

    {

        string path;

        audioDic.TryGetValue(audioType,out path);

        if(path!=null)

        {

            return path;

        }

        else

        {

            Debug.Log("不存在此背景音乐");

        return null;

        }

    }

 

    public void PlayBgMusic(AudioType audioType)

    {

        string path = GetOutputPath(audioType);

        AudioClip clip=Resources.Load(path)as AudioClip;

        Debug.Log(clip);

        bgMusicSource.clip = clip;

        bgMusicSource.Play();

        bgMusicSource.volume = 0.8f;

    }

 

    public void PlayAudio(AudioType audioType)

    {

        string path = GetOutputPath(audioType);

        AudioClip clip = Resources.Load<AudioClip>(path);

        AudioSource.PlayClipAtPoint(clip,this.transform.position, 0.8f);

    }

}

6.那么除了Unity C#继承MonoBehaviour的类可以有单例,普通类也有一种单例模式,如下:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using System;

/// <summary>

/// 所有单例的基类

/// </summary>

public class Singleton<T>:IDisposable where T:new(){

 

    private static T instance;

 

    public static T Instance

    {

        get

        {

            if (instance == null)

            { instance = new T(); }

            return instance;

        }

    }

 

    public virtual void Dispose()

    {

        

    }

}


 

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页