c++里面有一个消息机制,我觉得很方便,在mfc里面以前看到过,但是真正觉得它好用还是在做游戏之后。
很多人会碰到一个问题,举个例子:游戏里面主角被砍了一刀后(HeroCutEvent),会发生A:gui显示血量减少(Ahandler),B:屏幕闪红(Bhandler),C:主角播放被砍动作(Chandler),D:播放血的特效(Dhandler)。
英雄被砍事件发生后,如果控制ABCD的程序都不在一个源文件里面,而是分别在四个源文件里面,这下会变的很麻烦,因为要调用其它对象的代码,就要有对它们的引用。如果不想引用呢?而且在调用的时候还可以传参数(比如说ABCDhandler都以此时的血量做参数)。
下面的是我用js写的一个轻型的消息(事件)处理类,当然其它语言也一样可以,思路是一样的
#pragma strict//预处理选项,让编译器强制类型检查 ,C,C++不需要
import System;
import System.Collections.Generic; //要用.net的哈希表 public class MessageManager
{
private static var messages :Hashtable;//哈希表,key是事件类型(DropBloodEvent),value是个数组,数组里面存放处理函数(Ahandler,Bhandler,Chandler,Dhandler)
private static var initialized:boolean=false;//判断是否初始化
public static function Initialize()
{
if(initialized)
{
return ;
}
initialized=true;
messages=new Hashtable();
}
public static function AddListener(type:Type,callBack:Function):void //Function类型就是C,C++里面的函数指针,指向一个函数
{
Initialize();
GetListeners(type).Push(callBack);
}
public static function GetListeners(type:Type):Array//GetListeners就是通过事件类型(HeroCutEvent)找到存储相应处理函数的数组(Array,里面存放ABCDhandler)
{
if(!messages[type.ToString()])
{
messages[type.ToString()]=new Array();
return messages[type.ToString()] as Array;//type.ToString()就是把这个类型读成字符串,作为类的唯一标识(也可用枚举) }
else
{
var arr:Array= messages[type.ToString()] as Array;
return arr;
}
}
public static function Dispatch(gameMessage:GameMessage):void
{
for(var i:int=0;i<GetListeners(typeof(gameMessage)).length;++i)
{
(GetListeners(typeof(gameMessage))[i] as Function)(gameMessage);
}
}
}public class GameMessage
{
public function GameMessage()
{
MessageManager.Initialize();
}}public class HeroCutEvent extends GameMessage//主角被砍事件
{
public var currBlood :float;//这个做参数使用
public function HeroCutEvent(arCurrBlood:float)
{
this.currBlood=arCurrBlood;
super();//就是父类GameMessage的构造函数。
MessageManager.Dispatch(this);//this就是指自己作为参数
}
}程序一开始将ABCDhandler加入哈希表。
在A里面一开始调用MessageManager.AddListener(HeroCutEvent,Ahandler)(Ahandler只对A可见);
BCD同理。
程序一开始,由于ABCD调用了AddListener,所以hashtable类型的messages便有了一个key-value对,主角被砍时,只要实例化一个HeroCurEvent的对象就可以了.
New HeroCutEvent(bloodAmt);//新建消息(事件)对象Obj
由于HeroCutEvent的构造函数被调用,所以Dispatch会被调用,参数就是自己这个对象(Obj),Dispatch就是通过这个对象类型来找到ABCDhandler,并一个一个轮流调用,参数就是这个HeroCurEvent对象Obj,ABCDhandler需要的参数(此时主角的血量)被保存在这个对象Obj里面(currBlood一定是public否则ABCDhandler不可读)
以前自学mfc那会搞的不是很明白,但是用了一段事件之后感觉的确很方便,其实还是被高人指点,要感谢我的前一个老板,也算是我的老师。
这里有一个问题就是新建了Obj之后怎么办,就让让它一直在内存里吗?如果是C,C++可以用delete直接处理,如果是java或.NET编译器,那就不要管它了,后台自动垃圾回收。
用一个全局的哈希表,键就是各个消息类型的唯一标识,值就是一个数组,里面元素是这个消息的处理函数指针。新建一个消息类型对象的时候,构造函数被调用,构造函数会在全局哈希表里面通过自己的唯一标识符索引到处理函数数组,并将这个数组里面的函数循环调用一次。
真的很方便,我现在做串口通信,串口代码在对象A里面,但是串口要控制的对象是B,A对B没有引用,而且B可能随时被销毁,随时实例化,这样A引用B就更难了。
之前的程序员就是用引用的方式做的,代码写的很夸张。
用消息机制之后就很好处理了。
希望对被同样问题困扰的朋友有所帮助。
很多人会碰到一个问题,举个例子:游戏里面主角被砍了一刀后(HeroCutEvent),会发生A:gui显示血量减少(Ahandler),B:屏幕闪红(Bhandler),C:主角播放被砍动作(Chandler),D:播放血的特效(Dhandler)。
英雄被砍事件发生后,如果控制ABCD的程序都不在一个源文件里面,而是分别在四个源文件里面,这下会变的很麻烦,因为要调用其它对象的代码,就要有对它们的引用。如果不想引用呢?而且在调用的时候还可以传参数(比如说ABCDhandler都以此时的血量做参数)。
下面的是我用js写的一个轻型的消息(事件)处理类,当然其它语言也一样可以,思路是一样的
#pragma strict//预处理选项,让编译器强制类型检查 ,C,C++不需要
import System;
import System.Collections.Generic; //要用.net的哈希表 public class MessageManager
{
private static var messages :Hashtable;//哈希表,key是事件类型(DropBloodEvent),value是个数组,数组里面存放处理函数(Ahandler,Bhandler,Chandler,Dhandler)
private static var initialized:boolean=false;//判断是否初始化
public static function Initialize()
{
if(initialized)
{
return ;
}
initialized=true;
messages=new Hashtable();
}
public static function AddListener(type:Type,callBack:Function):void //Function类型就是C,C++里面的函数指针,指向一个函数
{
Initialize();
GetListeners(type).Push(callBack);
}
public static function GetListeners(type:Type):Array//GetListeners就是通过事件类型(HeroCutEvent)找到存储相应处理函数的数组(Array,里面存放ABCDhandler)
{
if(!messages[type.ToString()])
{
messages[type.ToString()]=new Array();
return messages[type.ToString()] as Array;//type.ToString()就是把这个类型读成字符串,作为类的唯一标识(也可用枚举) }
else
{
var arr:Array= messages[type.ToString()] as Array;
return arr;
}
}
public static function Dispatch(gameMessage:GameMessage):void
{
for(var i:int=0;i<GetListeners(typeof(gameMessage)).length;++i)
{
(GetListeners(typeof(gameMessage))[i] as Function)(gameMessage);
}
}
}public class GameMessage
{
public function GameMessage()
{
MessageManager.Initialize();
}}public class HeroCutEvent extends GameMessage//主角被砍事件
{
public var currBlood :float;//这个做参数使用
public function HeroCutEvent(arCurrBlood:float)
{
this.currBlood=arCurrBlood;
super();//就是父类GameMessage的构造函数。
MessageManager.Dispatch(this);//this就是指自己作为参数
}
}程序一开始将ABCDhandler加入哈希表。
在A里面一开始调用MessageManager.AddListener(HeroCutEvent,Ahandler)(Ahandler只对A可见);
BCD同理。
程序一开始,由于ABCD调用了AddListener,所以hashtable类型的messages便有了一个key-value对,主角被砍时,只要实例化一个HeroCurEvent的对象就可以了.
New HeroCutEvent(bloodAmt);//新建消息(事件)对象Obj
由于HeroCutEvent的构造函数被调用,所以Dispatch会被调用,参数就是自己这个对象(Obj),Dispatch就是通过这个对象类型来找到ABCDhandler,并一个一个轮流调用,参数就是这个HeroCurEvent对象Obj,ABCDhandler需要的参数(此时主角的血量)被保存在这个对象Obj里面(currBlood一定是public否则ABCDhandler不可读)
以前自学mfc那会搞的不是很明白,但是用了一段事件之后感觉的确很方便,其实还是被高人指点,要感谢我的前一个老板,也算是我的老师。
这里有一个问题就是新建了Obj之后怎么办,就让让它一直在内存里吗?如果是C,C++可以用delete直接处理,如果是java或.NET编译器,那就不要管它了,后台自动垃圾回收。
用一个全局的哈希表,键就是各个消息类型的唯一标识,值就是一个数组,里面元素是这个消息的处理函数指针。新建一个消息类型对象的时候,构造函数被调用,构造函数会在全局哈希表里面通过自己的唯一标识符索引到处理函数数组,并将这个数组里面的函数循环调用一次。
真的很方便,我现在做串口通信,串口代码在对象A里面,但是串口要控制的对象是B,A对B没有引用,而且B可能随时被销毁,随时实例化,这样A引用B就更难了。
之前的程序员就是用引用的方式做的,代码写的很夸张。
用消息机制之后就很好处理了。
希望对被同样问题困扰的朋友有所帮助。
当你明白事件原本就是可以多播滴,你会发现你做滴其实就是net里原本就有的东西当你读过被博客园们神话化了滴“观测者模式”就会知道如果灰太狼来啦,喜羊羊会砸石头,懒羊羊会哭鼻子,其实也木那么神奇虽然俺实在不想去神话“设计模式”,但是有空去稍微看一眼装饰模式,观察者模式,对你来说也许还不错