游戏开发少不了处理鼠标键盘手柄等外设输入消息。比较传统的做法是,截取windows系统消息,如WM_MOUSEMOVE,WM_LBUTTONDOWN等,然后对游戏数据进行分析:当前坐标位于什么物体之上,应该做出何种反应。这是一件非常复杂而繁琐的事情,你必须首先搞清楚物体的相互遮掩关系,如果要透过图像中的透明区域点选到背景物体则更加令人头疼。
ShinyNova的基础思想是面向对象化,一切面向实体。为了达到快速,便利开发的目标,我们必须把:
点在哪里--->点到什么物体上--->该物体是否处理该外设消息--->如何处理该外设消息
改变为:
实体需要接受什么外设消息--->实体如何处理外设消息
下面一个简单的例子,一个实体在鼠标触碰到该实体时,半透明度为1(高亮),否则为半透明度0.7(黯淡).
class myObject:public PObject
{
void OnIniData()
{
SetAlpha(0.7);
Updata();
SetAutoMsg(MSG_INPUT_TOUCH); //MSG_INPUT_TOUCH是内核发送的鼠标触发消息ID
SetAutoMsg(MSG_INPUT_UNTOUCH);
}
virtual BOOL OnMsg(SNG_MSG& msg)
{
switch(msg.MsgID)
{
case MSG_INPUT_TOUCH:
SetAlpha(1);
Updata();
break;
case MSG_INPUT_UNTOUCH:
SetAlpha(0.7);
Updata();
break;
default:
return PObject::OnMsg(msg);
}
}
};
OK,现在无论该实体位于一个多么复杂的环境之中,只要鼠标触碰到该物体任意一点,该物体将被激活。坐标选取遮掩判断等所有琐碎的工作都将被内核自动完成。也许有人会问?为什么还需要设置实体需要接受的消息呢。在windows中不是只需要处理接受消息就可以了吗?未被处理的外设消息将被自动丢弃。事实上,SetAutoMsg是一组函数:
void SetAutoMsg(MSGHANDLE msgID);//设置自己接受
void SetAutoMsg(MSGHANDLE msgID,SNG_MSG& Msg); //设置自动转发的消息,当Msg的转发消息为NULL时,自动转发传入的消息。
void SetAutoMsg(MSGHANDLE msgID,OBJECTHANDLE dest,MSGHANDLE destid,WPARAM param1=0,LPARAM param2=0);
设想一下在MFC开发中你定义了一个控件,当这个控件被触发时需要执行一些特别的操作应该怎么办?大部分时候需要派生这个控件类,并重载消息处理函数。麻烦,代码臃肿!然而在ShinyNova中有一个简便的做法:消息转发。将A实体接收到的消息,直接转发给B实体处理。好处有二,一是不需要去修改重载那些已经封装完整的控件就可以达到改变其功能的效果。二是利于将子实体的外设输入统一投递到母体处理(这才是主要功能)如下:
PObject* child,father;
child->SetAutoMsg(MSG_INPUT_MOUSE_LEFT_DOWN,father->GetHandle(),CHILD_LEFT_DOWN,child,0);//CHILD_LEFT_DOWN是自定义的消息号
好了,现在每当Child实体被鼠标左键点击的时候,母体都会收到消息CHILD_LEFT_DOWN,并在参数中附带了子体的指针,我们可以做出处理了:比如把这个母体移动,子体消失等等。
ShinyNova内核中有一个独立模块(InputModule)负责从环境外接受输入消息(Windows消息),该模块会解析传入的消息与当前视域中注册实体比较,并将比较结果以SNG_MSG的形式投递给实体(异步)。以下是ShinyNova内核占用的消息号MsgID,用于发送外设消息 :
MSG_INPUT_BEGIN ,
MSG_INPUT_TOUCH ,
MSG_INPUT_UNTOUCH ,
MSG_INPUT_MOUSE_MOVE ,
MSG_INPUT_MOUSE_LEFT_DOWN ,
MSG_INPUT_MOUSE_LEFT_UP ,
MSG_INPUT_MOUSE_LHIT_MOVE ,
MSG_INPUT_MOUSE_RIGHT_DOWN ,
MSG_INPUT_MOUSE_RIGHT_UP ,
MSG_INPUT_MOUSE_RHIT_MOVE ,
MSG_INPUT_MOUSE_DOUBLEHIT ,
MSG_INPUT_MOUSE_STAY ,
MSG_INPUT_MOUSE_OUTSTAY ,
MSG_INPUT_MOUSE_MID_DOWN ,
MSG_INPUT_MOUSE_MID_UP ,
MSG_INPUT_MOUSE_SCROLL_UP ,
MSG_INPUT_MOUSE_SCROLL_DOWN ,
MSG_INPUT_KEY_STAY ,
MSG_INPUT_KEY_DOWN ,
MSG_INPUT_KEY_UP ,
MSG_INPUT_SCREEN_GETFOCUS , //屏幕重新获得焦点
MSG_INPUT_SCREEN_LOSTFOCUS , //屏幕重新获得焦点
MSG_INPUT_END ,
当InputModule投递鼠标消息给实体时,会首先检测实体设置的转发消息结构中param2是否被占用.如果为空,那么将填充外设信息结构MOUSEDATA投递与实体。如PObject默认处理鼠标拖动消息:
BOOL PObject::OnMsg(SNG_MSG& Msg)
{
switch(Msg.msgID)
{
case MSG_INPUT_MOUSE_LHIT_MOVE:
{
MOUSEDATA* data=(MOUSEDATA*)Msg.param2;
if (data)
{
Move(data->MS_Offset.x,data->MS_Offset.y);
Updata();
}
}
break;
}
return TRUE;
}