Passer au contenu principal
Version: bleeding-edge 🩸

Concepts essentiels

Ce guide est destiné aux débutants qui souhaitent apprendre les concepts essentiels pour commencer avec le modding et le scripting sur nanos world.

Introduction​

Bienvenue sur nanos world ! Un jeu multijoueur qui vous permet de créer vos propres mode de jeux, importer des ressources personnalisés, des maps et d'étendre complétement ses fonctionnalités avec les scripts Lua.

Avant de plonger dans le modding et le scripting, il est important de comprendre les concepts essentiels de la façon dont tout fonctionne dans nanos world. Cette page vous donnera une brève explication de tout ce que vous devez savoir et des liens pour en savoir plus sur chaque sujet !

Après avoir lu ce Guide, vous serez prêt à commencer la programmation, le modding et la création de contenu !

Packs d'Assets​

Les Packs d'Assets sont le moyen pour les modders d'importer des assets personnalisés dans le jeu, tels que les nouveaux modèles, textures, sons et plus encore. They can be created using Unreal Engine, exported and then added to the server's Assets/ directory in nanos world server as an Asset Pack!

Chaque Pack d'Asset a un fichier de configuration appelé Assets.toml. Cette configuration est utilisée pour lister tous les assets inclus dans le pack, leurs chemins, et d'autres métadonnées telles que leurs types, catégories et tags. Ce fichier est utilisé par le jeu pour charger les ressources et les rendre disponibles dans les scripts Lua via les entités.

Par le biais du Lua Scripting, certaines entités nécessitent un Asset en tant que paramètre. Par exemple, l'entité Prop demande un StaticMesh Reference dans le troisième paramètre du constructeur.

De cette façon, nous faisons apparaître un Prop comme celui-ci, en utilisant la référence de l'asset:

my-package/Server/Index.lua
-- Utiliser l'Asset Pack 'nanos-world' par défaut, qui est déjà inclus nativement dans le jeu
local my_prop = Prop(Vector(), Rotator(), "nanos-world::SM_Cube")

Les packs d'Assets sont un outil puissant pour étendre le jeu et ajouter de nouvelles fonctionnalités, et ils sont essentiels pour les modders qui veulent créer une expérience unique et passionnantes pour les joueurs. En savoir plus sur les Packs d'Assets dans notre Guide d'Assets:

Guide d'Assetscore-concepts/assets

Packages​

Les Packages sont les principaux éléments de construction de nanos world, car ils fournissent un moyen aux scripteurs de créer du contenu personnalisé et des fonctionnalités pour le jeu.

Il y a différents types de packages, chaque type avec une capacité différente, y compris l'exécution de scripts Lua, la création de modes de jeu, la définition de maps et également la création d'écrans de chargements personnalisés !

Script is the most common type of Package, it allow scripters to run Lua code on both the client and server which can listen to events, interact with game classes, and modify game behavior in various ways.

tip

Les packages peuvent être chargés/déchargés dynamiquement pendant que le serveur est en route avec package reload all !

Each Package is a folder under server's Packages/ directory with a configuration file Package.toml inside, on which we define the type of the package and it's relevant settings.

Les packages peuvent aussi avoir des dépendances sur d'autres packages et Packs d'Assets, ce qui permet aux scripters de réutiliser du code et des ressources sur plusieurs paquets. Cela peut aider à réduire la duplication et améliorer l'organisation, en particulier pour les scripts plus grands ou les modes de jeu.

tip

If we want a Package to be loaded, we must define them in our Server's Config.toml, we have a section for each package type (game_mode, loading_screen, map and packages), which we must list the Package names we want the server to load!

Dans l'ensemble, les packages sont un concept essentiel dans le modding de nanos world, et ils fournissent un moyen puissant et souple de créer du contenu personnalisé et des fonctionnalités pour le jeu. En savoir plus dans notre Guide des Packages:

Guide des Packagescore-concepts/packages/packages-guide

Scripting​

Le scripting est un moyen puissant de programmer et de personnaliser nanos world. Il nous permet d'écouter les événements du jeu, d'apparaître, d'interagir et de changer les entités à volonté.

Tous les codes Lua sont exécutés par le biais de Packages, il est possible d'avoir des scripts qui s'exécute uniquement sur le serveur, sur le client ou sur les deux.

Apprentissage​

tip

Si vous débutez à la programmation, YouTube ou n'importe quel site Web de Cours sont de bons endroits pour commencer à apprendre les bases du codage dans divers langages de programmation.

Pour le scripting en Lua, le Manuel Lua officiel fournit un tutoriel complet sur les bases du langage.

Also, our documentation is a great resource for learning about everything available in nanos world. You can explore our Game-Modes and Packages category to find real examples!

The 🎓 CORE CONCEPTS section provides guides and great information whilst 👨‍💻 SCRIPTING REFERENCE provides technical details of the usage of our API!

Also, our Official Discord is an active community where you can ask questions, get help, and connect with other scripters!

Package Folder Structure​

The folder structure of a scripting Package includes Server/, Client/, and Shared/ folders. You can put .lua files in each folder to execute code on the server, client, or both (Shared):

my-package/
├── Server/
│ ├── Index.lua
│ └── *.lua
├── Client/
│ ├── Index.lua
│ └── *.lua
├── Shared/
│ ├── Index.lua
│ └── *.lua
└── Package.toml
tip

Only the Packages of type script, game-mode and map will have this structure, as those are the only ones which run Lua scripts.

The Package only loads the Index.lua file of each base folder (Server/, Client/ or Shared/), this file is responsible for importing other scripting files.

You can find several guides for scripting under 🎓 CORE CONCEPTS / Scripting section in the left sidebar.

Unreal Engine Concepts​

Some concepts that may sound confusing if you are not familiar with Unreal Engine. Since nanos world is built on top of it, we mostly adapt it's own concepts and terminologies.

Vector, Rotator and Units​

In Unreal Engine, the world is represented in a 3D space using a coordinate system. The position of objects in this space is represented using Vector, which are essentially a set of three numbers representing the X (Forward), Y (Right), and Z (Up) coordinates.

The orientation of objects is represented using Rotator, which are a set of three numbers representing the Pitch (Rotation around the Y-axis), Yaw (Rotation around the Z-axis), and Roll (Rotation around the X-axis).

Unreal Engine uses centimeters as the default unit of measurement, so when you see a Vector with values (100, 0, 0), it means that the object is 100 centimeters (or 1 meter) forward from the origin point.

Acteurs​

In Unreal Engine, an Actor is any object that can be placed in the world. It is the base class for all objects that have a physical representation in the game world, such as Character, Prop, Weapon, and more. They usually can be spawned with a location and rotation.

Players vs Characters vs Pawn​

An important concept to understand is the difference between Player, Character and Base Pawn.

In Unreal Engine, a PlayerController is the entity that represents a connected player in the server, while a Pawn is the physical representation of a player in the world, which can be controlled. And Character is a type of Pawn that has additional features such as walking, jumping, and more.

For nanos world, we simplified this concept a bit, and we just have a Player class that represents the player. And a few character classes such as Character and CharacterSimple that represent the physical representation of the player in the world.

So when a Player spawns, it means a real person just connected to the server.

Logging & Debugging​

You can output messages to the console using the Console Static Class, which provides different log levels such as .Log(), .Debug(), .Warn() and .Error():

my-package/Shared/Index.lua
-- Standard console output
Console.Log("Normal scripting logs!")
Console.Debug("Debug message that will only appear if log level is >= debug!")
Console.Warn("Warning orange message! Will show the stack trace!")
Console.Error("Error red messages! Will show the stack trace!")

Classes​

Classes are entities that can be spawned, interacted with, and changed as you wish. An example spawning a Light and StaticMesh would be like this:

my-package/Shared/Index.lua
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")

Or even interact and change the existing ones the way we want, such as with an existing Prop:

my-package/Shared/Index.lua
local some_prop = Prop.GetByIndex(1)
some_prop:SetMesh("nanos-world::SM_Pyramid_VR")
some_prop:SetLocation(Vector(100, 100, 100))

local prop_rotation = some_prop:GetRotation()

Read more about Classes in the Classes Guide:

Classes Guidecore-concepts/scripting/classes-guide

Classes statiques​

Besides Classes we also have the Static Classes, which are just Lua libraries with functions which can be called without needing to spawn an entity or have an instance.

For example we can send a Chat message using the Chat Static Class:

my-package/Server/Index.lua
Chat.BroadcastMessage("Hello world!")

Or doing a raycast with the Trace Static Class on the client side:

my-package/Client/Index.lua
local start_location = Vector(0, 0, 100)
local end_location = Vector(1000, 0, 100)
local collision_channels = CollisionChannel.WorldStatic
local trace_mode = TraceMode.DrawDebug | TraceMode.TraceComplex

-- Do the trace
local trace_result = Trace.LineSingle(start_location, end_location, collision_channels, trace_mode)

Events​

In nanos world, everything in the API is based on Events, which are similar to Hooks in other games.

For example, events are fired when a Player spawns (Spawn), a Character enters a vehicle (EnterVehicle) or takes damage (TakeDamage).

You can listen to these events using Lua scripting and perform actions accordingly. For example, this is how you know when a Player joins the server or a Prop is spawned:

my-package/Shared/Index.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 id %d just spawned!", prop:GetID())
end)

Read more about Events in the Events Guide:

Events Guidecore-concepts/scripting/events-guide

Timers​

With our Timer Static Class, you can create timers and delays to execute code after a certain amount of time has passed. This is useful for creating timed events, cooldowns, or any situation where you want to delay the execution of some code.

Our Timer API is similar to JavaScript Timers, you can use Timer.SetTimeout() to execute a function once after a delay, or Timer.SetInterval() to continuously execute a function repeatedly at a specified interval.

my-package/Shared/Index.lua
-- Runs a function every 10 seconds
Timer.SetInterval(function()
Console.Log("Triggered each 10 seconds!")
end, 10000)

-- Runs a function after 1 minute (once)
Timer.SetTimeout(function()
-- Do Something
end, 60000)

Read more about Timers in the Timer Page:

Timer Static Classscripting-reference/static-classes/timer

Finding Existing Entities​

Besides spawning an entity and storing a variable reference to it, or getting it's reference through an Event, sometimes you need to get an already spawned entity.

For example, we can get all spawned entities from a class with their static methods .GetAll() or .GetPairs():

my-package/Shared/Index.lua
-- Returns a copy of a table of all Characters spawned
local all_characters = Character.GetAll()
my-package/Shared/Index.lua
-- Efficiently iterates over all Players spawned
for _, player in pairs(Player.GetPairs()) do
Console.Log("Player %s is connected!", player:GetName())
end

Or we can get a reference to an existing Prop with a specific Index with the method .GetByIndex():

my-package/Shared/Index.lua
local my_prop = Prop.GetByIndex(10)

Another way is retrieving an entity by it's globally unique ID with the methods Server.GetEntityByID() or Client.GetEntityByID():

my-package/Server/Index.lua
local my_character = Character(Vector(), Rotator(), "nanos-world::SK_Male")
local character_id = my_character:GetID()

-- Later on...
local my_character = Server.GetEntityByID(character_id)

Retrieving a Player Character​

As explained in Players vs Characters vs Pawn section, a Player represents a connected player in the server, while a Character is the physical representation of the player in the world.

You can retrieve the Player's possessing Character with the method Player:GetControlledCharacter().

Retrieving the Local Player on Client Side​

On Client side, there are some special methods and events to retrieve the local Player: Client.GetLocalPlayer() and SpawnLocalPlayer (Client):

my-package/Client/Index.lua
local local_player = Client.GetLocalPlayer()

-- Note: the local Player is only available after the Client "SpawnLocalPlayer" event is triggered
if (local_player) then
local local_character = local_player:GetControlledCharacter()

-- Get my character's location
Console.Log("My character is at location %s", local_character:GetLocation())
end
my-package/Client/Index.lua
Client.Subscribe("SpawnLocalPlayer", function(player)
Console.Log("My Player just spawned in client side! %s", player:GetName())
-- Only now it's possible to access Client.GetLocalPlayer()
end)

Networking​

Our client-server architecture has been designed thinking about the ease and simplicity of everything related to networking and synchronization. Leaving the scripter to worry only with the creative part!

All entities that are spawned on the server are also automatically spawned and synchronized on the client.

When calling a function on an entity in the server side, this function is also automatically called on the same entity on every client connected. Making everyone always to be synchronized with all changes!

For example, if we spawn a Character, and set it's location, all Clients will have it's location updated as well:

my-package/Server/Index.lua
-- 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))

Some Classes can only be spawned on server, others only on client. The same for some functions of them. This way, if you spawn an entity in the client side, it will only exist for that client.

tip

Through this docs, you will find Authority tooltips telling whether a function can be called on server , on client , on both or only on the side which spawned it !

Communication Between Sides​

To be able to send information from the server to client or vice versa, we exposed some especial methods in our Events Static Class to allow that!

my-package/Client/Index.lua
-- 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)
my-package/Server/Index.lua
-- Sends a remote Event to all Players in all Client Packages
Events.BroadcastRemote("MyClientEvent", Reliability.Reliable, "hello nanos world!")

Read more about Networked Events in our Communicating Between Packages Guide:

Communicating Between Packagescore-concepts/scripting/communicating-between-packages

Synchronized Values​

Besides sending data through Events, which is a dynamic thing, we also have the possibility to bind/set custom values to entities, or even globally in the Server/Client.

For example we can set a value on any entity, which can be accessed by any Package:

-- 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")

This will make my_value to be set on this entity globally in this side (client or server).

But we also have the ability to send a synchronized value on an entity (if we are on server side)! For that we just append the true parameter, telling it to be sent and synchronized to all clients too!

my-package/Server/Index.lua
-- Sets a synchronized 'my_value' value to a Prop
my_prop:SetValue("my_value", 100, true)

Those values are accessible by any Package and are a great way to synchronize custom values!

Read more about Entity Values in our Entity Values Guide:

Entity Valuescore-concepts/scripting/entity-values

UI​

User Interface is an essential aspect of the game, we provide several flexible ways to create UI!

WebUI​

The most direct way is with WebUI, which allows you to create and integrate UIs using HTML/CSS/JavaScript, having absolute freedom to create and customize as you want.

It is possible to communicate between Lua and JavaScript using events!

For more information refer to our WebUI tutorial:

Basic HUD (HTML)getting-started/tutorials-and-examples/basic-hud-html

Widget​

Another new and more advanced way to create UI is using Unreal Widget! With them you can have all access to the powerful widgets and even create your own through Unreal Engine and import them in-game, providing a seamless User Experience!

For more information refer to our Widget class page:

Widget Classscripting-reference/classes/widget

Maps​

Maps (or Levels in Unreal) are the entry point of the game, this is where the world is loaded and the entities are spawned in the client side.

To configure a Map, we create a Package of type map, this type is like script, but it's Package.toml has additional settings to configure the map, e.g. we have map_asset = "my-asset-pack::MyLevel" which we need to define which Map Asset this Package will load.

We can configure which map the server will run in the Config.toml, under map = "my-map-package" setting.

tip

We provide 4 built-in Maps which can be loaded without any additional Package or Asset Pack: default-blank-map, default-empty-map, default-ocean-map and default-testing-map.

Also, in the map's Package.toml, we can configure spawn_points and custom_data, which can be accessed through scripting by any Package:

my-package/Server/Index.lua
local spawn_points = Server.GetMapSpawnPoints()
local custom_data = Server.GetMapConfig()

As you could notice, by loading a Map you are also loading a Package with scripts, so you can have custom scripts running per-map. Which can be useful to add custom behaviors such like interactable environments or doors.

Game Modes​

Game-Modes are a type of Package which behave exactly like scripts, with the exception we can just have one game-mode loaded at once, while we can have as many as scripts running as we want.

They should be used to create unique and self-contained game experiences.

Players can select and start servers easily by choosing a game-mode in the New Game section on Main Menu.

Conclusion​

In this guide, we have covered some of the essential concepts of nanos world scripting and modding. These concepts are important to understand as they form the foundation of building any game-mode or script. We hope you can now start creating content and bringing life to your ideas!

If you have any question, don't hesitate in asking questions on our Discord! Our community is amazing and very friendly, we will be glad to help you!

Now we suggest starting by our Quick Start Guide to create your first package!

Quick Startgetting-started/quick-start