“游戏中调用LUA脚本的开发及调试”的版本间的差异
Kevintien86(讨论 | 贡献) |
Skyswordkill(讨论 | 贡献) |
||
(未显示2个用户的6个中间版本) | |||
第1行: | 第1行: | ||
===安装LUA脚本开发IDE工具=== | ===安装LUA脚本开发IDE工具=== | ||
− | + | 开发者可以使用任何趁手的工具来编写LUA脚本。目前推荐使用VsCode + Emmylua插件来编辑LUA脚本,并能实现断点调试、变量监控等功能 | |
*安装微软VsCode开发工具。VSCode下载地址:https://code.visualstudio.com/ | *安装微软VsCode开发工具。VSCode下载地址:https://code.visualstudio.com/ | ||
− | * | + | *在VSCode插件栏中选择Emmylua并下载 |
− | [[文件: | + | [[文件:Emmylua.png|700x410像素]] |
*由于xlua的lua文件格式是.lua.txt的,所以vscode默认情况下并不能将其识别为lua文件,需要我们通过settings来完成 | *由于xlua的lua文件格式是.lua.txt的,所以vscode默认情况下并不能将其识别为lua文件,需要我们通过settings来完成 | ||
第23行: | 第23行: | ||
[[文件:Imgpic 4.png|568x568像素]] | [[文件:Imgpic 4.png|568x568像素]] | ||
− | * | + | === 如何在MOD中运行及调试脚本 === |
+ | 游戏中的Lua脚本代码最终必须映射到资源目录“Asset\BuildSource\LuaScripts\”下来运行。运行时可以使用 RUN_SCRIPT 或者 RUN_SCRIPT_FUNC 来调用脚本或者脚本中的函数(具体见通用剧情事件指令说明)。 | ||
+ | |||
+ | 如: RUN_SCRIPT_FUNC*test_script#TestFunc1 | ||
+ | |||
+ | *如果想要调试功能能正常运行,则VsCode必须以工作区的方式打开。 | ||
+ | 从VsCode顶部 文件-将文件夹添加到工作区 或 在工程目录空白处右键,选择通过VsCode打开 来将整个工程文件夹或包含LuaScripts的目录在VSCode中打开。 | ||
− | + | 打开后,我们需要保存工作区,从VsCode顶部 文件-将工作区另存为 把工作区设置储存为一个文件。此时VsCode顶部应当显示: | |
− | + | [[文件:Lua Img 1.png|338*42像素]] | |
− | [[文件: | ||
− | + | *开始断点 | |
− | + | 1.按F5 或 从VsCode顶部点击 运行-启动调试 | |
+ | 2.在如下窗口中选择EmmyLua Attach Debug | ||
+ | |||
+ | [[文件:Lua Img 3.png|554x126像素]] | ||
− | + | 3.选择附加到弯刀的游戏进程 DesertLegend.exe 上 | |
− | + | [[文件:Lua Img 2.png|554x94像素]] | |
− | + | 附加成功后,VsCode左侧会切换到调试栏,而底部也会变为橙色,表明此时已经进入了Debug模式。 | |
− | + | [[文件:Lua Img 4.png|489x340像素]] | |
<br /> | <br /> | ||
+ | |||
+ | === 使用插件设置表挂载Lua逻辑 === | ||
+ | 当需要将自定义的Lua逻辑挂载到游戏中时,可以通过插件设置表来实现。通过指定要挂载的逻辑,并绑定挂载的函数后,游戏会在对应逻辑执行时,自动执行挂载逻辑。 | ||
+ | |||
+ | 推荐使用该方式新增Lua逻辑,以避免Lua脚本的冲突 | ||
+ | |||
+ | 在插件设置表中可配置的Lua挂载逻辑如下 | ||
+ | {| class="wikitable" | ||
+ | |+ | ||
+ | !类型 | ||
+ | !目标字段 | ||
+ | !目标值 | ||
+ | !函数范例 | ||
+ | !说明 | ||
+ | |- | ||
+ | |ADD | ||
+ | |on_game_start | ||
+ | |脚本名#函数名 | ||
+ | |<syntaxhighlight lang="lua" > | ||
+ | function onGameStart() | ||
+ | -- 进入游戏时进行的操作 | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |进入游戏或载入游戏后运行 | ||
+ | |- | ||
+ | |ADD | ||
+ | |get_lua_intAD | ||
+ | |脚本名#函数名 | ||
+ | |<syntaxhighlight lang="lua" > | ||
+ | function GetLuaIntVal(valKey, contextArgVal) | ||
+ | if string.find(valKey,'test=') ~= nil then | ||
+ | local key = string.sub(valKey, 6) | ||
+ | return tonumber(key) | ||
+ | elseif valKey == "rand100" then | ||
+ | return math.random(1, 100) | ||
+ | end | ||
+ | return 0 | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |通过lua实现自定义的整形变量查询,对应查询指令[$lua_int:KEY$] | ||
+ | |||
+ | |||
+ | valKey: 类型string,用指令传入进来的KEY | ||
+ | |||
+ | contextArgVal: 类型RuntimeArgVals,当前脚本的环境参数 | ||
+ | |||
+ | 返回0代表查询没有结果,因此将会继续调用其他的lua查询挂载函数,直到获得有效结果为止。 | ||
+ | 返回非0数值会立即结束查询,并将该值返回到查询指令的结果当中。 | ||
+ | |- | ||
+ | |ADD | ||
+ | |get_lua_str | ||
+ | |脚本名#函数名 | ||
+ | |<syntaxhighlight lang="lua" > | ||
+ | function GetLuaStringVal(valKey, contextArgVal) | ||
+ | if valKey == "test" then | ||
+ | return "ok" | ||
+ | end | ||
+ | return "" | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |通过lua实现自定义的字符串变量查询,对应查询指令[$lua_str:KEY$] | ||
+ | |||
+ | valKey: 类型string,用指令传入进来的KEY | ||
+ | |||
+ | contextArgVal: 类型RuntimeArgVals,当前脚本的环境参数 | ||
+ | |||
+ | 返回nil或空字符串代表查询没有结果,因此将会继续调用其他的lua查询挂载函数,直到获得有效结果为止。 | ||
+ | 返回非空字符串会立即结束查询,并将该值返回到查询指令的结果当中。 | ||
+ | |- | ||
+ | |ADD | ||
+ | |game_hour_logics | ||
+ | |脚本名#函数名 | ||
+ | |<syntaxhighlight lang="lua" > | ||
+ | function OnGameHourLogic(curDay, curH) | ||
+ | if curH == 7 then | ||
+ | --每天固定时间检测队伍成员好感度 | ||
+ | PlayerPartyMemberLeaveCheck(curDay); | ||
+ | elseif curH == 9 then | ||
+ | --阵营势力自定义逻辑遍历 | ||
+ | CampDailyUpdateLogic(); | ||
+ | end | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |游戏中游戏时间每过1个单位执行的逻辑(每天12个单位) | ||
+ | |||
+ | |||
+ | curDay: 类型int,游戏当前天数 | ||
+ | |||
+ | curHour: 类型int,游戏当前天单位数,游戏内一天12个单位 | ||
+ | |||
+ | 在该接口中尽量不要集中进行逻辑判断,或者尽量将逻辑部分分散到各个单位之中,以避免游戏卡顿 | ||
+ | |- | ||
+ | |ADD | ||
+ | |camp_daily_logics | ||
+ | |脚本名#函数名 | ||
+ | |<syntaxhighlight lang="lua" > | ||
+ | function OnCampDailyLogic(tagCamp, curDay) | ||
+ | -- 进行阵营逻辑判断 | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |游戏内阵营每日执行逻辑 | ||
+ | |||
+ | tagCamp: 类型HanFramework.GameCampRtData,当前进行逻辑判断的阵营 | ||
+ | |||
+ | curDay: 类型int,游戏当前天数 | ||
+ | |||
+ | 只有参与游戏斗争的已激活阵营才会执行阵营逻辑 | ||
+ | |} | ||
+ | |||
+ | === 如何重写游戏核心逻辑脚本 === | ||
+ | 在0.7.4.1版本以后,我们逐渐将游戏的一些核心逻辑放到LUA脚本中,这样MOD制作者就可以改写自己的一些游戏核心逻辑(如外交、内政、好感度系统等)。后续还将陆续开放战斗数值结算等相关的核心逻辑接口,以给予MOD制作者更大的发挥创意的空间。 | ||
+ | |||
+ | 目前游戏内置的主要逻辑接口代码都写在位于映射路径“Asset\BuildSource\LuaScripts\GameLogics.lua.txt”的LUA脚本中(源代码可在游戏根目录下\ModSamples\LuaScripts\文件夹中找到),通过MOD设置中的override功能映射该路径就可以对指定的LUA脚本进行替换重写了。 | ||
+ | |||
+ | GameLogics.lua.txt脚本中的主要代码接口有: | ||
+ | {| class="wikitable" | ||
+ | |+ | ||
+ | !接口方法 | ||
+ | !用途 | ||
+ | !参数说明 | ||
+ | |- | ||
+ | |OnGameStart() | ||
+ | |每次进入游戏(或是载入游戏后)会调用此接口 | ||
+ | |无参数 | ||
+ | |- | ||
+ | |GetLuaStringVal(valKey, contextArgVal) | ||
+ | |[$lua_str:KEY$]查询指令对应的接口 | ||
+ | |valKey:查询字段的KEY | ||
+ | contextArgVal:查询指令调用时的环境变量参数 | ||
+ | |||
+ | 返回值:查询结果字符串 | ||
+ | |- | ||
+ | |GetLuaIntVal(valKey, contextArgVal) | ||
+ | |[$lua_int:KEY$]查询指令对应的接口 | ||
+ | |valKey:查询字段的KEY | ||
+ | contextArgVal:查询指令调用时的环境变量参数 | ||
+ | |||
+ | 返回值:查询结果整形数字 | ||
+ | |- | ||
+ | |SetRoleRep(opRole, targetType, tagID, opVal, isAdd, isNotify, isChainedMode) | ||
+ | |游戏内为角色改变对应声望的接口 | ||
+ | |opRole:执行操作的角色对象 | ||
+ | targetType:操作对象类型 0:阵营 1:角色 2:地点 | ||
+ | |||
+ | tagID:操作对象的ID | ||
+ | |||
+ | opVal:操作值 | ||
+ | |||
+ | isAdd:是否改变模式,当为true时在原值上加减,否则为直接设置到目标值 | ||
+ | |||
+ | isNotify:当涉及主角时是否进行信息播报 | ||
+ | |||
+ | isChainedMode:是否进行链式操作(关联好感度的对象进行改变) | ||
+ | |||
+ | 返回值:查询结果整形数字 | ||
+ | |- | ||
+ | |GivePresentLogic(contextArgVal) | ||
+ | |给NPC送礼物涨好感度的逻辑接口 | ||
+ | |contextArgVal:调用此逻辑时的环境变量参数 | ||
+ | 返回值:无 | ||
+ | |- | ||
+ | |OnCampDailyLogic(tagCamp, curDay) | ||
+ | |阵营每日逻辑(游戏中每天都会对所有阵营执行此逻辑接口) | ||
+ | |无参数和返回值 | ||
+ | |- | ||
+ | |OnAIDecideDipEvent(execCamp, dipInfo) | ||
+ | |AI势力决定外交行为的判断(玩家处理外交行为逻辑通过处理 dip_event 触发器事件来决定) | ||
+ | |execCamp:当前执行此外交事件的阵营对象 | ||
+ | dipInfo:当前执行外交事件的详细信息,类型为DiplomaticEventInfo,其数据结构如下: | ||
+ | |||
+ | * public GameCampRtData fromCamp; | ||
+ | * public GameCampRtData execCamp; | ||
+ | * public int dipType; | ||
+ | * public string tagRoleID; | ||
+ | * public string argsVal; | ||
+ | * public float resultRatio; | ||
+ | * public bool isDecided; | ||
+ | |||
+ | 返回值:无 | ||
+ | |- | ||
+ | |OnDipEventCallback(dipResult, dipInfo) | ||
+ | |游戏中所有阵营执行外交事件处理结果接口 | ||
+ | |dipResult:外交事件结果 true 同意 false 拒绝 | ||
+ | dipInfo:同上 | ||
+ | |||
+ | 返回值:无 | ||
+ | |- | ||
+ | |SetCampRl(campID1, campID2, rlState, fvOpMode, fvOpVal, isNotify) | ||
+ | |游戏中所有操作阵营外交关系时执行的接口 | ||
+ | |campID1:外交关系对象A | ||
+ | campID2:外交关系对象B | ||
+ | |||
+ | rlState:欲改变至的外交状态 -1:不改变 0:中立 1:敌对 2:结盟 | ||
+ | |||
+ | fvOpMode:外交好感度的操作方式:0:不改变 1:增加 2:设置到目标值 | ||
+ | |||
+ | fvOpVal:外交好感度操作值 | ||
+ | |||
+ | isNotify:当涉及玩家时是否进行信息播报 | ||
+ | |||
+ | 返回值:查询结果整形数字 | ||
+ | |} |
2024年3月26日 (二) 15:08的最新版本
安装LUA脚本开发IDE工具
开发者可以使用任何趁手的工具来编写LUA脚本。目前推荐使用VsCode + Emmylua插件来编辑LUA脚本,并能实现断点调试、变量监控等功能
- 安装微软VsCode开发工具。VSCode下载地址:https://code.visualstudio.com/
- 在VSCode插件栏中选择Emmylua并下载
- 由于xlua的lua文件格式是.lua.txt的,所以vscode默认情况下并不能将其识别为lua文件,需要我们通过settings来完成
- 打开自定义设置的JSON文件,并添加如下内容:
{ "files.associations": { "*.lua.txt":"lua" }, "luaide.apiType": "xlua", }
注意:在选择配置文件时一定要选择workspace settings而不是user settings,选择之后会在工程根目录下生成一个.vscode文件夹,里面有一个settings.json;如果.lua.txt文件中出现了lua的语法高亮,则为配置成功;
如何在MOD中运行及调试脚本
游戏中的Lua脚本代码最终必须映射到资源目录“Asset\BuildSource\LuaScripts\”下来运行。运行时可以使用 RUN_SCRIPT 或者 RUN_SCRIPT_FUNC 来调用脚本或者脚本中的函数(具体见通用剧情事件指令说明)。
如: RUN_SCRIPT_FUNC*test_script#TestFunc1
- 如果想要调试功能能正常运行,则VsCode必须以工作区的方式打开。
从VsCode顶部 文件-将文件夹添加到工作区 或 在工程目录空白处右键,选择通过VsCode打开 来将整个工程文件夹或包含LuaScripts的目录在VSCode中打开。
打开后,我们需要保存工作区,从VsCode顶部 文件-将工作区另存为 把工作区设置储存为一个文件。此时VsCode顶部应当显示:
- 开始断点
1.按F5 或 从VsCode顶部点击 运行-启动调试 2.在如下窗口中选择EmmyLua Attach Debug
3.选择附加到弯刀的游戏进程 DesertLegend.exe 上
附加成功后,VsCode左侧会切换到调试栏,而底部也会变为橙色,表明此时已经进入了Debug模式。
使用插件设置表挂载Lua逻辑
当需要将自定义的Lua逻辑挂载到游戏中时,可以通过插件设置表来实现。通过指定要挂载的逻辑,并绑定挂载的函数后,游戏会在对应逻辑执行时,自动执行挂载逻辑。
推荐使用该方式新增Lua逻辑,以避免Lua脚本的冲突
在插件设置表中可配置的Lua挂载逻辑如下
类型 | 目标字段 | 目标值 | 函数范例 | 说明 |
---|---|---|---|---|
ADD | on_game_start | 脚本名#函数名 | function onGameStart()
-- 进入游戏时进行的操作
end |
进入游戏或载入游戏后运行 |
ADD | get_lua_intAD | 脚本名#函数名 | function GetLuaIntVal(valKey, contextArgVal)
if string.find(valKey,'test=') ~= nil then
local key = string.sub(valKey, 6)
return tonumber(key)
elseif valKey == "rand100" then
return math.random(1, 100)
end
return 0
end |
通过lua实现自定义的整形变量查询,对应查询指令[$lua_int:KEY$]
contextArgVal: 类型RuntimeArgVals,当前脚本的环境参数 返回0代表查询没有结果,因此将会继续调用其他的lua查询挂载函数,直到获得有效结果为止。 返回非0数值会立即结束查询,并将该值返回到查询指令的结果当中。 |
ADD | get_lua_str | 脚本名#函数名 | function GetLuaStringVal(valKey, contextArgVal)
if valKey == "test" then
return "ok"
end
return ""
end |
通过lua实现自定义的字符串变量查询,对应查询指令[$lua_str:KEY$]
valKey: 类型string,用指令传入进来的KEY contextArgVal: 类型RuntimeArgVals,当前脚本的环境参数 返回nil或空字符串代表查询没有结果,因此将会继续调用其他的lua查询挂载函数,直到获得有效结果为止。 返回非空字符串会立即结束查询,并将该值返回到查询指令的结果当中。 |
ADD | game_hour_logics | 脚本名#函数名 | function OnGameHourLogic(curDay, curH)
if curH == 7 then
--每天固定时间检测队伍成员好感度
PlayerPartyMemberLeaveCheck(curDay);
elseif curH == 9 then
--阵营势力自定义逻辑遍历
CampDailyUpdateLogic();
end
end |
游戏中游戏时间每过1个单位执行的逻辑(每天12个单位)
curHour: 类型int,游戏当前天单位数,游戏内一天12个单位 在该接口中尽量不要集中进行逻辑判断,或者尽量将逻辑部分分散到各个单位之中,以避免游戏卡顿 |
ADD | camp_daily_logics | 脚本名#函数名 | function OnCampDailyLogic(tagCamp, curDay)
-- 进行阵营逻辑判断
end |
游戏内阵营每日执行逻辑
tagCamp: 类型HanFramework.GameCampRtData,当前进行逻辑判断的阵营 curDay: 类型int,游戏当前天数 只有参与游戏斗争的已激活阵营才会执行阵营逻辑 |
如何重写游戏核心逻辑脚本
在0.7.4.1版本以后,我们逐渐将游戏的一些核心逻辑放到LUA脚本中,这样MOD制作者就可以改写自己的一些游戏核心逻辑(如外交、内政、好感度系统等)。后续还将陆续开放战斗数值结算等相关的核心逻辑接口,以给予MOD制作者更大的发挥创意的空间。
目前游戏内置的主要逻辑接口代码都写在位于映射路径“Asset\BuildSource\LuaScripts\GameLogics.lua.txt”的LUA脚本中(源代码可在游戏根目录下\ModSamples\LuaScripts\文件夹中找到),通过MOD设置中的override功能映射该路径就可以对指定的LUA脚本进行替换重写了。
GameLogics.lua.txt脚本中的主要代码接口有:
接口方法 | 用途 | 参数说明 |
---|---|---|
OnGameStart() | 每次进入游戏(或是载入游戏后)会调用此接口 | 无参数 |
GetLuaStringVal(valKey, contextArgVal) | [$lua_str:KEY$]查询指令对应的接口 | valKey:查询字段的KEY
contextArgVal:查询指令调用时的环境变量参数 返回值:查询结果字符串 |
GetLuaIntVal(valKey, contextArgVal) | [$lua_int:KEY$]查询指令对应的接口 | valKey:查询字段的KEY
contextArgVal:查询指令调用时的环境变量参数 返回值:查询结果整形数字 |
SetRoleRep(opRole, targetType, tagID, opVal, isAdd, isNotify, isChainedMode) | 游戏内为角色改变对应声望的接口 | opRole:执行操作的角色对象
targetType:操作对象类型 0:阵营 1:角色 2:地点 tagID:操作对象的ID opVal:操作值 isAdd:是否改变模式,当为true时在原值上加减,否则为直接设置到目标值 isNotify:当涉及主角时是否进行信息播报 isChainedMode:是否进行链式操作(关联好感度的对象进行改变) 返回值:查询结果整形数字 |
GivePresentLogic(contextArgVal) | 给NPC送礼物涨好感度的逻辑接口 | contextArgVal:调用此逻辑时的环境变量参数
返回值:无 |
OnCampDailyLogic(tagCamp, curDay) | 阵营每日逻辑(游戏中每天都会对所有阵营执行此逻辑接口) | 无参数和返回值 |
OnAIDecideDipEvent(execCamp, dipInfo) | AI势力决定外交行为的判断(玩家处理外交行为逻辑通过处理 dip_event 触发器事件来决定) | execCamp:当前执行此外交事件的阵营对象
dipInfo:当前执行外交事件的详细信息,类型为DiplomaticEventInfo,其数据结构如下:
返回值:无 |
OnDipEventCallback(dipResult, dipInfo) | 游戏中所有阵营执行外交事件处理结果接口 | dipResult:外交事件结果 true 同意 false 拒绝
dipInfo:同上 返回值:无 |
SetCampRl(campID1, campID2, rlState, fvOpMode, fvOpVal, isNotify) | 游戏中所有操作阵营外交关系时执行的接口 | campID1:外交关系对象A
campID2:外交关系对象B rlState:欲改变至的外交状态 -1:不改变 0:中立 1:敌对 2:结盟 fvOpMode:外交好感度的操作方式:0:不改变 1:增加 2:设置到目标值 fvOpVal:外交好感度操作值 isNotify:当涉及玩家时是否进行信息播报 返回值:查询结果整形数字 |