SOUI中的Lua脚本开发¶
Warning
The current page still doesn't have a translation for this language.
You can read it through google translate.
本文介绍如何在SOUI中使用Lua脚本进行界面开发,包括基本用法、C++对象导出到Lua以及实现类似Web开发的开发模式。
脚本系统概述¶
SOUI的脚本系统基于Lua 5.2.3,使用lua_tinker实现C++对象的导出。主要特性包括:
- 基本功能
- 使用XML描述界面布局
- 使用Lua脚本处理界面逻辑
- 动态更新界面和逻辑
-
实现类似Web开发的体验
-
C++集成
- C++代码可以调用Lua函数
- Lua脚本可以访问C++对象
- 支持事件处理和回调
-
支持对象继承关系
-
接口设计
struct IScriptModule : public IObjRef { // 获取脚本引擎 virtual void* GetScriptEngine() = 0; // 执行脚本文件 virtual void executeScriptFile(LPCSTR pszScriptFile) = 0; // 执行脚本缓冲区 virtual void executeScriptBuffer(const char* buff, size_t sz) = 0; // 执行脚本字符串 virtual void executeString(LPCSTR str) = 0; // 执行事件处理器 virtual bool executeScriptedEventHandler(LPCSTR handler_name, EventArgs *pEvt) = 0; };
启用Lua支持¶
1. 加载Lua模块¶
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
SComMgr *pComMgr = new SComMgr;
{
// 创建SOUI应用程序
SApplication *theApp = new SApplication(pRenderFactory, hInstance);
#ifdef DLL_CORE
// 加载Lua脚本模块
bool bLoaded = pComMgr->CreateScrpit_Lua((IObjRef**)&pScriptLua);
SASSERT_FMT(bLoaded, _T("load interface [%s] failed!"),
_T("scirpt_lua"));
theApp->SetScriptFactory(pScriptLua);
#endif
// ...其他初始化代码...
}
delete pComMgr;
return 0;
}
2. 在XML中添加脚本¶
<SOUI>
<script src="lua:game_logic">
<![CDATA[
-- Lua脚本代码
function on_init(args)
-- 初始化代码
end
function on_exit(args)
-- 退出清理代码
end
function on_button_click(args)
-- 按钮点击处理
return 1
end
]]>
</script>
<root on_init="on_init" on_exit="on_exit">
<!-- 界面布局 -->
</root>
</SOUI>
脚本编写¶
1. 基本结构¶
-- 全局变量定义
local win = nil
local main_wnd = nil
-- 初始化函数
function on_init(args)
win = toHostWnd(args.sender)
main_wnd = win:GetRoot()
end
-- 退出函数
function on_exit(args)
-- 清理代码
end
-- 事件处理函数
function on_button_click(args)
local btn = toSWindow(args.sender)
-- 处理按钮点击
return 1
end
2. 常用操作¶
-- 查找控件
local button = win:FindChildByNameA("btn_test", -1)
-- 修改属性
button:SetAttribute("text", "新文本")
-- 设置可见性
button:SetVisible(1, 1)
-- 移动位置
local rc = button:GetWindowRect2()
rc:OffsetRect(10, 10)
button:Move(rc)
-- 定时器操作
local timer_id = win:setInterval("on_timer", 1000)
win:clearTimer(timer_id)
3. 事件处理¶
-- 点击事件
function on_button_click(args)
local sender = toSWindow(args.sender)
-- 处理点击
return 1
end
-- 大小改变事件
function on_size_changed(args)
local rc = sender:GetWindowRect2()
-- 处理大小改变
return 1
end
-- 定时器事件
function on_timer(args)
-- 处理定时器
end
实例:跑马游戏¶
1. 界面布局¶
<include>
<window size="full,full" name="game_wnd" on_size="on_canvas_size">
<!-- 游戏场景 -->
<window name="game_canvas" clipClient="1">
<!-- 马匹 -->
<gifplayer name="player_1" float="1" skin="gif_horse">
<text pos="0,0" font="size:20">1</text>
</gifplayer>
<!-- 更多马匹... -->
</window>
<!-- 控制按钮 -->
<window name="game_toolbar">
<button name="btn_run" on_command="on_run">开始</button>
<text name="txt_coins">金币: 100</text>
</window>
</window>
</include>
2. 游戏逻辑¶
-- 全局变量
local game_state = {
coins = 100,
bet = {0,0,0,0},
running = false
}
-- 初始化
function on_init(args)
win = toHostWnd(args.sender)
game_wnd = win:FindChildByNameA("game_wnd", -1)
-- 初始化其他控件...
end
-- 开始/停止游戏
function on_run(args)
if not game_state.running then
-- 开始游戏
game_state.running = true
start_race()
else
-- 停止游戏
stop_race()
end
return 1
end
-- 下注处理
function on_bet(args)
local btn = toSWindow(args.sender)
local horse_id = btn:GetID()
if game_state.coins >= 10 then
game_state.coins = game_state.coins - 10
game_state.bet[horse_id] = game_state.bet[horse_id] + 10
update_ui()
end
return 1
end
高级用法¶
1. 模块化开发¶
-- 游戏逻辑模块
local game = {
init = function(wnd)
-- 初始化游戏
end,
start = function()
-- 开始游戏
end,
stop = function()
-- 停止游戏
end
}
-- 使用模块
function on_init(args)
game.init(toHostWnd(args.sender))
end
2. 动态UI生成¶
function create_dynamic_ui()
local parent = win:FindChildByNameA("container", -1)
-- 创建按钮
parent:CreateChildrenFromString([[
<button pos="0,0,100,30" on_command="on_dynamic_click">
动态按钮
</button>
]])
end
3. 数据绑定¶
function bind_data()
-- 创建数据模型
local model = {
score = 0,
level = 1
}
-- 更新UI
function update_ui()
local txt_score = win:FindChildByNameA("txt_score", -1)
txt_score:SetWindowText(T(model.score))
end
-- 数据变化时更新UI
function model:set_score(value)
self.score = value
update_ui()
end
end
最佳实践¶
1. 脚本组织¶
-- config.lua:配置
local config = {
GAME_TIME = 60,
MAX_SCORE = 1000
}
-- ui.lua:UI操作
local ui = {
init = function()
-- UI初始化
end
}
-- game.lua:游戏逻辑
local game = {
start = function()
-- 游戏逻辑
end
}
2. 错误处理¶
function safe_call(func, ...)
local status, err = pcall(func, ...)
if not status then
-- 错误处理
print("Error:", err)
end
end
3. 性能优化¶
-- 缓存频繁使用的对象
local cached_controls = {}
function get_control(name)
if not cached_controls[name] then
cached_controls[name] = win:FindChildByNameA(name, -1)
end
return cached_controls[name]
end
注意事项¶
- 内存管理
- 及时清理不用的定时器
- 避免循环引用
-
注意变量作用域
-
错误处理
- 使用pcall捕获错误
- 添加错误日志
-
提供错误恢复机制
-
性能考虑
- 缓存频繁使用的对象
- 避免频繁的字符串操作
- 合理使用定时器