找回密码
 register

QQ登录

只需一步,快速开始

查看: 1415|回复: 1

[游戏教程] 九、武侠世界2源码分析:UISystem 用户界面系统

[复制链接]

[游戏教程] 九、武侠世界2源码分析:UISystem 用户界面系统

[复制链接]
  • 打卡等级:热心大叔
  • 打卡总天数:122
  • 打卡月天数:18
  • 打卡总奖励:121
  • 最近打卡:2025-02-22 14:24:44
Waylee

主题

0

回帖

1万

积分

仙帝

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

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

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

×
UI 的核心是 CUISystem

9.1 Class view

11.jpg

class CUISystem : public tUISystem


9.2 初始化

加载 ui_ceguil.dll
VOID CGameProcedure::InitStaticMemeber(VOID)
{
g_theKernel.LoadPlugin(_T("UI_CEGUI_d.dll"), &g_theKernel);
//7. UI 管理器
s_pUISystem=(tUISystem*)g_theKernel.NewNode(_T("CUISystem"),_T("bin"),_T("ui_"));
s_pUISystem ->Initial(NULL);
…
}


  • 游客,如果您要查看本帖隐藏内容请回复





Codesnippet:

VOID CUISystem::Initial(VOID*)
{
g_pScriptSys = (tScriptSystem*)g_pKernel->GetNode("bin\\script");
g_pEventSys = (tEventSystem*)g_pKernel->GetNode("bin\\event");
g_pActionSys = (tActionSystem*)g_pKernel->GetNode("bin\\action");
g_pInputSys = (tInputSystem*)g_pKernel->GetNode("bin\\input");
g_pGfxSystem = (tGfxSystem*)g_pKernel->GetNode("bin\\gfx");




9.3 Tick
UISYSTEM 的 Tick 主要完成如下工作:
1. CEGUI 的时间脉冲的 Tick

CEGUI::System::getSingleton().injectTimePulse(g_pTimer->CalSubTime(s_nLastTime, 
nTimeNow)/1000.0f);



2. 角色信息板管理器的 Tick
m_pCreatureBoardSystem->Tick();

3. 定时释放未 CEGUI 图片缓存
CEGUI::ImagesetManager::getSingleton().FreeUnuseImageset();



每帧 Tick

VOID CGameProcedure::Tick(VOID)
{
s_pUISystem->Tick();
…
}




9.4 处理文字输入
UI 处理文字输入思想:
在窗口过程中优先处理文字输入。

LRESULT CALLBACK CGameProcedure::_MainWndProc(HWND hWnd, UINT message, WPARAM wParam, 
LPARAM lParam)
{
// 首先让输入管理器处理消息
if(s_pInputSystem && s_pInputSystem->MessageProc( hWnd, message, wParam, lParam ))
return 0;
// UI处理文字输入
if(s_pUISystem && s_pUISystem->MessageProc( hWnd, message, wParam, lParam ))

return 0;




9.5 处理 Size 事件
UISsytem 的 Size 在窗口的 SIZE 消息和拖动窗口边缘时进行处理。

VOID CGameProcedure::HandlePossibleSizeChange( UINT message, WPARAM wParam, LPARAM
lParam )
{
s_pGfxSystem->OnSizeChange( message, wParam, lParam );
s_pUISystem->OnSizeChange( message, wParam, lParam );


HandlePossibleSizeChange 在 WM_SIZE 和 WM_EXITSIZEMOVE 中处理

LRESULT CGameProcedure::MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
// 用户拖动窗口边缘结束
case WM_EXITSIZEMOVE:
// 窗口大小发生改变
case WM_SIZE:



9.6 窗口加载与预加载



在窗口系统(UISystem)被创建的时候创建了窗口管理器,窗口管理器初始化的时候创建了所有的窗口对象(CUIWindowItem),并对窗口进行预加载。窗口预加载过程将这个窗口对象与其对应的 lua脚本实现 c++绑定,并调用绑定的 lua 脚本中的一个命名为“窗口名+s_PreLoad”的方法进行初始化。这里的窗口是指 UISystem 窗口 CUIWindowItem,而非 cegui 窗口。窗口与 CEGUI 窗口是一一对应的关系。窗口的加载在窗口事件第一次处理时进行。这是一个简单的分散 CPU 计算的小技巧。


9.6.1 预加载

窗口预加载过程名:VOID CUIWindowItem::PreLoadWindow(VOID)

窗口预加载的工作:


  • 创建脚本运行环境
  • 加载并执行脚本
  • 绑定 C++对象,主要是 this 的绑定
  • 执行脚本中的***_PreLoad 过程
  • 将窗口加载标记设为 FLASE



9.6.2 加载

窗口加载过程名:VOID CUIWindowItem::LoadWindow(VOID)



窗口加载的工作:

  • 创建CEGUI窗口,即加载布局XML
  • 将CEGUI窗口插到顶层窗口下
  • 注册窗口控件
  • 执行脚本中的***_OnLoad 函数
  • 将窗口加载标记设为 TRUE





注册窗口控件 CUIWindowItem::_RegisterControlToScript
这个过程比较关键,主要目的文如其名即:将窗口控件注册到脚本中去。这个过程使用递归的方式
将顶层 CEGUI 窗口及其所有子窗进行注册。
注册窗口控件到脚本的工作:


  • 将 CEGUI 的 userdata 设为窗口的 THIS 指针。

              也即 CEGUI 窗口可以随时访问窗口(CUIWindowItem)内容。
  • 创建一个类似“窗口名---窗口控件”映射关系的中间对象(?),这样在销毁的时候就知道销毁那些窗口控件了。
  • 对 ActionButton 进行处理




         FalagardActionButton、FalagardMeshWindow、FalagardComplexWindow、FalagardChatHistory 等几个特殊窗口进行处理。


  • 递归注册子窗口





窗口加载实例:加载的第一个窗口


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


驱动事件

pro_event(2)
[0] EVENT 107,CHAT_ADJUST_MOVE_CTL,0 [1] EVENT 345,OBJECT_CARED_EVENT,0



9.7 注册窗口事件


_OnGameEvent


INT CUIWindowItem::LUA_RegisterEvent(LuaPlus::LuaState* pState)


LuaStack args(pState);

if(m_bLayoutLoaded)

{

KLThrow("%s Must register event in \"***PreLoad\" Function ",

m_strWindowName.c_str());

}

STRING strEventName = args[2].GetString();

g_pEventSys->RegisterEventHandle(strEventName, _OnGameEvent,

(DWORD)(DWORD_PTR)this);

return 0;

}

初始化窗口管理器


注册窗口事件


UI_CEGUI_d.dll!CUIWindowItem::LUA_RegisterEvent

UI_CEGUI_d.dll!LPCD::Object_MemberDispatcher_to_LuaState<CUIWindowItem>

LuaPlus_d.dll!0023e7fa

LuaPlus_d.dll!00263128

LuaPlus_d.dll!0023f283

LuaPlus_d.dll!00230aaa

LuaPlus_d.dll!0023e006

LuaPlus_d.dll!0023fdd4

LuaPlus_d.dll!002309ef

LuaPlus_d.dll!00234033

LuaPlus_d.dll!002341d0

LuaPlus_d.dll!0023423c

LuaPlus_d.dll!002121d2

Game_d.exe!CScriptEnvironment::DoString_WithCPPException

Game_d.exe!CScriptEnvironment::DoString_WithGenException

Game_d.exe!CScriptEnvironment::DoString

Game_d.exe!CScriptEnvironment::DoFunction

UI_CEGUI_d.dll!CUIWindowItem::PreLoadWindow

UI_CEGUI_d.dll!CUIWindowMng::Init

UI_CEGUI_d.dll!CUISystem::Initial

Game_d.exe!CGameProcedure::InitStaticMemeber

Game_d.exe!_tMain_With_CPPException

Game_d.exe!WinMain

Game_d.exe!__tmainCRTStartup




9.8 窗口控件



窗口控件定义在 LUA_CONTROL 名字下。定义文件是:
tlbb\source\wx2\wxsj2\trunk\Client\UI_CEGUI\UILuaControl.h


窗口控件类

13.jpg

9.8.1 控件 Window 的主要属性与方法

主要属性

CEGUI::Window* m_pWindow;
static LuaPlus::LuaObject* s_pMetaTable;


主要方法

static Window* CreateControl(CEGUI::Window* pWindow);
static VOID DestroyControl(Window* pThis);
static VOID RegisterMetaTable(VOID);
static VOID DestroyMetaTable(VOID);
INT getName(LuaPlus::LuaState* pState)
INT Show(LuaPlus::LuaState* pState)
INT Hide(LuaPlus::LuaState* pState);
INT GetPosition( LuaPlus::LuaState* pState );
INT SetPosition(LuaPlus::LuaState* pState);



9.8.2 类型与类对应关系
Falagard/ActionButtonActionButton
Falagard/ProgressBarProgressBar
Falagard/SuperProgressSuperProgressBar
Falagard/PopupMenuPopMenu
Falagard/RadarMinimap
Falagard/ComplexWindowComplexWindow
Falagard/ListboxListBox
Falagard/ImageListboxImageListBox
Falagard/ImageListboxExImageListBoxEx
Falagard/CheckBoxCheckButton
Falagard/ComboboxComboBox
Falagard/StaticImageStaticImage
Falagard/ScrollbarScrollBar
Falagard/SuperTooltipSuperTooltip
Falagard/MeshWindowMeshWindow
Falagard/ChatHistoryChatHistory
Falagard/ChatChannelChatChannel
Falagard/SceneMapScenemap
Falagard/WorldMapWorldmap
Falagard/InfoListInfoList
Falagard/MultiColumnListCtrlList
Falagard/SoftKeyWindowSoftKeyWindow
Falagard/AnimateAnimateWindow
Falagard/IMEEditBoxEditBox
Falagard/MultiIMEEditBoxMultiLineEditbox
Falagard/ScrollInfoScrollInfo
Falagard/TreeTree


9.8.3 绑定窗口类到脚本对象

Window::RegisterMetaTable 绑定了所有控件窗口类到 lua



窗口控件消息处理
注册到 CEGUI 的窗口处理过程如下


FalagardActionButton
subscribeDragDropStartedEvent->CUISystem::handleActionDragDropStarted
subscribeMouseEnterEvent->CUISystem::handleActionButtonMouseEnter
subscribeMouseLeaveEvent->CUISystem::handleActionButtonMouseLeave
subscribeEvent(ParentHidden,CUISystem::handleActionButtonParentHidden)
FalagardMeshWindow
subscribeShownEvent->CUISystem::handleMeshWindowShown
subscribeHidenEvent->CUISystem::handleMeshWindowHiden
FalagardComplexWindow
subscribInfoItemClickEvent->CUISystem::handleChatHistoryInfoElementClick
subscribInfoItemDeleteEvent->CUISystem::handleElementDelete
FalagardChatHistory
subscribInfoItemClickEvent->CUISystem::handleChatHistoryInfoElementClick
subscribInfoItemDeleteEvent->CUISystem::handleElementDelete
subscribInfoItemMoveInEvent->CUISystem::handleChatHistoryInfoElementMoveIn
subscribInfoItemMoveOutEvent->CUISystem::handleChatHistoryInfoElementMoveOut


9.9 窗口事件的处理

点击窗口主框架中的【系统设置】按钮,发送窗口事件,其中 this 是 windows,WindowEventArgs只是对窗口指针的封装。
WindowEventArgs args(this);
onClicked(args);



CEGUIBase_d.dll!CEGUI::PushButton::onMouseButtonUp
CEGUIBase_d.dll!CEGUI::System::injectMouseButtonUp
UI_CEGUI_d.dll!CUISystem::InjectInput
Game_d.exe!CInputSystem::Tick
Game_d.exe!CGameProcedure::Tick
Game_d.exe!CGamePro_Main::Tick
Game_d.exe!CGameProcedure::TickActive


Game_d.exe!CGameProcedure::MainLoop
Game_d.exe!_tMain_With_CPPException



窗口事件的处理
由于已经加载了脚本模块,第一时间会在脚本处理方法中进行


UI_CEGUI_d.dll!CEGUI::CGameUIScript::executeScriptedEventHandler
CEGUIBase_d.dll!CEGUI::ScriptFunctor::operator
CEGUIBase_d.dll!CEGUI::_functorBinder<CEGUI::ScriptFunctor,bool,CEGUI::EventArgs const &>::operator
CEGUIBase_d.dll!CEGUI::SubscriberTemplate<bool,CEGUI::EventArgs const &>::operator
CEGUIBase_d.dll!CEGUI::Event::operator
CEGUIBase_d.dll!CEGUI::EventSet::fireEvent_impl
CEGUIBase_d.dll!CEGUI::EventSet::fireEvent
CEGUIBase_d.dll!CEGUI::PushButton::onClicked


CEGUIBase_d.dll!CEGUI::PushButton::onMouseButtonUp
CEGUIBase_d.dll!CEGUI::System::injectMouseButtonUp
UI_CEGUI_d.dll!CUISystem::InjectInput
Game_d.exe!CInputSystem::Tick
Game_d.exe!CGameProcedure::Tick
Game_d.exe!CGamePro_Main::Tick
Game_d.exe!CGameProcedure::TickActive
Game_d.exe!CGameProcedure::MainLoop
Game_d.exe!_tMain_With_CPPException
Game_d.exe!WinMain


在 executeScriptedEventHandler 中对 cegui 事件进行解析,然后将控制权交到 CUIWindowItem,并回
传了 2 个参数:脚本事件函数和窗口参数。其中脚本事件函数是注册脚本事件时定义的,这里定义
在了 cegui 窗口布局文件里:
interface\script\mainmenubar.layout.xml


<Window Type="WoWLook/Button_1" Name="main_fuction_button_sys" >
<Property Name="HoverImage" Value="set:wxsj_maininterface image:maininterface_button_sys_h" />
<Property Name="NormalImage" Value="set:wxsj_maininterface image:maininterface_button_sys_n" />
<Property Name="PushedImage" Value="set:wxsj_maininterface image:maininterface_button_sys_p" />
<Property Name="Tooltip" Value="系统" />
<Property Name="UnifiedAreaRect"
Value="{{0.000000,766.000000},{0.000000,22.000000},{0.000000,800.000000},{0.000000,56.000000}}" />
<Event Name="Clicked" Function="main_fuction_button_sys_OnClicked()" />
</Window>



首先取到 cegui 窗口:WindowEventArgs 中的 window 即是。
然后取到 CUIWindowItem 窗口:UI 窗口指针保存在 cegui 的 userdata 里。
最后,调用 CUIWindowItem 的 FireUIEvent。
至此,鼠标事件经过一些折腾,最终回到了上层 UI 的管辖。事情还未完。
14.jpg

窗口事件的控制权已经落到上层 UI 系统了,现在只有 cegui 的事件

main_fuction_button_sys_OnClicked()

在 CUIWindowItem::FireUIEvent 中对这个脚本进行处理即大功告成。

首先将 cegui 窗口名传到 lua 里,

g_pScriptSys->GetLuaState()->GetGlobals().SetString("arg0", pWindow->getName().c_str());



此时的窗口名是 main_fuction_button_sys,
然后执行这条 lua 语句,

m_pScriptEnv->DoString(“main_fuction_button_sys_OnClicked()”);



执行这个窗口脚本
首先保持旧的环境名“Action_Env”
设置新的环境名 setmetatable(_G, {__index = MainMenuBar_Env});
执行脚本“main_fuction_button_sys_OnClicked()”
设置回旧的环境名
执行 main_fuction_button_sys_OnClicked()过程如下:


Game_d.exe!SCRIPT_SANDBOX::SystemSetup::Lua_OpenSetup
Game_d.exe!LPCD::Object_MemberDispatcher_to_LuaState<SCRIPT_SANDBOX::SystemSetup>
LuaPlus_d.dll!luaD_precall
LuaPlus_d.dll!luaV_execute
LuaPlus_d.dll!luaD_call
LuaPlus_d.dll!f_call
LuaPlus_d.dll!luaD_rawrunprotected
LuaPlus_d.dll!luaD_pcall
LuaPlus_d.dll!lua_pcall
LuaPlus_d.dll!aux_do
LuaPlus_d.dll!lua_dobuffer
LuaPlus_d.dll!lua_dostring


LuaPlus_d.dll!LuaPlus::LuaState::DoString
Game_d.exe!CScriptEnvironment::DoString_WithCPPException
Game_d.exe!CScriptEnvironment::DoString_WithGenException
Game_d.exe!CScriptEnvironment::DoString
UI_CEGUI_d.dll!CUIWindowItem::FireUIEvent
UI_CEGUI_d.dll!CEGUI::CGameUIScript::executeScriptedEventHandler
CEGUIBase_d.dll!CEGUI::ScriptFunctor::operator
CEGUIBase_d.dll!CEGUI::_functorBinder<CEGUI::ScriptFunctor,bool,CEGUI::EventArgs const &>::operator


CEGUIBase_d.dll!CEGUI::SubscriberTemplate<bool,CEGUI::EventArgs const &>::operator
CEGUIBase_d.dll!CEGUI::Event::operator
CEGUIBase_d.dll!CEGUI::EventSet::fireEvent_impl
CEGUIBase_d.dll!CEGUI::EventSet::fireEvent
CEGUIBase_d.dll!CEGUI::PushButton::onClicked
CEGUIBase_d.dll!CEGUI::PushButton::onMouseButtonUp
CEGUIBase_d.dll!CEGUI::System::injectMouseButtonUp
UI_CEGUI_d.dll!CUISystem::InjectInput
Game_d.exe!CInputSystem::Tick
Game_d.exe!CGameProcedure::Tick
Game_d.exe!CGamePro_Main::Tick
Game_d.exe!CGameProcedure::TickActive
Game_d.exe!CGameProcedure::MainLoop
Game_d.exe!_tMain_With_CPPException
Game_d.exe!WinMain


最终控制权还是回到了 SystemSetup::Lua_OpenSetup

//打开系统主界面
INT SystemSetup::Lua_OpenSetup(LuaPlus::LuaState* state)
{
CEventSystem::GetMe()->PushEvent(GE_TOGLE_SYSTEMFRAME);
return 0;
}
{ GE_TOGLE_SYSTEMFRAME, "TOGLE_SYSTEMFRAME", },



处理事件“TOGLE_SYSTEMFRAME


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
Game_d.exe!__tmainCRTStartup
Game_d.exe!WinMainCRTStartup


首先从消息参数中取出窗口

CUIWindowItem* pWinItem = (CUIWindowItem*)(DWORD_PTR)(dwOwnerData);



然后生成 lua 函数,规则是“窗口名+_OnEvent”
“GameSetup_Frame_OnEvent”
并生成参数
“"TOGLE_SYSTEMFRAME"”


最后合成 lua 脚本
“GameSetup_Frame_OnEvent("TOGLE_SYSTEMFRAME");”
执行此 lua 命令即可
GameSetup_Frame.lua

15.jpg


9.9.1 窗口事件处理的逻辑通道

Mouse cegui cegui 脚本模块 lua 脚本 事件系统 事件处理模块

16.jpg




  • 打卡等级:即来则安
  • 打卡总天数:20
  • 打卡月天数:2
  • 打卡总奖励:19
  • 最近打卡:2025-02-02 09:43:45
xiaomage

主题

0

回帖

102

积分

筑基

积分
102
xiaomage 2024-8-25 22:10 | 显示全部楼层
只身千里客,孤枕一灯秋。
您需要登录后才可以回帖 登录 | register

本版积分规则

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

GMT+8, 2025-2-22 21:39 , Processed in 0.115217 second(s), 8 queries , Redis On.

Powered by XueWu Licensed

Copyright © Tencent Cloud.

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