栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

设计模式之游戏--观察者模式详解

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计模式之游戏--观察者模式详解

引入问题

在我们的游戏开发中,我们常常会遇见这种场景,比如在战斗系统中如果敌人死亡,我们要通知UI系统,成就系统,关卡系统等一些其它系统做出相应的行为。你可能做出如下设计

方法1:让战斗系统亦或者敌人持有其它系统的的引用,然后在敌人死亡时调用其它系统的相应方法。
方法2:让各系统时时监听敌人的相应状态,敌人死亡时调用系统的相应方法

这两种方法看起来没多大毛病,但仔细分析我们就就会发现其实是有所欠缺的。
方法1:当我们后期需求变更,增加了一个新的系统或者一个新的功能,那么我们就需要修改战斗系统或者敌人的代码,使之符合我们的要求,但这显然不符合我们的开闭原则。
方法2:时时监听,在我们的Unity具体来说就是Update(),FixedUpdate()等函数,在这些函数写入过多的诸如此类的判断,很可能会引发性能瓶颈,常见的就是卡顿,掉帧。

为了解决上述诸如此类的问题,我们引入的观察者模式。

定义

它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象在状态变化时,会通知所有的观察者对象并使它们能够自动更新。所以说这种模式有时又称作发布-订阅模式。

UML

Subject:抽象主题(抽象被观察者),抽象主题对象把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供相应接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题(具体被观察者),它实现抽象目标中的通知方法,在具体主题的对应状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

具体示例:

下面我们以游戏中的敌人死亡后,通知各个系统做出相应响应为例, 实现观察者模式。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public abstract class GameEventSubjects
{
    protected List observers = new List();

    public virtual void AddOBS(GameEventObserver observer)
    {
        observers.Add(observer);
    }
    public virtual void RemoveOBS(GameEventObserver observer)
    {
        observers.Add(observer);
    }
    public abstract void Notify();
   
}

public class EnemyDeathSubject : GameEventSubjects
{
    public override void Notify()
    {
        foreach (var item in observers)
        {
            item.Update();
        }
    }
}



public abstract class GameEventObserver
{
    protected string system;
    public GameEventObserver(string system)
    {
        this.system = system;
    }

    public abstract void Update();

}

/// 
/// 关卡系统订阅者
/// 
public class EnemyDeathObs_StageSys : GameEventObserver
{
    public EnemyDeathObs_StageSys(string system) : base(system)
    {
    }
    public override void Update()
    {
        MonoBehaviour.print(system + "接收敌人死亡消息,调用"+ system+"的判断方法,是否进入下一关");
    }
}


/// 
/// UI系统订阅者
/// 
public class EnemyDeathObs_UISys : GameEventObserver
{
    public EnemyDeathObs_UISys(string system) : base(system)
    {
    }
    public override void Update()
    {
        MonoBehaviour.print(system + "接收敌人死亡消息,调用" + system + "的更新方法");
    }
}


/// 
/// 成就系统订阅者
/// 
public class EnemyDeathObs_ArchievementSys : GameEventObserver
{
    public EnemyDeathObs_ArchievementSys(string system) : base(system)
    {
    }
    public override void Update()
    {
        MonoBehaviour.print(system + "接收敌人死亡消息,调用" + system + "的记录方法");
    }
}

public class Client_0 : MonoBehaviour
{

    private void Start()
    {
        EnemyDeathSubject enemyDeathEvent = new EnemyDeathSubject();

        GameEventObserver enemyDeathObs_StageSys = new EnemyDeathObs_StageSys("关卡系统");
        GameEventObserver enemyDeathObs_UISys = new EnemyDeathObs_UISys("UI系统");
        GameEventObserver enemyDeathObs_ArchievementSys = new EnemyDeathObs_ArchievementSys("成就系统");
        enemyDeathEvent.AddOBS(enemyDeathObs_StageSys);
        enemyDeathEvent.AddOBS(enemyDeathObs_UISys);
        enemyDeathEvent.AddOBS(enemyDeathObs_ArchievementSys);
        enemyDeathEvent.Notify();
    }
}
优点:

1.降低了耦合度:通过观察我们发现,观察者和被观察者都是依赖于抽象 ,它们都通过抽象进行联系,复合我们的依赖倒置原则。
2.符合我们的开闭原则:当我们后期引进的新功能与主题状态变化有关时,只需要新建相应的观察者类。

缺点:

在顺序执行通知观察者时,也就是这个函数

  public override void Notify()
    {
        foreach (var item in observers)
        {
            item.Update();
        }
    }

那么一个观察者卡顿,会影响整体的执行效率,所以说在这种情况下,一般会采用异步实现。

使用场景:

1.跨系统的消息交换场景,如我们上面的那个示例相当于就相当于战斗系统和UI,成就,关卡等系统的消息交换。
2.一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。如我们上面的那个示例,UI敌人数量的显示,成就中累积杀敌数的更新,是否进入下一个关卡都依赖于敌人死亡这个事件。

在Unity中的使用

很常见的就是我们的UI事件的监听,比如我们的按钮, button.onClick.AddListener()这个方法就是为某个按钮添加我们的观察更新方法。

最后如果本文有什么不妥之处还望指出,谢谢!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/900685.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号