观察者模式

在对象间定义一种一对多的依赖关系,以便当某对象的状态发生改变时,与它存在依赖关系的所有对象都能收到通知并自动进行更新。

1、举例

在整个游戏系统中,有许许多多负责不同功能的系统组成,例如物理系统、声音系统等等组成。现在我们考虑这样一件事情,在碰撞到树5次后,达成成就。那么我们应该怎么完成这样一件事情呢?
在物理引擎碰撞器的代码里,可以加上关于成就系统的代码,但是这样真的好吗?将物理系统和成就系统耦合在一起,这不是明智的选择。万一以后成就系统业务逻辑发生改变,我们还要到物理系统里修改关于成就的代码,这无疑给系统维护带来了极大的不便,我们不愿看到这样的事情发生,因此观察者模式就派上用场了。

2、观察者模式简述

在我看来,通俗理解就是,有一个被观察者A,有许多观察者B、C……,观察者们时时刻刻的观者着A的举动,当A发出某个消息时,B、C收到A发出的消息,并根据A所发出的消息,做出相应的举动。这大概就是观察者模式,最简单的理解了。

3、在Unity中设计一个观察者模式

3.1C#中的委托和事件

网上已经有了许多关于委托和事件的解释,在这里就不赘述了,给出链接事件和委托

3.2、在Unity中实现

3.2.1、EventArgs

首先我们定义一个结构体,用来存放事件的参数,参数是这个事件具有的信息。例如在本例中,我让这个结构体里存放lifenum这两个变量。这个结构体定义在外部,这样可以确保别的别的类也可以访问到。
这个结构体代码如下:

public struct GameEventArgs
{
    public float life;
    public float num;
}

3.2.2、定义一个委托

public delegate void GameEventHandler(object sender, GameEventArgs e);

3.2.3、主类的实现

public class GameEvents : MonoBehaviour {
    public float life = 0;
    public float num = 0;

    public event GameEventHandler KeyCodeADown;
    public virtual void OnKeyCodeADown(GameEventArgs e)
    {
        if (KeyCodeADown != null)
        {
            KeyCodeADown(this, e);
        }
    }
    private GameEventArgs setGameEventArgs()
    {
        GameEventArgs e;
        e.life = life;
        e.num = num;
        return e;
    }
    
    void Update () {
        life += Time.deltaTime;
        num += Time.deltaTime * 2;

        if(Input.GetKeyDown(KeyCode.A))
        {
            OnKeyCodeADown(setGameEventArgs());
        }

    }
}
  • 定义一个事件

public event GameEventHandler KeyCodeADown;

这个事件发出消息告诉所有的观察者们,A键被按下了。

  • 定义触发事件的函数

 public virtual void OnKeyCodeADown(GameEventArgs e)
    {
        if (KeyCodeADown != null)
        {
            KeyCodeADown(this, e);
        }
    }

判断事件是否为空,如果不为空,则会发出这个事件,在Unity中,假如这个事件没有人监听,如果发送这个事件,会发生null的错误,所以要判断一下再发送。

  • 定义设置eventArgs的函数

private GameEventArgs setGameEventArgs()
    {
        GameEventArgs e;
        e.life = life;
        e.num = num;
        return e;
    }

这个函数会设置事件触发时的,所要传递的参数,例如在本例中设置了life和num

  • 定义每一帧的逻辑

void Update () {
        life += Time.deltaTime;
        num += Time.deltaTime * 2;
        if(Input.GetKeyDown(KeyCode.A))
        {
            OnKeyCodeADown(setGameEventArgs());
        }
    }

life和num在跟着时间发生改变,当按下A键的时候,调用 OnKeyCodeADown函数来触发事件,并调用setGameEventArgs()函数来设置事件参数。

3.2.4、定义观察者

简单的定义一个观察者,当收到消息时,打印信息,大码如下

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

public class Info : MonoBehaviour {
    public GameEvents gameEvents;
    // Use this for initialization
    void Start () {
        gameEvents.KeyCodeADown += CodeADown;
    }
    
    private void CodeADown(object sender,GameEventArgs e)
    {
        Debug.Log("Code A is pressed!");
        Debug.Log("life: " + e.life);
        Debug.Log("num: " + e.num);
    }
}

注意CodeADown函数的参数要和定义的委托的参数一致。

4、测试

开始游戏,按下A键成功打印出信息


测试.JPG

5、总结

基本在游戏使用的套路:

public struct XXXEventArgs{}
public delegate void XXXEventHandler(object sender, XXXEventArgs e);
public virtual void OnXXX(XXXEventArgs e){}
private XXXEventArgs setXXXEventArgs(){}

 if(触发事件的条件)
        {
            OnXXX(setXXXEventArgs());
        }

你可能感兴趣的:(观察者模式)