MMO_3_观察者设计模式_3_底层脚本和实例应用

前言:“在对象间定义一种一对多的依赖关系,以便当某对象的状态改变时,与它存在依赖关系的所有对象都能够接收到通知并自动进行更新。”----------摘自《游戏编程模式》。

图示观察者模式:
MMO_3_观察者设计模式_3_底层脚本和实例应用_第1张图片

一、创建Singleton.cs脚本。

因为我们的观察者模式将会用到单例,所以创建Singleton.cs脚本,实现单例泛型。
因为Unity是单线程,所以不必加锁,代码如下:

public class Singleton where T : class, new()
{
    private static T _instance;

    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new T();
            }
            return _instance;
        }
    }
}

二、创建EventDispatcher.cs脚本。

1.首先我们需要定义委托public delegate void OnEventHandler(params object[] param);
2.定义一个字典private Dictionary m_Dic = new Dictionary();
3.分别创建AddEventListener(string key, OnEventHandler handler)、RemoveEventListener(string key, OnEventHandler handler)、Dispatch(string key, params object[] param)方法。依次为:添加监听、移出监听、派发消息。
4.代码如下:

using System.Collections.Generic;

public class EventDispatcher:Singleton 
{
    public delegate void OnEventHandler(params object[] param);

    private Dictionary> m_Dic = new Dictionary>();

    /// 
    /// 添加监听
    /// 
    /// 
    /// 
    public void AddEventListener(string key, OnEventHandler handler)
    {
        //当前若存在key值对应的委托列表
        if (m_Dic.ContainsKey(key))
        {
            m_Dic[key].Add(handler);
        }
        else
        {
            List listHandler = new List();
            listHandler.Add(handler);
            m_Dic.Add(key, listHandler);
        }
    }

    /// 
    /// 移出监听
    /// 
    /// 
    /// 
    public void RemoveEventListener(string key, OnEventHandler handler)
    {
        if (m_Dic.ContainsKey(key))
        {
            m_Dic[key].Remove(handler);
            if (m_Dic[key].Count == 0)
            {
                m_Dic.Remove(key);
            }
        }
    }

    /// 
    /// 派送消息
    /// 
    /// 
    /// 
    public void Dispatch(string key, params object[] param)
    {
        //若包含
        if (m_Dic.ContainsKey(key))
        {
            List listHandler = m_Dic[key];

            //遍历 该key值对应的所有委托
            for (int i = 0; i < listHandler.Count; ++i)
            {
                if (listHandler[i] != null)
                {
                    listHandler[i].Invoke(param);
                }
            }
        }
    }

}

三、创建Events.cs脚本,定义我们的key

1.脚本如下:

public class Events 
{
    public static string PlayerAttacked = "PlayerAttacked";

    public static string PlayerChangeHp = "PlayerChangeHp";

}

四、修改GameController.cs脚本。

1.当角色受到攻击,发送消息给Player.cs脚本。
2.注意GameController.cs脚本下的PlayerAttacked(int hp);
3.代码如下:

sing UnityEngine;

public class GameController : MonoBehaviour 
{
    public static GameController Instance;

    private void Awake()
    {
        Instance = this;
    }

    private void Start()
    {
        
    }

    private void Update()
    {
        //按下数字键 1 减血
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            if (!Player.Instance.PlayerDead())
            {
                PlayerAttacked(-20);
            }
        }

        //按下数字键 2 加血
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            if (!Player.Instance.PlayerFullBlood())
            {
                PlayerAttacked(20);
            }
        }
    }

    /// 
    /// 角色被攻击
    /// 
    private void PlayerAttacked(float hp)
    {
        //发送消息 角色被攻击
        EventDispatcher.Instance.Dispatch(Events.PlayerAttacked, hp);
    }

}

五、修改Player.cs脚本,监听Gamecontroller.cs发送的消息,并且发送消息给所有与血量相关的对象。

1.监听Gamecontroller.cs发送的消息
2.发送消息给所有与血量相关的对象(发送消息给所有监听player的血量的对象)
3.代码如下:

using UnityEngine;

public class Player : MonoBehaviour 
{
    public static Player Instance;

    /// 
    /// 满血血量
    /// 
    public float Max_Hp;

    /// 
    /// 当前血量
    /// 
    public float Current_Hp;

    private void Awake()
    {
        Instance = this;       
    }

    private void Start()
    {
        EventDispatcher.Instance.AddEventListener(Events.PlayerAttacked, OnPlayerAttacked);
    }

    private void Destroy()
    {
        EventDispatcher.Instance.RemoveEventListener(Events.PlayerAttacked, OnPlayerAttacked);
    }

    /// 
    /// 角色被攻击回调
    /// 
    /// 
    private void OnPlayerAttacked(object[] param)
    {
        float hp = (float)param[0];
        Current_Hp += hp;
        float b = Current_Hp / Max_Hp;

        EventDispatcher.Instance.Dispatch(Events.PlayerChangeHp, Current_Hp, b);
    }

    /// 
    /// 角色初始化信息
    /// 
    public void Init()
    {
        Max_Hp = 100;
        Current_Hp = Max_Hp;
    }

    /// 
    /// 判断当前角色是否死亡
    /// 
    /// 
    public bool PlayerDead()
    {
        if (Current_Hp <= 0)
        {
            Current_Hp = 0;
            return true;
        }
        return false;
    }

    /// 
    /// 判断当前角色是否满血
    /// 
    /// 
    public bool PlayerFullBlood()
    {
        if (Current_Hp >= Max_Hp)
        {
            Current_Hp = Max_Hp;
            return true;
        }
        return false;
    }

}

六、修改UIRoot01.cs、UIRoot02.cs、UIPlayer.cs脚本。

1.修改这三个脚本,让他们分别监听Player.cs发送过来的消息。
2.代码如下:

using UnityEngine;
using UnityEngine.UI;

public class UIRoot01 : MonoBehaviour
{
    private Image m_HpImage;

    private void Awake()
    {
        m_HpImage = transform.Find("Canvas/HP_Image/Image").GetComponent();


    }

    private void Start()
    {
        EventDispatcher.Instance.AddEventListener(Events.PlayerChangeHp, OnPlayerChangeHp);
    }


    private void Destroy()
    {
        EventDispatcher.Instance.RemoveEventListener(Events.PlayerChangeHp, OnPlayerChangeHp);
    }

    private void OnPlayerChangeHp(object[] param)
    {
        float b = (float)param[1];
        m_HpImage.transform.localScale = new Vector3(b, 1.0f, 1.0f);
    }
}
using System.Text;
using UnityEngine;
using UnityEngine.UI;

public class UIRoot02 : MonoBehaviour 
{
    private Text m_Text;

    private void Awake()
    {
        m_Text = transform.Find("Canvas/PlayerInfo/Text").GetComponent();
        
    }

    private void Start()
    {
        EventDispatcher.Instance.AddEventListener(Events.PlayerChangeHp, OnPlayerChangeHp);
    }

    private void Destroy()
    {
        EventDispatcher.Instance.RemoveEventListener(Events.PlayerChangeHp, OnPlayerChangeHp);
    }

    public void OnPlayerChangeHp(params object[] param)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("当前血量:");
        sb.Append(param[0].ToString());
        m_Text.text = sb.ToString();
    }
}
using UnityEngine;
using UnityEngine.UI;

public class UIPlayer : MonoBehaviour 
{
    private Image m_HpImage;

    private void Awake()
    {
        m_HpImage = transform.Find("Canvas/HP_Image/Image").GetComponent();
        
    }

    private void Start()
    {
        EventDispatcher.Instance.AddEventListener(Events.PlayerChangeHp, OnPlayerChangeHp);
    }

    private void Destroy()
    {
        EventDispatcher.Instance.RemoveEventListener(Events.PlayerChangeHp, OnPlayerChangeHp);
    }

    public void OnPlayerChangeHp(params object[] param)
    {
        float b = (float)param[1];
        m_HpImage.transform.localScale = new Vector3(b, 1.0f, 1.0f);
    }
}

七、总结

1.使用方法特别的简单,只需要保留Events.cs和EventDispatcher.cs脚本。
2.当我们在游戏中,假如某个状态值改变,就马上发送消息:EventDispatcher.Instance.Dispatch(Events.key,需要传递的值);
3.而监听这个值的对象,在他们的Start方法中,监听消息:EventDispatcher.Instance.AddEventListener(Events.key, 方法回调);
4.在他们的Destroy方法中,移除监听:EventDispatcher.Instance.AddEventListener(Events.key, 方法回调);
4.在“方法回调”里,修改我们的值。

这是一种不太完善的使用方式,因为我们应该将view和Model区分开来,也就是说,这种模式常常被用于MVC(Model-View-Controller)框架的开发的应用中。
在接下来的博客中,我将逐步的去完成一个基于MVC架构的UI框架,这将会使用到我们的观察者模式。

你可能感兴趣的:(MMORPG实战开发)