UI 的核心是 CUISystem
9.1 Class view
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
窗口控件类
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/ActionButton | ActionButton | Falagard/ProgressBar | ProgressBar | Falagard/SuperProgress | SuperProgressBar | Falagard/PopupMenu | PopMenu | Falagard/Radar | Minimap | Falagard/ComplexWindow | ComplexWindow | Falagard/Listbox | ListBox | Falagard/ImageListbox | ImageListBox | Falagard/ImageListboxEx | ImageListBoxEx | Falagard/CheckBox | CheckButton | Falagard/Combobox | ComboBox | Falagard/StaticImage | StaticImage | Falagard/Scrollbar | ScrollBar | Falagard/SuperTooltip | SuperTooltip | Falagard/MeshWindow | MeshWindow | Falagard/ChatHistory | ChatHistory | Falagard/ChatChannel | ChatChannel | Falagard/SceneMap | Scenemap | Falagard/WorldMap | Worldmap | Falagard/InfoList | InfoList | Falagard/MultiColumnList | CtrlList | Falagard/SoftKeyWindow | SoftKeyWindow | Falagard/Animate | AnimateWindow | Falagard/IMEEditBox | EditBox | Falagard/MultiIMEEditBox | MultiLineEditbox | Falagard/ScrollInfo | ScrollInfo | Falagard/Tree | Tree |
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 的管辖。事情还未完。
窗口事件的控制权已经落到上层 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
9.9.1 窗口事件处理的逻辑通道
Mouse cegui cegui 脚本模块 lua 脚本 事件系统 事件处理模块
|