SDL2 游戏开发日记(七) 自定义消息
在游戏中,有各种各样的消息,除了鼠标键盘等控制消息之外。游戏物体也会产生各种各样的消息,有些消息是需要立即处理的。有些消息需要过了一段时间后才会被处理。
例如在单机麻将游戏中,玩家出牌的消息要通知其他ai玩家,出了什么牌,其他玩家收到消息后,则需要判断这个牌有需不需要,是否碰/杠/胡等。
玩家出牌后,其他ai玩家不能立即摸牌,需要等待2秒后才能摸牌。
消息接收处理类
#pragma once
#include <cassert>
struct GameMessage;
class MessageListener{
protected:
int mID;
static int mNextVaildID;
public:
MessageListener(){
SetID(MessageListener::NextVaildID());
}
MessageListener(int id){
SetID(id);
}
virtual ~MessageListener(){
}
int ID(){
return mID;
}
void SetID(int id){
assert(id >= mNextVaildID && "MessageListener invaild id");
mID = id;
mNextVaildID = id + 1;
}
static int NextVaildID(){
return mNextVaildID;
}
//消息处理
virtual bool OnMessage(const GameMessage &msg){ return false; }
};
消息结构#
#pragma once
#include "MessageListener.h"
#include <math.h>
const float smallestDelay = 0.1f;
struct GameMessage{
//消息的发送端
void *Sender;
//消息的接收端
MessageListener *Receiver;
//消息类型
int message;
//消息发送时间,延时消息用
float DispatchTime;
//扩展数据
void *ExtraInfo;
GameMessage(){
Sender = 0;
Receiver = 0;
message = -1;
DispatchTime = 0;
ExtraInfo = 0;
}
GameMessage(float time, void *sender, MessageListener *receiver, int msg, void *extraInfo = 0){
DispatchTime = time;
Sender = sender;
Receiver = receiver;
message = msg;
ExtraInfo = extraInfo;
}
};
//重载运算符,放到set容器中时自动排序。
inline bool operator ==(const GameMessage &m1,const GameMessage &m2){
return fabs(m1.DispatchTime - m2.DispatchTime) <= smallestDelay &&
(m1.Sender == m2.Sender) &&
(m1.Receiver == m2.Receiver) &&
(m1.message == m2.message) && (m1.ExtraInfo == m2.ExtraInfo);
}
inline bool operator <(const GameMessage &m1,const GameMessage &m2){
if (m1 == m2){
return false;
}
else{
return m1.DispatchTime < m2.DispatchTime;
}
}
消息管理和分发
单例模式
#pragma once
#include <set>
#include "GameMessage.h"
#define Dispatcher MessageDispatcher::Instance()
class MessageDispatcher{
private:
//游戏运行时间
float mTimeElapse;
//游戏运行帧数
long mFrameCount;
//延时消息队列
std::set<GameMessage> mMessageQueue;
MessageDispatcher():mTimeElapse(0.0f),mFrameCount(0){};
MessageDispatcher(const MessageDispatcher&);
MessageDispatcher& operator=(const MessageDispatcher&);
public:
static MessageDispatcher &Instance(){
static MessageDispatcher instance;
return instance;
}
//发送消息
void DispatchMsg(float delay, void *sender, MessageListener *receiver, int message, void* extraInfo = NULL);
//处理延时消息
void DispatchDelayedMessages(float time_step);
};
#include "MessageDispatcher.h"
#include "MessageListener.h"
#include "GameMessage.h"
//发送消息
void MessageDispatcher::DispatchMsg(float delay, void *sender, MessageListener *receiver, int message, void* extraInfo){
if (receiver == NULL)
return;
GameMessage msg(0, sender, receiver, message, extraInfo);
//如果不是延时消息,receiver立即处理。
if (delay <= 0.0f){
receiver->OnMessage(msg);
}
else{
//延时消息,发送时间为当前时间加上延时时间。
msg.DispatchTime = mTimeElapse + delay;
mMessageQueue.insert(msg);
}
}
//处理延时消息,游戏主循环中每帧调用一次。
void MessageDispatcher::DispatchDelayedMessages(float time_step){
float currentTimeElapse = mTimeElapse;
while (!mMessageQueue.empty() &&
(mMessageQueue.begin()->DispatchTime <= currentTimeElapse)){
const GameMessage &message = *(mMessageQueue.begin());
message.Receiver->OnMessage(message);
mMessageQueue.erase(mMessageQueue.begin());
}
mTimeElapse += time_step;
mFrameCount++;
}