
如何编写 Lua 脚本
这是一份指南,描述了设置编码环境以及如何为《天国:拯救》开发Lua模组的步骤。
查看大图欢迎来到如何用 Lua 编程!
这是什么?
这个条目并非一个真正的模组,而是一份关于如何为《天国:拯救》开发 Lua 模组的指南。
为什么?
这是一种定义、执行和评估逻辑语句(即编程)、与游戏世界交互、改变参数等的方式。而且,据我所知,目前没有办法在《Cry Engine》/《KC:D》作为游戏时执行自定义代码——所以这不是一个模组,只是一份文档资料!
设置环境
- 通过你选择的平台下载并安装《天国:拯救》。
- 下载 官方《天国:拯救》模组制作工具(6.4GB)。
- 将 ZIP 文件中的内容解压到你的 KCD 文件夹,使得文件夹结构如下图左侧所示:

提示:仅使用游戏本身也能进行 Lua 开发,但模组制作工具为 KCD 提供了一个替代的可执行文件,其中包含更多与 Lua 开发相关的功能。
- 接下来,定位到路径:
KingdomComeDeliverance\Bin\win64releasedll\ - 为
kingdomecome.exe创建一个快捷方式,打开该快捷方式的属性,并在快捷方式的目标末尾添加“-devmode”,如下图所示:

- 现在你可以开始了 🤓 - 启动游戏。
- 看!正如你在主菜单中看到的,你的游戏已经有些不同了——在右上角可以看到开发信息:

相比原版游戏:

如何用 Lua 编程 - 101
在德语键盘上,按脱字符键 (^) 可以打开开发者控制台,按一下它:

如你所见,即使不真正玩游戏,你也可以进行一些编码——这在你想做一些快速测试等场景下非常有用。
现在,让我们做点简单的事——每个开发者都应该能编写的最基础程序,就是在控制台打印出那句经典的“Hello World”——让我们来做这件事:在控制台中输入 #System.Log("Hello World") 并按下回车:

恭喜!你在《KC:D》中编写并执行了你的第一个 Lua 片段/程序!
如你所见,需要在实际的 Lua 代码前加上一个“#”,这样开发者控制台才能识别它——如果你不在前面加“#”,你的输入将被解释为一个引擎/游戏命令(例如 cl_fov x 用于改变视野为 x)。
开发一个 RBC
接下来要做的是进行一些基本的算术计算,比如简单的加法——输入以下代码:
# a = 1
# b = 2
# c = a+b
# System.Log(c)

看!你编写了你的第一个 RBC(真正基础的计算机),它能够计算 1+2 的结果——我现在真的有种黑客的感觉了。
升级 RBC
假设你想把你的 RBC 升级成一个更有用的工具,能够将任意两个给定的数字相加——为此我们需要一个接受两个参数并返回结果的函数(声明一下,这本身不是一个基础的 lua 或编程课程——不要期望在这方面有太多深入讲解)——所以我们输入:
function add (numberOne, numberTwo)
return numberOne+numberTwo
end
成功了!如你所见,我们可以调用 add(13,37),该方法会返回 50,你可以质疑我,但 Windows 计算器也证明了它是 50。
我们需要将 add(13,37) 用 System.Log(...) 包裹起来,因为 add 函数返回了结果,但它不会自动打印到这个控制台。
基本上就是这样了!
如何:自动启动基于 Lua 的模组
到目前为止,每次你想执行代码时,都需要从游戏内控制台调用一个方法——这对玩家来说不太友好,所以我们需要一种在游戏启动并加载了新游戏或现有游戏后自动调用代码的方式——为此,可以使用一个场景初始化监听器。
在你的模组目录下创建一个新的文本文件,并将其保存为 mod_startup.lua 或类似名称,路径如下:
KingdomComeDeliverance\mods\menu_template\Data\Scripts\Startup\mod_startup.lua
添加以下内容:
---
--- Entry point for the project
---
architect_init = {}
-- this listener gets called after a scene has been loaded or a new game started
-- we need to wait until the game is "ready" (not on the loading screen or some other 'invalid' state)
function architect_init:sceneInitListener(actionName, eventName, argTable)
if actionName == "sys_loadingimagescreen" and eventName == "OnEnd" then
Game.ShowTutorial("Ingame-Menu loaded\n", 10, false, true)
-- execute your code here
end
end
-- initialize the mod after the player-scene has started
UIAction.RegisterActionListener(architect_init, "", "", "sceneInitListener")
启动你常规的 kc:d 可执行文件,或者你添加了“-devmode”的那个,一旦你开始新游戏或加载现有游戏,你将在屏幕右上角看到以下对话框:

上面的代码列表可以作为开发基于 Lua 的模组时的起点——在第 13 行的 -- execute your code here 之后添加你的自定义 lua 代码,来自动启动你的代码——我提供了一些文件供你直接使用。
挂钩实体生命周期
一次性地执行代码来改变运行时参数是有用的,但如果我们想要不是一次性地,而是持续地/每帧执行代码呢?也许你想要持续进行射线检测,以检查玩家是否“看到”了什么东西,或者每秒检查一个条件、更新一个值等——这可以通过使用一个实体来实现。
引用自 Cry Engine 官方文档:
实体 表示世界中可以移动、行动并在逻辑上表现出行为(与不会随时间变化的岩石相对)的元素。
Cry Engine 包含一个实体组件系统(ECS),其中实体(例如玩家)由不同的组件构成。然后有一些系统会每帧更新每个实体的组件(这只是为了覆盖 ECS 的基础知识,你不需要理解这个,但如果你感兴趣,可以阅读 维基百科文章,这是一个非常有趣的话题)。
一个实体至少包含两个文件:一个 *.ent 文件和一个 Lua 文件。第一个文件定义实体本身并引用 Lua 文件,第二个文件包含实体的实际实现逻辑——这就是生命周期发挥作用的地方。这个生命周期中的一个方法是“update”方法,它会在每一帧被调用(想想每秒帧数 / fps)——这样我们就可以持续地执行代码了!
定义一个实体
在你的模组数据目录下(在 \KingdomComeDeliverance\mods\<你的模组名称>\Data\),创建一个名为“Entities”的目录,并在该目录内创建一个名为 DemoEntity.ent 的文件,添加以下内容:
<Entity
Name="DemoEntity"
Script="Scripts/Entities/Mod_DemoEntity.lua"
/>
将该文件保存在 \KingdomComeDeliverance\mods\<你的模组名称>\Data\Entities\DemoEntity.ent。这将在引擎中“注册”一个新的实体类(DemoEntity),但此时我们还不能用它做什么,因为我们需要添加实体的实现。现在文件结构如下:

为我们的实体定义添加实现
添加另一个文件,即 Lua 实现文件,位于 \KingdomComeDeliverance\mods\mod_template_entity_lifecycle\Data\Scripts\Entities\Mod_DemoEntity.lua,添加以下内容——我几乎为每一行都添加了注释,其中重要的部分是 DemoEntity.Client:OnUpdate() 函数——这将由引擎自身执行,每帧一次——只需将你的代码添加到此方法中即可每帧执行。
---
--- The DemoEntity contains its own lifecycle, this way you can hook into the update method and call your logic every frame
---
DemoEntity = {
Properties = {
bSaved_by_game = 1
bSerialize = 1
}
}
function DemoEntity:OnInit()
-- this activates the OnUpdate() function
self:Activate(1)
end
-- this is called every frame given the entity has been spawned
function DemoEntity:OnUpdate()
-- this will print the given string to the console == the console of the user gets spammed, don't do this when releasing a mod, but its helpful while in testing :)
System.LogAlways("DemoEntity onUpdate has been called!")
end
-- this is called when the player saves or updates a save state - storing values for your entities
-- this is due bSaved_by_game = 1 and bSerialize = 1
function DemoEntity:OnSave(tbl)
System.LogAlways("DemoEntity OnSave")
end;
-- this is called when the player loads a save state - use this for restoring values when a game gets loaded
function DemoEntity:OnLoad(tbl)
System.LogAlways("DemoEntity OnLoad")
end;
现在我们已经创建了一个实体定义和一个实体实现——但这还不会做任何事情,因为实体不会自动生成。所以最后一步,我们需要通过 Lua 代码来生成我们的实体,代码如下:
demoEntityParams = {}
demoEntityParams.class = "DemoEntity"
demoEntityParams.name = "DemoEntity_Instance"
demoEntityEntity = System.SpawnEntity(demoEntityParams)
System.LogAlways("Spawning DemoEntity")
只需复制整个代码,打开游戏内的开发者控制台,输入一个单独的 #,粘贴代码(通过 ctrl+v)到控制台并按下回车(代码会覆盖有效的控制台用户界面,但执行起来没有问题)——由于我在更新方法中添加了一个简单的日志语句,你可以看到开发者控制台被消息 "DemoEntity onUpdate has been called!" 刷屏了——然而,这一步和最初的模组有同样的问题:用户必须手动执行这段代码才能使用我们的功能。要解决这个问题,你可以简单地在启动脚本中生成这个实体——就像之前解释的那样,使用 mod_startup.lua 文件(我也提供了这个文件的下载)并在其中生成实体:
---
--- Entry point for the project
---
demo_mod_startup = {}
-- this listener gets called after a scene has been loaded or a new game started
-- we need to wait until the game is "ready" (not on the loading screen or some other 'invalid' state)
function demo_mod_startup:sceneInitListener(actionName, eventName, argTable)
if actionName == "sys_loadingimagescreen" and eventName == "OnEnd" then
Game.ShowTutorial("Ingame-Menu with entity loaded!", 10, false, true)
if demoEntity == nil then
demoEntityParams = {}
demoEntityParams.class = "DemoEntity"
demoEntityParams.name = "DemoEntity_Instance"
demoEntityEntity = System.SpawnEntity(demoEntityParams)
System.LogAlways("Spawning DemoEntity")
end
end
end
-- initialize the mod after the player-scene has started
UIAction.RegisterActionListener(demo_mod_startup, "", "", "sceneInitListener")
这样,在用户创建或加载游戏状态后,实体就会被生成——就是这样!现在你可以做任何《KC:D》LUA-API 能做的事了——例如,根据特定规则移动玩家的变换、每秒进行一次射线检测以在某个位置放置东西(在玩家视线位置生成一个实体——欢迎尝试!)!
我提供了另一个基础的模组模板,其中包含了代码的自动启动和自定义实体的生成——欢迎学习或将其作为你的模组的模板/起点!
文件及目录结构应如下所示:
KingdomComeDeliverance\mods\mod\<你的模组名称>\
Data\
--- Entities\
------ DemoEntity.ent - 我们实体的定义
--- Scripts\
------ Entities\
--------- Mod_DemoEntity.lua - 我们实体的实现
------ Startup\
--------- mod_startup.lua - 生成我们实体的启动脚本
mod.manifest - 包含模组名称、描述和其他信息
技巧与窍门
- 如果你使用的是官方模组制作工具(已将 6.4GB 的 SDK 解压到你的 KC:D 文件夹),请查看
\KingdomComeDeliverance\Data_reference\Scripts\目录——游戏中每个 Lua 文件都在那里,是一个很好的学习资源。 - 如果你没有使用官方模组制作工具,请查看
\KingdomComeDeliverance\Data\Scripts.pak文件,这是一个常规的压缩包,包含所有脚本,就像上面提到的目录一样。 - 仔细查看所有的 lua 文件,特别是那些
*Util.lua文件,它们包含了很多有趣和有用的内容。 - 研究其他模组,你将从别人的工作中获得很多知识,如果你使用了他们作品的部分内容,请不要忘记注明出处。
有趣的东西
由于《KC:D》使用了 CryEngine,游戏中仍然可以使用来自其他作品(如《孤岛惊魂》等)的一些“遗留物”——例如,霜冻后期处理效果——只需打开控制台,输入 #System.SetScreenFx("ScreenFrost_Amount", 1) 并亲眼看看——纳米服激活!->

额外资料
- CryEngine 的 Lua 脚本文档 - https://docs.cryengine.com/display/SDKDOC4/Lua+Scripting
- WarHorse 的 Lua ScriptBind 文档 - https://warhorse.nexusmods.com/
- 我的《KC:D》模组 Architect - https://www.nexusmods.com/kingdomcomedeliverance/mods/958
还没有人评论,去客户端里说两句吧。
评论在新手盒子客户端中发表,这里同步展示。