“游戏中调用LUA脚本的开发及调试”的版本间的差异

来自部落与弯刀Wiki

 
(未显示2个用户的11个中间版本)
第1行: 第1行:
 
===安装LUA脚本开发IDE工具===
 
===安装LUA脚本开发IDE工具===
开发者可以使用任何趁手的工具来编写LUA脚本。这里我们为了调试方便所以采用了VsCode + LuaPanda插件来编辑LUA脚本,并能实现断点调试、变量监控等功能
+
开发者可以使用任何趁手的工具来编写LUA脚本。目前推荐使用VsCode + Emmylua插件来编辑LUA脚本,并能实现断点调试、变量监控等功能
  
 
*安装微软VsCode开发工具。VSCode下载地址:https://code.visualstudio.com/
 
*安装微软VsCode开发工具。VSCode下载地址:https://code.visualstudio.com/
*在VSCode插件栏中选择LuaPanda并下载
+
*在VSCode插件栏中选择Emmylua并下载
   [[文件:Img 1.png|884x884像素]]
+
 
 +
   [[文件:Emmylua.png|700x410像素]]
 +
 
 
*由于xlua的lua文件格式是.lua.txt的,所以vscode默认情况下并不能将其识别为lua文件,需要我们通过settings来完成
 
*由于xlua的lua文件格式是.lua.txt的,所以vscode默认情况下并不能将其识别为lua文件,需要我们通过settings来完成
  
第10行: 第12行:
  
 
*打开自定义设置的JSON文件,并添加如下内容:
 
*打开自定义设置的JSON文件,并添加如下内容:
 +
 
   {
 
   {
 
     "files.associations": {
 
     "files.associations": {
第17行: 第20行:
 
   }
 
   }
 
注意:在选择配置文件时一定要选择workspace settings而不是user settings,选择之后会在工程根目录下生成一个.vscode文件夹,里面有一个settings.json;如果.lua.txt文件中出现了lua的语法高亮,则为配置成功;
 
注意:在选择配置文件时一定要选择workspace settings而不是user settings,选择之后会在工程根目录下生成一个.vscode文件夹,里面有一个settings.json;如果.lua.txt文件中出现了lua的语法高亮,则为配置成功;
   [[文件:Imgpic 3.png]]   
+
   [[文件:Imgpic 3.png|735x735像素]]   
   [[文件:Imgpic 4.png]]
+
   [[文件: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 />
 +
 
 +
=== 使用插件设置表挂载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:设置到目标值
  
*在调试选项中选择生成launch.json,然后选择luapanda;
+
fvOpVal:外交好感度操作值
  
注意:如果没有看到luapanda,需要重启一下vscode,或者将其它调试器关掉后重启;、
+
isNotify:当涉及玩家时是否进行信息播报
生成了如下三种配置,三种不同的用法之后会讲到,这里我们需要注意的设置就是luaFileExtension选项,我们需要配置成我们xlua的格式,也就是".lua.txt";
 
  [[文件:Imgpic 5.png]]
 
  
===游戏中调用LUA脚本===
+
返回值:查询结果整形数字
目前游戏中
+
|}

2024年3月26日 (二) 15:08的最新版本

安装LUA脚本开发IDE工具

开发者可以使用任何趁手的工具来编写LUA脚本。目前推荐使用VsCode + Emmylua插件来编辑LUA脚本,并能实现断点调试、变量监控等功能

 Emmylua.png  
  • 由于xlua的lua文件格式是.lua.txt的,所以vscode默认情况下并不能将其识别为lua文件,需要我们通过settings来完成
 Imgpic 2.png  
  • 打开自定义设置的JSON文件,并添加如下内容:
 {
   "files.associations": {
       "*.lua.txt":"lua"
   },
   "luaide.apiType": "xlua",
 }

注意:在选择配置文件时一定要选择workspace settings而不是user settings,选择之后会在工程根目录下生成一个.vscode文件夹,里面有一个settings.json;如果.lua.txt文件中出现了lua的语法高亮,则为配置成功;

 Imgpic 3.png  
 Imgpic 4.png

如何在MOD中运行及调试脚本

游戏中的Lua脚本代码最终必须映射到资源目录“Asset\BuildSource\LuaScripts\”下来运行。运行时可以使用 RUN_SCRIPT 或者 RUN_SCRIPT_FUNC 来调用脚本或者脚本中的函数(具体见通用剧情事件指令说明)。

如: RUN_SCRIPT_FUNC*test_script#TestFunc1

  • 如果想要调试功能能正常运行,则VsCode必须以工作区的方式打开。

从VsCode顶部 文件-将文件夹添加到工作区 或 在工程目录空白处右键,选择通过VsCode打开 来将整个工程文件夹或包含LuaScripts的目录在VSCode中打开。

打开后,我们需要保存工作区,从VsCode顶部 文件-将工作区另存为 把工作区设置储存为一个文件。此时VsCode顶部应当显示:

 338*42像素
  • 开始断点

1.按F5 或 从VsCode顶部点击 运行-启动调试 2.在如下窗口中选择EmmyLua Attach Debug

 Lua Img 3.png

3.选择附加到弯刀的游戏进程 DesertLegend.exe 上

 Lua Img 2.png

附加成功后,VsCode左侧会切换到调试栏,而底部也会变为橙色,表明此时已经进入了Debug模式。

 Lua Img 4.png


使用插件设置表挂载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$]


valKey: 类型string,用指令传入进来的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个单位)


curDay: 类型int,游戏当前天数

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,其数据结构如下:

  • 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:当涉及玩家时是否进行信息播报

返回值:查询结果整形数字