找回密码
 register

QQ登录

只需一步,快速开始

查看: 688|回复: 0

[游戏教程] 五、六、武侠世界2源码分析:FairSystem、事件系统

[复制链接]

[游戏教程] 五、六、武侠世界2源码分析:FairSystem、事件系统

[复制链接]
  • 打卡等级:热心大叔
  • 打卡总天数:94
  • 打卡月天数:17
  • 打卡总奖励:94
  • 最近打卡:2025-01-18 01:28:20
Waylee

主题

0

回帖

1万

积分

仙帝

积分
12364
Waylee 2023-6-16 08:43 | 显示全部楼层 |阅读模式

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?register

×
5.1 创建角色
FairySystem.exe!Fairy::LogicModel::initModel
FairySystem.exe!Fairy::LogicModelManager::createLogicModel
FairySystem.exe!Fairy::LogicModelManager::createLogicModel
FairySystem.exe!Fairy::LogicModelObject::_createLogicModel
FairySystem.exe!Fairy::LogicModelObject::setLogicModelName
FairySystem.exe!Fairy::`anonymous namespace'::LogicModelNameCmd::doSet
FairySystem.exe!Fairy::PropertyInterface::setProperty
FairySystem.exe!wWinMain


6.1 Class view

class CEventSystem : public tEventSystem
class CActionItem : public tActionItem
  • CActionItem

    • CActionItem_ChangeSuit
    • CActionItem_ChatMood
    • CActionItem_Item
    • CActionItem_LifeAbility
    • CActionItem_MouseCmd_Exchange
    • CActionItem_MouseCmd_Friend
    • CActionItem_MouseCmd_Identify
    • CActionItem_MouseCmd_Repair
    • CActionItem_PetSkill
    • CActionItem_Skill



QQ截图20230616084209.jpg

2.jpg





名称描述
CActionItem_ChatMood操作管理器-聊天动作
CActionItem_Item操作管理器-物品
CActionItem_LifeAbility操作管理器-生活技能
CActionItem_MouseCmd_Identify鉴定
CActionItem_MouseCmd_Repair鼠标指令用于处理诸如鉴定,修理,加为好友等特殊鼠标状态
CActionItem_PetSkill宠物技能
CActionItem_Skill技能
CActionItem_XinFa心法

6.2 事件定义

6.2.1 事件 ID总共 492 个事件,定义于文件 GameInterface\Include\Game\GIEventDefine.h
enum GAME_EVENT_ID
{
GE_APPLICATION_INITED, /// 游戏程序初始化
...




事件 ID 均以 GE_打头。

6.2.2 事件名
定义于文件game\Event\GMEventSystem.cpp。

EVENT_DEFINE g_GlobalEvent[] = 
{
{ GE_APPLICATION_INITED, "APPLICATION_INITED", },
{ GE_ON_SCENE_TRANS, "ON_SCENE_TRANS", },
{ GE_ON_SERVER_TRANS, "ON_SERVER_TRANS", },




6.2.3 Event 结构
事件基本单元定义,此处重构为事件元 META_EVENT 更合适。

struct EVENT_DEFINE
{
typedef std::list< std::pair< FUNC_EVENT_HANDLE, UINT > > REGISTER_STRUCT;
GAME_EVENT_ID idEvent;
LPCTSTR szEvent;
BOOL delayProcess;
REGISTER_STRUCT listFuncNotify;



Event 结构包含了事件 ID、事件名、延迟处理标志、N 个处理过程。处理过程的定义如下:

typedef VOID (__stdcall* FUNC_EVENT_HANDLE)(const EVENT* pEvent, UINT dwOwnerData);



也即处理过程是返回值为空的,参数为 EVENT 和所有者 this(?未严格论证)指针的一个回调函数。
事件定义如下

struct EVENT
{
EVENT_DEFINE* pEventDef;
std::vector<STRING> vArg;
bool operator == (const EVENT& other);
};


事件包含了事件元、事件参数。
综上,事件、事件元、事件过程的关系为:事件是后者的组合。



6.2.4 事件初始化
搞了 2 个 map:

//通过事件名称检索表
std::map< STRING, EVENT_DEFINE* > m_mapEventIndex_AsName;
//通过事件ID检索表
std::map< GAME_EVENT_ID, EVENT_DEFINE* > m_mapEventIndex_AsID;


最终只是为了实现三角关系的勾结:事件 ID、事件名、事件元

3.jpg


6.3 注册事件
事件元是事件的中心,但是事件元不是天生的贵种,事件初始化虽然完成了所有事件的定义,也巩
固了三角关系,但是那只是一个空壳,没有事件处理过程,时间表什么也坐不了。注册事件这个方
法将时间表中的每一个事件元都绑定了一个处理过程。

// 注册事件处理函数
VOID CEventSystem::RegisterEventHandle(const STRING& nameEvent, FUNC_EVENT_HANDLE
funHandle, UINT uOwnerData)
{
EVENT_DEFINE* pEvent = m_mapEventIndex_AsName[nameEvent];
pEvent->listFuncNotify.push_back( std::make_pair(funHandle, uOwnerData) );
…



6.3.1 事件注册代码
一共 N 处事件注册

Client\UI_CEGUI\UIWindowMng.cpp
RegisterEventHandle( strEventName , _OnGameEvent , (DWORD)(DWORD_PTR)this )
Client\Game\Action\GMActionSystem.cpp
RegisterEventHandle("ON_SKILL_ACTIVE", _OnSkillActive)
Client\Game\DataPool\GMDataPool.cpp
RegisterEventHandle("UNIT_HP", _Skill_OnUpdatePlayerAttrib)
Client\Game\DataPool\GMDataPool.cpp
RegisterEventHandle("UNIT_MP", _Skill_OnUpdatePlayerAttrib)
Client\Game\DataPool\GMDataPool.cpp
RegisterEventHandle("UNIT_RAGE", _Skill_OnUpdatePlayerAttrib)
Client\Game\DataPool\GMDataPool.cpp
RegisterEventHandle("UNIT_EQUIP_WEAPON", _Skill_OnUpdatePlayerAttrib)
Client\Game\Engine\EngineInterface.cpp
RegisterEventHandle("VARIABLE_CHANGED", _OnVariableChangedEvent)
Client\Game\Interface\GMGameInterface.cpp
RegisterEventHandle("VARIABLE_CHANGED",
SCRIPT_SANDBOX::SystemSetup::_OnVariableChangedEvent)
Client\Game\Interface\GMInterface_Script_Talk.cpp
RegisterEventHandle("TIME_UPDATE", _SendTalkHelpMsg)
Client\Game\Interface\GMInterface_Script_Talk.cpp
RegisterEventHandle("VIEW_RESOLUTION_CHANGED", _HandleWindowSizeChange)
Client\Game\Sound\GMSoundSystem.cpp
RegisterEventHandle("VARIABLE_CHANGED", _OnVariableChangedEvent)
Client\Game\World\WorldManager.cpp
RegisterEventHandle("ON_SCENE_TRANS", _OnSceneTransEvent)
Client\Game\World\WorldManager.cpp
RegisterEventHandle("ON_SERVER_TRANS", _OnSceneTransEvent)
Client\Game\World\WorldManager.cpp
RegisterEventHandle("VARIABLE_CHANGED", _OnVariableChangedEvent)




INT CUIWindowItem::LUA_RegisterEvent(LuaPlus::LuaState* pState)
g_pEventSys->RegisterEventHandle( strEventName , _OnGameEvent ,
(DWORD)(DWORD_PTR)this );

VOID CActionSystem::Initial(VOID*)
// 注册关心的事件
// 技能开始使用
CEventSystem::GetMe()->RegisterEventHandle("ON_SKILL_ACTIVE", _OnSkillActive);

VOID CDataPool::Initial(VOID*)
CEventSystem::GetMe()->RegisterEventHandle("UNIT_HP", _Skill_OnUpdatePlayerAttrib);
CEventSystem::GetMe()->RegisterEventHandle("UNIT_MP", _Skill_OnUpdatePlayerAttrib);
CEventSystem::GetMe()->RegisterEventHandle("UNIT_RAGE",
_Skill_OnUpdatePlayerAttrib);
CEventSystem::GetMe()->RegisterEventHandle("UNIT_EQUIP_WEAPON",
_Skill_OnUpdatePlayerAttrib);

VOID CEngineInterface::Initial(VOID* pParam)
// 系统设置接口挂接变量控制系统
g_pEventSys->RegisterEventHandle("VARIABLE_CHANGED",_OnVariableChangedEvent);
CGameProcedure::s_pEventSystem->RegisterEventHandle("TIME_UPDATE",

VOID Talk::Initial()
// 注册时间事件回调函数
_SendTalkHelpMsg);
CGameProcedure::s_pEventSystem->RegisterEventHandle("VIEW_RESOLUTION_CHANGED",
_HandleWindowSizeChange);

VOID CSoundSystemFMod::Initial(VOID*)
// 挂接变量系统
CEventSystem::GetMe()->RegisterEventHandle("VARIABLE_CHANGED",
_OnVariableChangedEvent);
ON_SCENE_TRANS

VOID CWorldManager::Initial(VOID*)
// 注册事件处理
CEventSystem::GetMe()->RegisterEventHandle("ON_SCENE_TRANS", _OnSceneTransEvent);
CEventSystem::GetMe()->RegisterEventHandle("ON_SERVER_TRANS", _OnSceneTransEvent);
// 挂接变量系统
CEventSystem::GetMe()->RegisterEventHandle("VARIABLE_CHANGED",_OnVariableChangedEve
nt);


事件表
事件名称处理函数描述
ON_SCENE_TRANSCWorldManager::_OnSceneTransEventN/A
ON_SERVER_TRANSCWorldManager::_OnSceneTransEventN/A
VARIABLE_CHANGEDCWorldManager::_OnVariableChangedEvent挂接变量系统
VARIABLE_CHANGEDCSoundSystemFMod::_OnVariableChangedEventN/A
TIME_UPDATETalk::_SendTalkHelpMsg注册时间事件回调函数
VIEW_RESOLUTION_CHANGEDTalk::_HandleWindowSizeChangeN/A
VARIABLE_CHANGEDSCRIPT_SANDBOX::SystemSetup::_OnVariableChangedEvent系统设置接口挂接变量控制系统
VARIABLE_CHANGEDCEngineInterface::_OnVariableChangedEvent系统设置接口挂接变量控制系统
UNIT_HPCDataPool::_Skill_OnUpdatePlayerAttribN/A
UNIT_MPCDataPool::_Skill_OnUpdatePlayerAttribN/A
UNIT_RAGECDataPool::_Skill_OnUpdatePlayerAttribN/A
UNIT_EQUIP_WEAPONCDataPool::_Skill_OnUpdatePlayerAttribN/A
StrEventNameCUIWindowItem::_OnGameEventN/A

6.3.2 扯淡

事件其实就是一个这样的结构
Struct Event
{
STRING name;
UINT id;
List handle;
}


绕来绕去,搞这么绕口,非常 c 的一种风格,难道是修修补补的产物?


6.3.3 事件队列
事件队列定义

//事件队列
std::list< EVENT > m_queueEvent;
//慢速处理队列, 每桢一个,防止过多的消息同时涌现
std::list< EVENT > m_delayQueueEvent;
UINT m_dwLastTickCount;



事件队列就是一个链表。定义了一个慢速处理队列。大概是为了实现和 ms 的 Send、Post 不同方式
发送消息一样的作用。
事件入队可以通过事件 ID 和事件名两种方式,当然最后都是填充 EVENT 结构并入队。

virtual VOID PushEvent(GAME_EVENT_ID id);
virtual VOID PushEvent(STRING& eventName);


事件名和事件 ID 根据三角勾结关系取到事件元,入队即可。



6.3.4 执行事件处理
在主循环里执行

VOID CEventSystem::ProcessAllEvent(VOID)



大致过程如下,处理延迟处理事件不提,和非延迟事件一样处理。


  • 将重复事件合并
  • 从事件中取到事件元
  • 从事件元中取到事件过程链表
  • 依次执行链表中的每个处理过程
  • 将时间队列清空


6.4 事件源

Keyboard
Handle_AccelerateKey( event );
Handle_Combination_Key( event );
CEventSystem::GetMe()->PushEvent(GE_ACCELERATE_KEYSEND, "b");


Mouse


6.5 事件输入输出

产生事件
CEventSystem::GetMe()->PushEvent(GE_ACCELERATE_KEYSEND, "f");


6.6 事件回溯

以消息“GE_GAMELOGIN_OPEN_SELECT_SERVER”为例,打开 Analysis\event\武侠 2 窗口事件表.doc,找到消息
GAMELOGIN_OPEN_SELECT_SERVER21LoginSelectServerSelectServer.layout.xmlSelectServer.lua
然后打开 SelectServer.lua,在预加载的时候,注册了“GE_GAMELOGIN_OPEN_SELECT_SERVER”,
-- 注册 onLoad 事件
function LoginSelectServer_PreLoad()
-- 打开选择服务器界面
this:RegisterEvent("GAMELOGIN_OPEN_SELECT_SERVER"); 
-- 选择区域
this:RegisterEvent("GAMELOGIN_CLOSE_SELECT_SERVER"); 
-- 打开选择服务器界面
this:RegisterEvent("GAMELOGIN_SELECT_AREA"); 
-- 选择 login
this:RegisterEvent("GAMELOGIN_SELECT_LOGINSERVER"); 
-- 注册选择一个 login server 事件
this:RegisterEvent("GAMELOGIN_SELECT_LOGIN_SERVER"); 
-- 玩家进入场景
this:RegisterEvent("PLAYER_ENTERING_WORLD"); 
end

在消息响应的时候,在脚本的 function LoginSelectServer_OnEvent(event)中进行处理:

if( event == "GAMELOGIN_OPEN_SELECT_SERVER" ) then
this:Show();
return;
end


6.7 打开人物属性栏

GE_OPEN_CHARACTOR
e:\GameDev\projects\tlbb\source\wx2\wxsj2\trunk\Client\GameInterface\Include\Game\GIEventDefine.hL
line341 GE_OPEN_CHARACTOR, // 打开人物属性栏



e:\GameDev\projects\tlbb\source\wx2\wxsj2\trunk\Client\game\Event\GMEventSystem.cpp

line250 { GE_OPEN_CHARACTOR, "OPEN_CHARACTOR", },



e:\GameDev\projects\tlbb\source\wx2\wxsj2\trunk\Client\game\Interface\GMGameInterface_Script.cpp
line1374

INT Lua_OpenCharacter( LuaPlus::LuaState* state )
{
LuaStack args(state);
if( !args[ 1 ].IsInteger() ) return 0;
CEventSystem::GetMe()->PushEvent( GE_OPEN_CHARACTOR, args[ 1 ].GetInteger() );
return 0;
}



e:\GameDev\projects\tlbb\source\wx2\wxsj2\trunk\Client\game\Script\GMScriptSystm.cpp

objGlobal.Register( "OpenCharacter", SCRIPT_SANDBOX::Lua_OpenCharacter);



6.8 杂货商人交互分析

4.jpg
5.jpg


UI_CEGUI_d.dll!CUIWindowItem::_OnGameEvent
Game_d.exe!CEventSystem::_ProcessEvent
Game_d.exe!CEventSystem::ProcessAllEvent
Game_d.exe!CGameProcedure::ProcessGameEvent
Game_d.exe!CGameProcedure::MainLoop
Game_d.exe!_tMain_With_CPPException
Game_d.exe!WinMain

QUEST_EVENTLIST

{ GE_QUEST_EVENTLIST, "QUEST_EVENTLIST", },






您需要登录后才可以回帖 登录 | register

本版积分规则

雪舞知识库 | 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 |天天打卡

GMT+8, 2025-1-20 04:48 , Processed in 0.107492 second(s), 8 queries , Redis On.

Powered by XueWu Licensed

Copyright © Tencent Cloud.

快速回复 返回顶部 返回列表