基本概念
本指南适用于想要学习在 nanos world 上开始制作模组和编写脚本的基本概念的初学者。
简介
欢迎来到 nanos world! 这是一款沙盒多人游戏,允许你创建自己的游戏模式、导入自定义资产、地图,并完全使用 Lua 脚本扩展其功能。
在深入研究模组和脚本之前,了解 nanos world 中一切是如何运作的基本概念非常重要。 本页面将为你提供所需了解的一切内容的简要说明,并提供了解每个主题的更多信息的链接!
通读完本指南后,你将准备好开始进行编程、模组制作和内容创作!
资产包
资产包是模组作者将自定义资产(如新模型、纹理、声音等)导入游戏的方式。 它们可以使用虚幻引擎创建、导出,然后作为资产包添加到 nanos world 服务器的 Assets/ 目录下!
每个资产包都有一个名为 Assets.toml 的配置文件。 该配置用于列出包中包含的所有资产、它们的路径以及其他元数据(如它们的类型、类别和标签)。 游戏利用该文件来加载资产,并通过实体使其可在 Lua 脚本中使用。
在 Lua 脚本中,某些实体需要一个资产作为参数。 例如,实体 Prop 在第三个构造函数参数中需要一个 StaticMesh Reference。
通过这种方式,我们使用资产引用来生成这样一个道具:
-- Using the default 'nanos-world' Asset Pack, which is already natively included in the game
local my_prop = Prop(Vector(), Rotator(), "nanos-world::SM_Cube")
资产包是扩展游戏和添加新功能的强大工具,对于想要为玩家创建独特且令人兴奋的体验的模组作者来说至关重要。 在我们的资产指南中进一步阅读有关资产包的信息:
资产指南core-concepts/assets包
包是 nanos world 的核心构建块,因为它们为脚本编写者提供了一种为游戏创建自定义内容和功能的方法。
包有不同的类型,每种类型都有不同的能力,包括执行 Lua 脚本、创建游戏模式、定义地图,甚至创建自定义的加载屏幕!
脚本是最常见的包类型,它允许脚本编写者在客户端和服务器上运行 Lua 代码,从而可以监听事件、与游戏类交互并以各种方式修改游戏行为。
在服务器运行期间,可以使用 package reload all 动态加载/卸载包!
每个包都是服务器 Packages/ 目录下的一个文件夹,内部包含一个配置文件 Package.toml,我们在其中定义包的类型及其相关设置。
包还可以依赖于其他的包和资产包,这允许脚本编写者跨多个包复用代码和资产。 这有助于减少重复并改善组织结构,尤其是对于较大的脚本或游戏模式。
如果我们想要加载一个包,必须在服务器的 Config.toml 中定义它们。我们为每个包类型(game_mode、loading_screen、map 和 packages)都设置了一个部分,我们必须在其中列出希望服务器加载的包名称!
总的来说,包是 nanos world 模组制作中的一个基本概念,它们提供了一种强大且灵活的方式来为游戏创建自定义内容和功能。 在我们的包指南中进一步阅读:
包指南core-concepts/packages/packages-guide脚本
脚本是编程和自定义 nanos world 的强大方式。 它允许我们监听游戏事件,随意生成实体、与之交互并改变它们。
所有 Lua 代码都通过包来执行,可以拥有仅在服务器上、仅在客户端上或在两者上运行的脚本。
学习
如果你是编程新手,YouTube、哔哩哔哩或任何课程网站都是开始学习各种编程语言编码基础知识的好地方。
对于 Lua 脚本,官方的 Lua 手册提供了关于该语言基础知识的详尽教程。
此外,我们的文档是学习 nanos world 中可用一切内容的极佳资源。 你可以浏览我们的 游戏模式和包 类别来查找真实的示例!
🎓 核心概念部分提供了指南和极好的信息,而👨💻 脚本参考则提供了关于我们 API 使用的技术细节!
此外,我们的官方 Discord 是一个活跃的社区,你可以在那里提出问题、获得帮助并与其他脚本编写者建立联系!
日志与调试
你可以使用 Console 静态类将消息输出到控制台,该类提供了不同的日志级别,例如 .Log()、.Debug()、.Warn() 和 .Error():
-- 标准控制台输出
Console.Log("普通脚本日志!")
Console.Debug("仅当日志级别 ≥ debug 时才会出现的调试消息!")
Console.Warn("警告橙色消息!将显示堆栈跟踪!")
Console.Error("错误红色消息!将显示堆栈跟踪!")
Events
在 nanos world 中,我们的 API 是基于事件的,这与其他游戏中的钩子类似。 API 中的一切都通过事件来实现,例如当一个 Player 生成时(Spawn)、一个 Character 进入载具时(EnterVehicle)或受到伤害时(TakeDamage)。 你可以使用 Lua 脚本监听这些事件并执行相应的操作。
例如,我们可以监听玩家加入服务器或实体生成的时间:
-- Subscribes when a Player joins the server
Player.Subscribe("Spawn", function(player)
Console.Log("The player %s has joined the server!", player:GetName())
end)
-- Subscribes when a Prop is spawned
Prop.Subscribe("Spawn", function(prop)
Console.Log("A prop just spawned!")
end)
在事件指南中进一步阅读有关事件的信息:
Events Guidecore-concepts/scripting/events-guideClasses
类是可以根据你的意愿生成、交互和更改的实体。 我们可以像这样轻松地生成实体:
local my_light = Light(Vector(0, 100, 100), Rotator(), Color.RED)
local my_static_mesh = StaticMesh(Vector(0, 0, 100), Rotator(), "nanos-world::SM_Cube_VR_01")
甚至可以按照我们想要的方式与现有实体进行交互和更改:
local some_prop = Prop.GetByIndex(1)
some_prop:SetMesh("nanos-world::SM_Pyramid_VR")
在类指南中进一步阅读有关类的信息:
类指南core-concepts/scripting/classes-guideStatic Classes
除了类之外,我们还有静态类,它们只是包含函数的 Lua 库,可以直接调用而不需要生成实体或拥有实例。
例如,我们可以使用 Chat 静态类发送一条聊天消息:
Chat.BroadcastMessage("你好世界!")
包文件夹结构
一个脚本包的文件夹结构包括 Server/、Client/ 和 Shared/ 文件夹。 你可以在每个文件夹中放置 .lua 文件,以便在服务器、客户端或两者(共用)上执行代码:
my-package/
├── Server/
│ ├── Index.lua
│ └── *.lua
├── Client/
│ ├── Index.lua
│ └── *.lua
├── Shared/
│ ├── Index.lua
│ └── *.lua
└── Package.toml
只有 script、game-mode 和 map 类型的包才会具有这种结构,因为只有这些类型才会运行 Lua 脚本。
包仅加载每个基础文件夹(Server/、Client/ 或 Shared/)的 Index.lua 文件,该文件负责导入其他脚本文件。
你可以在左侧边栏的**🎓 核心概念 / 脚本** 部分下找到若干脚本编写指南。
地图
地图(在虚幻中称为关卡)是游戏的入口,这是加载世界并在客户端生成实体的地方。
为了配置地图,我们创建了一个 map 类型的包。这种类型类似于 script,但其 Package.toml 具有额外的设置来配置地图,例如我们有 map_asset = "my-asset-pack::MyLevel",我们需要用它来定义该包将加载哪个地图资产。
我们可以在 Config.toml 的 map = "my-map-package" 设置下配置服务器将运行哪个地图。
我们提供了 4 个内置地图,无需任何额外的包或资产包即可加载:default-blank-map、default-empty-map、default-ocean-map 和 default-testing-map。
此外,在地图的 Package.toml 中,我们可以配置 spawn_points 和 custom_data,任何包都可以通过脚本访问它们:
local spawn_points = Server.GetMapSpawnPoints()
local custom_data = Server.GetMapConfig()
正如你所注意到的,通过加载地图,你也在加载一个带有脚本的包,因此你可以让自定义脚本针对每个地图运行。 这对于添加诸如可交互环境或门之类的自定义行为非常有用。
游戏模式
游戏模式是一种包类型,其行为与 scripts 完全相同,唯一的区别是我们在同一时间只能加载一个游戏模式,而我们可以运行任意数量的脚本。
它们应该被用来创建独特且自成一体的游戏体验。
玩家可以通过在主菜单的“新游戏”部分选择一种游戏模式来轻松选择并启动服务器。
网络
我们的客户端-服务器架构在设计时就考虑到了与网络和同步相关的一切内容的轻松和简单性。 让脚本编写者只需专注于创意部分!
在服务器上生成的所有实体也会自动在客户端上生成并同步。
当在服务器端调用实体的某个函数时,该函数也会自动在每个连接的客户端上的相同实体上调用。 使每个人始终与所有更改保持同步!
例如,如果我们生成一个 Character 并设置其位置,所有客户端的位置也将同步更新:
-- Spawns a Character (on server side)
local my_character = Character(Vector(0, 0, 0), Rotator(), "nanos-world::SK_Male")
-- Sets it's location
my_character:SetLocation(Vector(100, 100, 100))
某些类只能在服务器上生成,有些只能在客户端上生成。 它们的某些函数也是如此。 通过这种方式,如果你在客户端生成一个实体,它将仅对该客户端存在。
通过本文档,你会发现权威工具提示,告知你一个函数是可以在服务器上调用 、在客户端上调用
、在两者上调用
还是仅在生成它的那一端调用
!
两端之间的通信
为了能够将信息从服务器发送到客户端或反之亦然,我们在 Events 静态类中开放了一些特殊方法来实现这一点!
-- Subscribes to a Remote Event on client side
Events.SubscribeRemote("MyClientEvent", function(my_text)
Console.Log("Event received from server! " .. my_text)
-- outputs "Event received from server! hello nanos world!"
end)
-- 向所有客户端包中的所有玩家发送一个远程事件
Events.BroadcastRemote("MyClientEvent", Reliability.Reliable, "你好 nanos world!")
在我们的“在包之间进行通信”指南中进一步阅读有关网络事件的信息:
在包之间进行通信core-concepts/scripting/communicating-between-packages同步值
除了通过作为动态事物的事件发送数据之外,我们还可以将自定义值绑定/设置到实体上,甚至全局设置在服务器/客户端中。
例如,我们可以在任何实体上设置一个值,该值可以被任何包访问:
-- Sets a 'my_value' value to a Prop
my_prop:SetValue("my_value", 100)
-- Later on, get the value again
local my_value = my_prop:GetValue("my_value")
这将使 my_value 在该端(客户端或服务器)上全局设置在此实体上。
但我们也有能力在实体上发送一个_同步_值(如果我们处于服务器端)! 为此,我们只需附加 true 参数,告知其也要发送并同步到所有客户端!
-- Sets a synchronized 'my_value' value to a Prop
my_prop:SetValue("my_value", 100, true)
这些值可由任何包访问,是同步自定义值的绝佳方式!
在实体值指南中进一步阅读有关实体值的信息:
实体值core-concepts/scripting/entity-valuesUI
用户界面是游戏的一个至关重要的方面,我们提供了几种灵活的方式来创建 UI!
WebUI
最直接的方式是使用 WebUI,它允许你使用 HTML/CSS/JavaScript 创建和集成 UI,拥有绝对的自由度来根据需要进行创建和自定义。
可以使用事件在 Lua 和 JavaScript 之间进行通信!
有关更多信息,请参阅我们的 WebUI 教程:
Basic HUD (HTML)getting-started/tutorials-and-examples/basic-hud-htmlWidget
另一种全新且更高级的 UI 创建方式是使用虚幻 Widget! 借助它们,你可以完全访问强大的控件,甚至可以通过虚幻引擎创建自己的控件并将其导入游戏,从而提供无缝的用户体验!
有关更多信息,请参阅我们的控件类页面:
控件类scripting-reference/classes/widgetConclusion
在本指南中,我们介绍了 nanos world 脚本编写和模组制作的一些基本概念。 理解这些概念非常重要,因为它们构成了构建任何游戏模式或脚本的基础。 我们希望你现在可以开始创作内容并将你的想法变为现实!
如果你有任何疑问,请随时在我们的 Discord 上提出! 我们的社区非常棒且非常友好,我们将乐意为你提供帮助!
现在我们建议从快速开始指南开始,创建你的第一个包!
快速开始getting-started/quick-start