2015-05-08 14:54:51 leoleocs 阅读数 1432

单体模式(Singleton)是一种最基础的设计模式,在几乎所有的程序设计和实现的时候都会用到,单体的主要作用为:

  • 确保只有一个实例(对象)被创建,一般来讲的范围就是在一个进程当中。
  • 需要提供全局的范围点来访问实例

在实现的时候特别要主要线程安全性,在C++的实现中特别需要注意,C#来说静态变量的初始化都是线程安全性的,一般问题不大。

下面就介绍一个Unity3d 中的单体脚本的实现方法。Unity3d的脚本都需要继承自MonoBehvaior, 而且不提供构造函数,其构造都是由游戏引擎中的代码来实现,那如何能保证实例对象的唯一性呢? 其基本思想不是去控制构造,而是在构造后删除。 所以,我们可以在Awake函数中检查实例是否唯一,在Start函数中删除。基本的实现要点如下:

单体模板类

//基本的模本类,单体类继承它
public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    /// <summary>
    /// 集体的单体实例
    /// </summary>
    private static Singleton<T> instance;

    /// <summary>
    /// 是否需要销毁
    /// </summary>
    private bool isDestroyed;

    /// <summary>
    ///  销毁时,是销毁GameObject还是Component 
    /// </summary>
    protected bool DestroyGameObject { get; set; }

    /// <summary>
    /// 全局访问单体实例的访问点
    /// </summary>
    public static T Instance
    {
        get { return instance as T; }
    }
  }

Awake 函数的实现

//注意函数签名,需要声明为virtual,这个
protected virtual void Awake()
{
     //基本思想为, 如果实例已经构造出来,就将isDestroyed标记为true,将在Start函数中析构否则,将该对象赋值给全局的instance 
         if(instance != null && instance != this)
        {
             isDestroyed = true;
        }
        else
        {
            instance = this;
        }
        return instance == this;
}

Start函数的实现

    // 注意该函数也是virtual的
    protected virtual void Start()
    {
        if(isDestroyed) // 标记位
        {
            if(DestroyGameObject) // 销毁标记位,子类可以在Awake中设置它
            {
                Destroy(gameObject);//销毁gameobject
            }
            else
            {
                Destroy(this); // 销毁component
            }
        }
    }

对于Unity3d的线程安全性,大家可以不用担心,脚本的执行都是在同一个线程中运行的,所以没必要考虑线程的安全性和加锁。

2016-09-10 01:01:54 yangxuan0261 阅读数 1320
  • 在游戏中很多地方都用了全局唯一的单件,像什么技能管理器、buff管理器等等…
  • Google了一下不外乎都是在unity中 实例 一个对象gameobject,往这个gameobject身上挂上所有的Mgr组件,然后设置这个gameobject在切场景的时候不让销毁(DontDestroyOnLoad

ps: 有些人喜欢在一个总控制器里加上一个dictionary来保存所有的mgr,但是我想如果使用了unity的 组件模式 的话,直接就可以通过它的api GetComponent 来获取对应的mgr


1. 先来个基础的mgr- BaseMgr,让所有的mgr都继承它,相同的特性都写在里面

public class BaseMgr : MonoBehaviour {

    public virtual void Awake()
    {
        Debug.Log("BaseMgr.Awake");
    }

    public int mCounter = 0;

    public virtual void Print()
    {
        Debug.LogFormat("--- name:{0}, counter:{1}", this.GetType().Name, mCounter);
        ++mCounter;
    }

    public virtual void Clear()
    {
        Debug.LogFormat("--- name:{0}, Clear", this.GetType().Name);
    }
}

2. 具体的mgr- SkillMgrBuffMgr

public class SkillMgr : BaseMgr 
{
    public override void Awake()
    {
        //base.Awake();
        Debug.Log("--- SkillMgr.Awake");
    }
}
public class BuffMgr : BaseMgr
{
    public override void Awake()
    {
        //base.Awake();
        Debug.Log("--- BuffMgr.Awake");
    }
}

3. 最后是总控制器 GameMgr,同样也是继承 BaseMgr 挂在gameobject身上 ,但负责更多的职责

public class GameMgr : BaseMgr {

    public static string gNodeName = "GameMgrNode";
    private static GameObject gGameObj = null;
    private static GameMgr gGameMgr = null;

    /// <summary>
    /// 游戏开始时实例宿主对象gameobject,全局唯一,切场景不被销毁
    /// </summary>
    public static void Init()
    {
        GameObject obj = Resources.Load("Prefabs/GameMgrNode") as GameObject;
        obj = Instantiate(obj);
        obj.name = gNodeName;
        DontDestroyOnLoad(obj);
    }

    public static GameObject Obj
    {
        get { return gGameObj; }
    }

    public static GameMgr Ins
    {
        get { return gGameMgr; }
    }

    public override void Awake()
    {
        base.Awake();
        gGameObj = gameObject;
        gGameMgr = this;
    }

    public void Start()
    {
        Debug.Log("--- GameMgr.Start");
    }

    /// <summary>
    /// 可能要求清除顺序
    /// </summary>
    public override void Clear()
    {
        BaseMgr[] comps = gameObject.GetComponents<BaseMgr>();
        for (int i = 0; i< comps.Length; ++i)
        {
            if (!(comps[i] is GameMgr)) //跳过自己,最后请自己
            {
                comps[i].Clear();
            }
        }
        base.Clear();
    }

    public void Quit()
    {
        Debug.Log("--- GameMgr.Quit");
        Clear();

        DestroyObject(gGameObj);
    }
}

4、制作个空对象的预制件,把所有mgr都挂上去(当然你也可以代码动态挂上去)

这里写图片描述


5. 游戏开始的入口脚本,直接挂在摄像机上

public class Main : MonoBehaviour {

    // Use this for initialization
    void Start () {
        GameObject go = GameObject.Find(GameMgr.gNodeName);
        if (null == go)
        {
            GameMgr.Init();
            GameMgr.Ins.Start();
        }
    }
}

6、测试下

这里写图片描述

2018-05-06 14:59:15 weixin_33750452 阅读数 161

为了重构手头的一款项目,翻出来当时未接触Unity时候收藏的视频《Unity项目架构设计与开发管理》,对于我这种初学者来说全是干货。简单的总结了一下,以后慢慢提炼。

关于Unity的架构有如下几种常用的方式。

1.EmptyGO:

在Hierarchy上创建一个空的GameObject,然后挂上所有与GameObject无关的逻辑控制的脚本。使用GameObject.Find()访问对象数据。

缺点:逻辑代码散落在各处,不适合大型项目。

2.Simple GameManager:

所有与GameObject无关的逻辑都放在一个单例中。 缺点:单一文件过于庞大。

3.Manager Of Managers:

将不同的功能单独管理。如下:

  • MainManager: 作为入口管理器。
  • EventManager: 消息管理。
  • GUIManager: 图形视图管理。
  • AudioManager: 音效管理。
  • PoolManager: GameObject管理(减少动态开辟内存消耗,减少GC)。

实现一个简单的 PoolManager:

// 存储动可服用的GameObject。
private List<GameObject> dormantObjects = new List<GameObject>();  
// 在dormantObjects获取与go类型相同的GameObject,如果没有则new一个。
public GameObject Spawn(GameObject go)  
{
     GameObject temp = null;
     if (dormantObjects.Count > 0)
     {
          foreach (GameObject dob in dormantObjects)
          {
               if (dob.name == go.name)
               {
                    // Find an available GameObject
                    temp = dob;
                    dormantObjects.Remove(temp);
                    return temp;
               }
          }
     }
     // Now Instantiate a new GameObject.
     temp = GameObject.Instantialte(go) as GameObject;
     temp.name = go.name;
     return temp;
}

// 将用完的GameObject放入dormantObjects中
public void Despawn(GameObject go)  
{
     go.transform.parent = PoolManager.transform;
     go.SetActive(false);
     dormantObject.Add(go);
     Trim();
}

//FIFO 如果dormantObjects大于最大个数则将之前的GameObject都推出来。
public void Trim()  
{
     while (dormantObjects.Count > Capacity)
     {
          GameObject dob = dormantObjects[0];
          dormantObjects.RemoveAt(0);
          Destroy(dob);
     }
}
复制代码

缺点:

  • 不能管理prefabs。
  • 没有进行分类。

更好的实现方式是将一个PoolManager分成:

  • 若干个 SpawnPool。
    • 每个SpawnPool分成PrefabPool和PoolManager。
      • PrefabPool负责Prefab的加载和卸载。
      • PoolManager与之前的PoolMananger功能一样,负责GameObject的Spawn、Despawn和Trim。

要注意的是:

  • 每个SpawnPool是EmeptyGO。
  • 每个PoolManager管理两个List (Active,Deactive)。

讲了一堆,最后告诉有一个NB的插件叫PoolManager- -。

4.将View和Model之间增加一个媒介层。

MVCS:StrangeIOC插件。

MVVM:uFrame插件。

5. ECS(Entity Component Based System)

Unity 是基于 ECS,比较适合GamePlay模块使用。 还有比较有名的Entitas-CSharp

相关链接:

我的框架地址:https://github.com/liangxiegame/QFramework

教程源码:https://github.com/liangxiegame/QFramework/tree/master/Assets/HowToWriteUnityGameFramework/

QFramework&游戏框架搭建QQ交流群: 623597263

转载请注明地址:凉鞋的笔记http://liangxiegame.com/

微信公众号:liangxiegame

如果有帮助到您:

如果觉得本篇教程或者 QFramework 对您有帮助,不妨通过以下方式赞助笔者一下,鼓励笔者继续写出更多高质量的教程,也让更多的力量加入 QFramework 。

  • 给 QFramework 一个 Star:https://github.com/liangxiegame/QFramework
  • 下载 Asset Store 上的 QFramework 给个五星(如果有评论小的真是感激不尽):http://u3d.as/SJ9
  • 购买 gitchat 话题并给 5 星好评: http://gitbook.cn/gitchat/activity/5abc3f43bad4f418fb78ab77 (6 元,会员免费)
  • 购买同名的蛮牛视频课程并给 5 星好评:http://edu.manew.com/course/431 (目前定价 19 元,之后会涨价,课程会在 2018 年 6 月初结课)
  • 购买同名电子书 :https://www.kancloud.cn/liangxiegame/unity_framework_design( 29.9 元,内容会在 2018 年 10 月份完结)

笔者在这里保证 QFramework、入门教程、文档和此框架搭建系列的专栏永远免费开源。以上捐助产品的内容对于使用 QFramework 的使用来讲都不是必须的,所以大家不用担心,各位使用 QFramework 或者 阅读此专栏 已经是对笔者团队最大的支持了。

没有更多推荐了,返回首页