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

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

图示观察者模式:
在这里插入图片描述

一、创建Singleton.cs脚本。

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

public class Singleton<T> 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<string, List> m_Dic = new Dictionary<string, List>();
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<EventDispatcher> 
{
    public delegate void OnEventHandler(params object[] param);

    private Dictionary<string, List<OnEventHandler>> m_Dic = new Dictionary<string, List<OnEventHandler>>();

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

    /// <summary>
    /// 移出监听
    /// </summary>
    /// <param name="key"></param>
    /// <param name="handler"></param>
    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);
            }
        }
    }

    /// <summary>
    /// 派送消息
    /// </summary>
    /// <param name="key"></param>
    /// <param name="param"></param>
    public void Dispatch(string key, params object[] param)
    {
        //若包含
        if (m_Dic.ContainsKey(key))
        {
            List<OnEventHandler> 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);
            }
        }
    }

    /// <summary>
    /// 角色被攻击
    /// </summary>
    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;

    /// <summary>
    /// 满血血量
    /// </summary>
    public float Max_Hp;

    /// <summary>
    /// 当前血量
    /// </summary>
    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);
    }

    /// <summary>
    /// 角色被攻击回调
    /// </summary>
    /// <param name="param"></param>
    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);
    }

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

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

    /// <summary>
    /// 判断当前角色是否满血
    /// </summary>
    /// <returns></returns>
    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<Image>();


    }

    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<Text>();
        
    }

    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<Image>();
        
    }

    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框架,这将会使用到我们的观察者模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: mmo(Massively Multiplayer Online)是一种在线游戏类型,与其他游戏不同之处在于它可以同时容纳大量玩家在线游戏。在mmo游戏中,网络通讯是游戏架构的核心之一,游戏的流畅性、安全性以及玩家之间的互动性都需要它的支持。 而u3d(Unity3D)是一种广泛应用于游戏开发的开发平台,其拥有的强大的跨平台支持和极其方便的开发环境使得它成为了mmo游戏开发的重要选择。 mmo u3d网络通讯的实现需要几个重点技术的支持,其中最为核心的是实现不同玩家之间的数据交换,包括玩家之间的文字、语音、位置、状态等。在网络通讯过程中,数据传输的稳定性和即时性是非常重要的,因此mmo u3d游戏需要使用可靠的协议(例如TCP)以及优秀的消息队列等技术手段,来保证数据流畅的传输,并提供实时的反馈。 另外,mmo u3d游戏中的玩家绝大部分时间是在线的,所以游戏的安全性也极其重要。开发者需要对网络数据进行加密、防篡改等一系列技术手段来保证游戏数据的安全性,并为玩家构建一个安全可信的游戏环境。 总之,mmo u3d网络通讯是一个非常关键的技术领域,它对于mmo游戏的顺利开发和运行都具有非常重要的作用。 ### 回答2: mmo(Massively Multiplayer Online)游戏是一种在线的多人游戏,它允许成千上万的玩家在同一时间内共同参与到游戏中。u3d(Unity3D)是一款游戏引擎软件,它提供了许多易于使用的开发工具来制作各种类型的游戏,包括mmo游戏。 在mmo u3d游戏中,网络通讯非常重要。由于大量的玩家同时参与游戏,需要处理大量的信息交换,并且保持游戏的同步性。网络通讯主要分为两种类型:客户端与服务器之间的通讯和客户端之间的通讯。 客户端与服务器之间的通讯是mmo u3d游戏中最关键的一部分,因为它涉及到游戏逻辑和数据的传输。服务器担任着管理游戏状态和处理玩家输入的角色,向客户端发送信息以及从客户端接收信息。而客户端主要负责显示游戏场景,向服务器发送信息并接收信息。由于网络通讯中的延迟和数据传输的稳定性等问题,需要对通讯过程进行优化。 客户端之间的通讯是指玩家之间的信息传输,例如聊天、交易、组队等。这种通讯方式相对于客户端和服务器之间的通讯较为简单,但同样需要考虑网络延迟和稳定性问题,避免数据传输错误和时间延迟。 总之,mmo u3d游戏的网络通讯是游戏开发中非常重要的一环,需要开发者们不断进行技术的优化和提升,以达到更好的游戏体验效果。 ### 回答3: MMO U3D网络通讯是一种基于Unity3D引擎的多人在线游戏开发技术,它采用C#语言编写,结合了多种网络协议,实现了快速、稳定、高效的网络通讯。 MMO U3D网络通讯主要分为服务端和客户端两个部分。服务端采用自主开发的框架,支持多种数据库,可以快速实现游戏服务器的搭建和维护,实现多人同时在线,支持实时聊天、交易、组队等功能。客户端采用可视化的编辑器,支持一键打包发布到各个平台,实现多人游戏的联机,并提供丰富的美术效果和音效,使游戏更具吸引力。 在开发过程中,MMO U3D网络通讯提供了一系列的API和工具,可以简化游戏开发过程,大大提高开发效率。同时,它还提供了完善的安全机制,通过数据加密、防作弊等措施保障游戏的公平性和安全性。 总之,MMO U3D网络通讯是一种成熟、先进的多人在线游戏开发技术,它具有极高的稳定性和安全性,可以快速实现多人游戏的联机和各种功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值