【Unity 3D事件优化】
公司项目开发中,大部分函数功能回调都是通过注册,派发事件实现的。将回调的一个或多个对象方法绑定某个事件,当派发该事件时,绑定该事件的一个或多个对象接收到该事件后调用对应的方法。模式属于:一对一或一对多。这样的模式在处理大多数事件情况非常得体。
最近接策划的一个案子,大体功能是组队功能界面的多个队员item,实时显示更新队员的血量。项目中所有玩家的血量改变都通过一个SM_HealthChange协议,该协议包含要改变血量的玩家Id号和改变的血量值。接收到这个协议,通过该协议的Id号,获取对应玩家的entity实体table表,然后调用该entity的setHp函数处理血量。开始想到的组队界面血量同步方法是:沿用项目已有的事件系统,所有队员先注册血量改变事件,当接收SM_HealthChange协议时,发送血量改变事件同时将改变血量的玩家Id号和改变的血量值作为参数传递。之前注册了血量改变事件的队员接到该事件后,先判断血量改变的玩家id是不是自己的,如果是自己的改变自己的血量显示,否则就不做处理。这样就实现了血量同步方法。
但后面分析存在个问题,只要接到SM_HealthChange协议就发送事件,不管绑定的对象id是不是协议中的玩家id,只要发送事件就得接受,因为事件发送者没法提前判断多个绑定事件的对象谁才是真正要接收的对象,从而造成了不必要的开销消耗。后来采取同事的建议,可以采用c#自带的事件功能event,每个玩家实体entity内创建一个血量改变事件,组队界面每个队员item将对应的玩家entity的血量改变事件和自身的血量更新函数绑定,这样当对应的entity改变血量调用自身的setHp函数时就调用对应组队界面队员item绑定的血量改变函数,实现了谁的血量改变就调用对应玩家的血量同步函数,达到了一对一的事件处理,优化了不必要的开销。
下面上热腾腾的代码
using System;
using System.Collections.Generic;
namespace 深入理解CShape
{
class Entity
{
public delegate void Func(float hp);
public event Func HP_ChangeEvent;
public readonly int ID;
private float hp;
public Entity(int id)
{
ID = id;
}
public float HP
{
get { return hp; }
set {
hp = value;
Console.WriteLine("玩家{0}的血量改变为{1}", ID, hp);
HP_ChangeEvent(hp);
}
}
}
class TeamMember
{
public readonly int ID;
private float m_Hp;
public TeamMember(int id)
{
ID = id;
}
public void SynchronousBlood(float hp)
{
m_Hp = hp;
Console.WriteLine("队员{0}改变血量为{1}", ID, hp);
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<int, Entity> entityList = new Dictionary<int, Entity>();
for(int i = 1; i <= 5; ++i)
{
entityList.Add(i, new Entity(i));
TeamMember teamMember = new TeamMember(i);
Entity entity = entityList[teamMember.ID];
entity.HP_ChangeEvent += teamMember.SynchronousBlood;
}
var iter = entityList.GetEnumerator();
Random random = new Random();
while (iter.MoveNext())
{
iter.Current.Value.HP = random.Next(50, 100);
Console.WriteLine();
}
Console.ReadLine();
}
}
}
运行结果

这样一对一发送,保证了对应的事件改变发送给对应的监听者,省去了不必要的开销。个人对优化的理解就行,在保障功能不受影响的前提下,重复计算处理的,不需要计算的统统pass,只留下真正起作用的部分,以保障性能开销。