Categories
Advanced Tutorials Uncategorized

Adding editor properties to Crayta scripts

Preface

All Crayta scripts can have properties that can be edited directly in the editor. This is a powerful concept both for easily testing values, and reusing the script on different entities to produce new kinds of behavior each time.

In this tutorial we will learn how to add properties in scripts as well as learn all the different types of properties available.

If you are coming from Unity to Crayta you will recognize this concept and find that it works in a similar manner.

In Unity to expose properties to the editor you add public properties to your script class (or use the SerializeField attribute). In Crayta you add a child table to the Script.Properties table of your script.

In Unity the exposed properties appear in the Inspector window in the editor, in Crayta in the Properties window.

In Unity you can use editable properties to edit values or reference objects, in Crayta you can do the same.

Adding properties in a script

When creating a new script, Crayta will add by default an empty table named Properties (often called the property bag) to your script.

If your script is named HealthScript then the property bag will be initially an empty Lua table:

Health.Properties = {}

To add a new property you append a child table (a Lua associative array) to the properties table with a number of options (key/values) in it:

Health.Properties = {
   { name = "maxHealth", type = "number"}
}

Below you can see the property bag of the main script used in the Health package.

And here is how these properties are exposed in the editor:

Property options

There are different types of properties (text, number, entity, template etc) which we will explain in detail in the next section. Though there are a number of property options that are available to all property types:

  • name – string
    • The property name that will appear in the editor.
  • type – string.
    • All available types are explained in the next section.
  • tooltip – string
    • A tooltip message that appears when hovering over the element in editor.
  • editable – bool (default:true)
    • False hides the property in the editor UI. Useful if you want a property just for replicating data to clients, and want to keep the UI tidy by preventing the user from being able to see/edit it.
  • editableInBasicMode – bool (default:true)
    • False hides the property, but only in basic mode.
  • container – string (default:nil)
    • Possible values: “” (empty string or nil), “array”
    • Setting this to “array” will present the property as an array of the given type, allowing the user to add and remove values. Each array element, and the “length” field is inherited from a template individually.
  • visibleIf – function (default: nil)
               function(properties) return 
                  properties.allowRespawns == true
               end

This function should return true when the property should be shown on the UI, false to hide it. Useful for keeping the UI tidy by hiding irrelevant options. The function’s properties parameter provides access to the other properties (just as you would access with self.properties in a script)

Property types

  • number
    • Optional extra attributes:
      • default – number (0)
      • min/max – number – sets the min/max value that can be entered into the editor UI. Doesn’t apply at runtime.
      • stepSpeed – number (default: 1) – adjusts how fast the number changes with a gamepad
      • slowStepSpeed – number (default: 0.1) – as above, but when pressing the ‘slow’ modifier
      • fastStepSpeed – number (default: 10) – as above, but when pressing the ‘fast’ modifier
      • allowFloatingPoint – bool (default: true) – allow/disallow decimal numbers in the editor UI. Doesn’t apply at runtime.
      • options – table, a list of allowed values, either named: {Slow = 0.25, Normal = 1, Fast = 2, Ludicrous = 100}, or unnamed: {0.25, 1, 2, 100}
      • editor – string (default: chosen automatically based on other settings; either enum, int or float). Changes the widget displayed in the editor.
      • Possible values: int, float, seconds, days, slider
  • string
    • Optional extra attributes:
      • default – string (“”)
      • options – table – a list of allowed values, either named: {Monday = “Mon”, Tuesday = “Tue”, Wednesday = “Wed”}, or unnamed: {“Monday”, “Tuesday”, “Wednesday”}
  • text
    • Similar to string, but localisable. Uses a Text type at runtime.
  • boolean
    • Optional extra attributes:
      • default – boolean (false)

  • vector
    • Optional extra attributes:
      • default – Vector.Zero or any other Vector value using Vector.New(1,2,3)
      • stepSpeed – number (default: 1) – adjusts how fast the number changes with a gamepa
      • slowStepSpeed – number (default: 0.1) – as above, but when pressing the ‘slow’ modifie
      • fastStepSpeed – number (default: 10) – as above, but when pressing the ‘fast’ modifier
  • vector2d
    • Optional extra attributes:
      • default – Vector2D.Zero or any other Vector2D value using Vector2D.New(1,2)
      • stepSpeed – number (default: 1) – adjusts how fast the number changes with a gamepa
      • slowStepSpeed – number (default: 0.1) – as above, but when pressing the ‘slow’ modifie
      • fastStepSpeed – number (default: 10) – as above, but when pressing the ‘fast’ modifier
  • rotation
    • Optional extra attributes:
      • default – Rotation.Zero or any other Rotation value using Rotation.New(0,90,0)
      • stepSpeed – number (default: 1) – adjusts how fast the number changes with a gamepa
      • slowStepSpeed – number (default: 0.1) – as above, but when pressing the ‘slow’ modifie
      • fastStepSpeed – number (default: 10) – as above, but when pressing the ‘fast’ modifier
  • entity
    • Optional extra attributes:
      • is – string (default: “”) – require the entity to be of a specific physical type: Character, User, Mesh, Light, Sound, Effect, Voxels, Trigger, Locator, Camera. This doesn’t apply at runtime.
  • color
    • Optional extra attributes:
      • default – Color.New(1, 1, 1, 1)
  • event
    • An event property allows you to broadcast a message to a number of listeners all at once.
    • Each event can have one or more listeners. 
    • Using the Send function available on the event will trigger all listeners, passing on any parameters given to it:
       -- using the send method on an event property
       self.properties.myEvent:Send(myParam)

There are two ways to setup a listener in the editor:

  1. Specifying an entity in the world (or in the same template) like a normal entity property.
  2. Specifying an entity within any template.

Then as a final step you choose a script on that entity and a function within that script. In the first case, which is the simplest, only the specified entity will receive the event (when using the send method).

In the second case, the event will be broadcasted to every instance of that entity in the world, including any entity that is spawned after the game starts. This is quite powerful when you have to inform all entities of the same type for a certain event e.g. all chests have to refill or all doors have to close.

For more advanced uses, there is a Listen method available on the event property which allows you to add a listener to an event procedurally, in code:

gameManager.properties.OnMatchStart:Listen(self, "SwitchOn")

This method accepts two arguments:

  • A script component, usually that would be set to self, the script where this method is used from.
  • A function name, which will be the method in that script which will be called when the event is broadcasted.
  • [asset type]
    • The following asset types can be easily referenced by any script using a property with one of the following types:
      • template, voxel, mesh, sound, world, effect, script, widget, skydome, innerhorizon, outerhorizon, grip, colorgrading, postprocess, skymesh, skylayer, voxelmesh
MyScript.Properties = {
    { name = "greenVoxel", type = "voxel"}
}

Here is how a script exposing multiple properties to the editor, looks like:

Categories
Advanced Tutorials

Using the Inventory Package

Preface

In this tutorial we will learn how to use the Inventory package provided by Crayta to help us easily set up a system to manage a player’s inventory.

Crayta has an inbuilt Asset Library which provides easy access to packages provided by the Crayta team or Crayta users.

If you are coming from a Unity background this is similar to the Unity Asset Store. A Unity Package downloaded from their store will be a Crayta Package, that is a collection of templates, scripts, widgets etc that can be easily shared between Crayta projects to enhance the functionality of your game.

The Crayta team provides and maintains a collection of packages (Game, Shop, Inventory etc) that can help you easily put in place the basic functionality for a game or learn by studying how they work.

Installing the Inventory Package

Using the Community Tab of the Asset Library window in the editor we can easily install the inventory package to any game by selecting it from the packages list and hitting install.

Crayta will download and install the package to your game alongside any dependencies required. You can now see what the package has imported to your project by selecting and expanding it in the Packages section of the library window:

Structure of the Inventory Package

Let’s take a look at the contents of the package that are now available to be used in our game. A Crayta package can contain any kind of assets that can be authored in the editor (meshes, effects, scripts, templates etc.). In this case the Inventory package imported two templates, a widget and a number of scripts.

Templates

  • User Inventory, is a simple template that holds the main inventoryScript component and an example of setting a default item.
  • Player Inventory View, is a template containing the widget/UI logic for the inventory.

Those templates provide a basic layout on how to structure your inventory logic, you can use them as they are by dragging them straight onto the User and Player template accordingly or attaching the required scripts and widgets yourself to the User/Player entity.

Widget

  • inventoryViewWidget, this is a Crayta widget asset (a file containing HTML/CSS/Javascript) which is responsible for rendering the inventory interface on screen. This widget has to be attached to the Player template to work.

The default layout renders a configurable number of slots at the bottom of the screen, each slot can be empty or hold an item. Using HTML/CSS you can easily add your own styling to the interface.

Scripts

  • inventoryScript, this is the heart of the inventory system, this script is responsible for initialising and managing all inventory items. To use this script attach it to the User template.
  • inventoryDefaultScript, this is an optional script used to set the default items in inventory slots that are available when the game starts. You can attach this on any entity (the inventoryScript can be configured where to look for default items) or add it to the User template.
  • inventoryViewScript, this script is responsible for communicating all inventory logic to the inventory widget for rendering. It also handles equipping/unequipping items and passing through inputs / button presses to the currently equipped item. It should be attached together with the widget to the Player template.
  • inventoryItemSpecScript, is used to define an item which can be picked up by the Player and added to the inventory. It should be attached to a Template.
  • pickupSpawnerScript, this is an optional script used to handle spawning and respawning pickups of items that can be picked by the Player and added to the inventory. It can be either attached to a Template to use that template as pickup or to any entity and be configured to use a template as the pickup item.

Most of the methods in the inventory scripts have detailed comments explaining how they work and what they do, making it easy to understand and extend their behavior.

How to setup the inventory

Let’s examine now how we can start using those scripts to setup a basic inventory easily and quickly in our game.

Putting the scripts in place

After installing the package we should start by adding the base inventory scripts and widgets in place. We can do that easily by:

  1. Dragging and dropping the User Inventory template from the inventory package to our User template.
  2. Dragging and dropping the Player Inventory View template from the inventory package to our Player template.

Those two templates will add script folders with the inventory scripts to our User/Player templates.

Running the game by pressing the preview button (Key: F5) will render the inventory automatically.

Adding items to the inventory

There are 2 steps to making an item become collectable: first we have to turn the item into a template, and then we need to add the appropriate scripts to it.

Step 1: Select your item (in our example it’s a ChineseLantern) and then select Template->Create New Template. Give your new template a name (we’ve called ours ‘Lamp’).

Create a new template of your collectable item.

Step 2: At the top of the world tree you need to click the dropdown to change the view from World to your template:

Click the dropdown to select your template.

You now need to add the inventoryItemSpecScript to your template:

  • Select Entity->Add->Script->inventoryItemSpecScript

The inventoryItemSpecScript makes it easy to make any template in our game ready to be added to the inventory. We can give it a freindly name (such as ‘Lamp’), choose whether it becomes a stacked item, and what grip the player’s character should use when they carry it.

This script adds the required properties to any template so that it can now be added to the player’s inventory.

To actually appear in the Player’s inventory, we have two options – let’s explore both.

Option 1: Start with the item in the inventory

Adding the User Inventory template to our User template created a new script folder called Defaults

Add an inventoryDefaultScript component to this folder, and set the template to the one for your item (i.e. Lamp). You should now start with the item already in the player’s inventory. Here you can also configure the quantity of this item the User will start with by playing with the count property.

Running the game now you will see an item being automatically added to the inventory. The Player carries it on its assigned grip position. If no grip position is selected, the item will attach to the right hand by default.

Option 2: Add pickup items to the game

Having the Player carrying all the items from the start of the game might make it too easy and not so fun. Let’s learn how to easily add pickup spawnable items to the game world.

Add the pickupSpawnerScript to your custom item template. Now this script comes with a large number of properties to customize it to your liking:

  1. For the moment let’s decrease the min/max respawnTime property to 5 seconds, which affects how often the item will respawn after it is picked by the Player.
  2. Also be sure to enable the showOnInit property which will make the item available from the start of the game.

Return to the World Tree and add a few instances of this template to the World. Now if you go and preview the game you will find yourself able to start picking up those items with the Interact key (Keyboard E or gamepad face button left).

You can also enable the useOnCollision property on the pickupSpawnerScript to have the Player pick up any item he collides with automatically.

Configuring the inventory

You will notice that you can pick up several items and the number of items on that slot will increase. That is happening thanks to the canStack property on the inventoryItemSpecScript.

Go ahead and disable canStack and run the game again. You will see that as soon as you pick up this item, it will be added to the third item slot of the inventory. Trying to pick up more items will fail since your inventory is full.

To have more slots available go ahead on the User template and on the inventoryScript increase the maxSize property.

Using inventory items

We now have an inventory full of useful items, so how do we use them? The inventory package has by default input configured to use the following controls:

  1. Extra Action 2/Extra Action 3 to switch the active inventory slot to the previous/next slot. This is an easy way to cycle through your inventory items in game. (Keys F/Q on keyboard and Gamepad Right/Left Shoulder).
  2. Hotbar 1,2,3,4 etc to change the active inventory slot by its index (this works only on a keyboard right now, Keys 1,2,3,4 etc)

Advanced inventory features (Keyboard required)

Extending the default styling

To start extending the default styling provided by the inventory package, you have to edit the HTML/CSS code found in the inventoryViewWidget widget.

Let’s change the styling of the selected item. To do that locate the .item-selected class in the widget and replace it with the following code:

        .item-selected {
            background-color: #ffff1a;
            color: black;
            text-shadow: 0 1px rgba(0,0,0,.3);
            height: 100%;
            width: 100%;
            border-radius: 50px;
            box-shadow: 0px 0px 0px 2px #fff inset;
        }

This will update the color of the background and text and also increase the border radius making the item slot round.

Let’s make all item slots match this style by updating the .item and .item-empty classes accordingly to:

        .item {
            margin-right: 5px;
            display: block;
            background-color: #f3cda5;
            text-align: center;
            width: 100px;
            border-radius: 50px;
            height: 100px;
            color: #808080;
            z-index: 1;
        }

        .item-empty {
            background-color: #c1c1c1;
            height: 100%;
            width: 100%;
            border-radius: 50px;
        }

And also update the .item-hotkey class to center the slot index:

        .item-hotkey {
            width: 100px;
            display: block;
            padding-top: 2px;
        }
Note that you undo these changes with CTRL + Z

Saving/loading items

You can install a package available in the Crayta store, called Auto-save that can help you persist a Player’s inventory items between sessions or when changing worlds.

Installing the package will add two scripts in your project, the autoSaveScript and the inventorySaveScript.

To enable this functionality all you have to do is add those two scripts to your User template. There is only a single property that can be configured called interval. This sets the gameplay time after it elapses the inventory will automatically save in the background.

When the Player starts a new session or moves to a new world, his inventory will be automatically filled with the items he had in place.

Removing inventory items

Many inventory items in games can be used limited times or just once before they are consumed. The inventory package doesn’t provide an automatic way to handle that, though it provides a method that you can easily use from any script to add item removal logic.

Here is the method, found in the inventoryScript:

function Inventory:RemoveCurrent(removeCount)
    return self:RemoveFromItem(self.inventory[self.properties.currentIndex], removeCount)
end

Let’s create a new script called consumeItemScript and populate it with the following code:

local ConsumeItemScript = {}

function ConsumeItemScript:OnButtonReleased(btnName)
    if btnName == "primary" then
        self.inventoryScript = self:GetEntity():GetUser().inventoryScript
        self.inventoryScript:RemoveCurrent(1)
    end
end

return ConsumeItemScript

Now attach this script to your Player template and try pressing the primary action button (Left Mouse Button or Gamepad Right Trigger) while playing the game.

The active item will be used once and then it will be removed from the inventory.

Accessing the inventory items

All items the inventory holds, together with info regarding their quantity and the slot index they occupy, are stored in the self.inventory table in the inventoryScript.

Here is sample code on how to access that table from another User/Player script and print the item names in the console:

function ConsumeItemScript:Init()
    self.inventoryScript = self:GetEntity():GetUser().inventoryScript
    
    for index, inventoryItem in ipairs(self.inventoryScript.inventory) do
        
        if inventoryItem.template ~= nil then
            Printf('Item name {1}', inventoryItem.template:GetName())
        end
    end
end

Categories
Advanced Tutorials

Event Functions in Crayta Scripts

Preface

Scripts in Crayta use an event-driven approach by calling certain functions that can be declared in any script. These functions, which can be called entry points, are activated by Crayta as a response to events that occur in the game, quite often with associated arguments.

All these methods are specified in the Crayta Lua API. Some of them you will already have seen like the Init (called on the server to initialize a script) or the OnInteract (called on the server when a player interacts with an entity on all scripts of the entity.)

In this tutorial you will be introduced to the most common and important event functions.

If you are coming to Crayta from a Unity background, entry points will look familiar.

What might be new is the context of execution, where the script is running:  Server, Client or Local. So, many event functions will have variants depending on where they are executing e.g. Init() vs ClientInit() vs LocalInit().

In the beginning this can be slightly confusing but in the long run it will become a powerful way of programming multiplayer games. 

By sharing the same codebase between client/server and easily communicating between the server/client. In another tutorial the relationship between server and client and how the two communicate will be fully explained.

Initialization

It is a common use case to be able to call initialization code before updates occur in gameplay. The Init function provided by Crayta is used to do that and is called as soon as the script is initialised.

There are three variants of this method depending on where it is called:

  • Init, called on the server, on the first frame that the server is running.
  • ClientInit, called on each connected client when that client initializes.
  • LocalInit, this is similar to ClientInit except it only works for scripts attached to either the User or Player template and is only called on the local client.
function ScriptComponent:Init() ... end
function ScriptComponent:ClientInit() ... end
function ScriptComponent:LocalInit() ... end

Here are some examples to help you understand where to use each:

  1. In a dungeon crawler you would like to position random crates with hidden power ups across the dungeon. Running the code in the Init method will ensure that the server is responsible for spawning crates in random places and have those entities propagate automatically to all connected clients.
  2. You would like to start animating a cosmetic object (not important to gameplay) as soon as a player connects. ClientInit will execute as soon as that client is ready and the animation will start playing for the player to watch. If animating entities important to gameplay e.g. a door opening/closing that blocks a gameplay area, that should be done on the Server.
  3. You would like to render in the UI how much time the player has been playing. As soon as he is connected you can save the current time in the LocalInit and pass it to a Widget to be used in the interface logic to measure time.

Regular Updates

Much like most game engines Crayta provides a way to run code that is executed per frame. That is key to program changes to the position, rotation or behavior of objects before each frame is rendered. The OnTick function is the main place to put that kind of code. 

When OnTick runs it also provides the delta time in seconds which is the time elapsed since the last frame was rendered.

function ScriptComponent:OnTick(number deltaTimeSeconds) ... end
function ScriptComponent:ClientOnTick(number timeDeltaSeconds) ... end
function ScriptComponent:LocalOnTick(number timeDeltaSeconds) ... end

Much like the Init function, OnTick comes with the same three variants (regular/Server, Client and Local).

Here is an example on how to use OnTick to rotate an entity each frame:

local RotateScript = {}

RotateScript.Properties = {
    { name = "speed", type = "number", default = 200}
}

function RotateScript:Init()
    self.yaw = 0
end

function RotateScript:OnTick(dt)

    self.yaw = self.yaw + self.properties.speed * dt

    local rotation = Rotation.New(0, self.yaw, 0)
    self:GetEntity():SetRotation(rotation)
end

return RotateScript

Input

Crayta has in place a default player controller (configurable on the Player template) that handles all player and camera movement in the world (walking, running, jumping etc).

Further input handling is provided by a set of event methods that take care of abstracting the source of input (keyboard/mouse or controller).

Interact

The OnInteract method is executed on entities the player interacts with (player targeting the entity and pressing keyboard E or the left face button on the controller with the default mappings).

When OnInteract runs it provides the Player that interacted with that entity as an argument as well as the hitResult on the entity.

function ScriptComponent:OnInteract(Entity player, HitResult hitResult) ... end

There are also two other methods available, OnInteractPressed and OnInteractReleased, that run on scripts attached to either the User or the Player when the interact key is pressed or released accordingly. These two methods will be executed even when there isn’t any object available to interact with.

All OnInteract methods run on the server only.

Actions

Similar to the OnInteractPressed and OnInteractReleased methods, Crayta provides additional methods to listen to user input (keyboard/mouse or controller). 

All methods run on the server only, and most of them on scripts attached to the User or Player entity.

function ScriptComponent:OnButtonPressed(btnName) ... end
function ScriptComponent:OnButtonReleased(btnName) ... end

World

There are several event functions provided by Crayta to interface with the game world and respond to updates or changes that happen in it.

Here are some of the most commonly used events.

OnUserLogin/OnUserLogout

This is a method called on the server to inform you when a new user joins or leaves a World. When it runs the User that has joined or left the World is passed as argument.

function ScriptComponent:OnUserLogin(User user) ... end
function ScriptComponent:OnUserLogout(User user) ... end

OnTriggerEnter/OnTriggerExit

This is a method called on the server by a trigger component placed in the world when any entity enters or exits the trigger. When it runs the Entity that is responsible for triggering the event is passed as argument.

function ScriptComponent:OnTriggerEnter(Entity other) ... end
function ScriptComponent:OnTriggerExit(Entity other) ... end

OnCollision

This method is called on the server when a player collides with an entity. The method will be called on all scripts of that entity and also on the player’s scripts. 

In the first case the Player that collided with the entity will be passed as an argument, in the second case the Entity that the player has collided with will be passed.

function ScriptComponent:OnCollision(Entity collidingPlayerOrEntity) ... end

OnDamage

This method is called on the server on all scripts of the entity when damage is inflicted on that entity, provided the damageEnabled property is enabled.

The arguments passed when this method runs are the damage amount (number), the damage causer (Entity) and the hit result (HitResult).

function ScriptComponent:OnDamage(number damageAmount, Entity damageCauser, HitResult hitResult) ... end

Categories
Advanced Tutorials

Introduction to Lua Scripting in Crayta

Preface

Scripts are the way to make your Crayta entities come to life by defining custom logic and behavior. All Crayta scripts are written in regular Lua, a lightweight and high-level programming language widely used in the video game industry.

In this tutorial you will learn how to start writing scripts by learning how to add/edit scripts through the editor, exploring the structure used by Crayta, the properties and methods commonly used and also how to start debugging your code.

Scripts can define reusable behaviors that extend the general purpose functionality Crayta implements. Here is an example of a simple script that gives a random rotation to an entity each time you press the interaction key:

local Rotate = {}

function Rotate:OnInteract(player)
    local rotation = Rotation.New(0, math.random(360), 0)
    self:GetEntity():SetRotation(rotation)
end

return Rotate 

When creating a script the user inputs a name. The script created can then be attached to any number of entities using a script component.

Here are a couple of ways a script exists in Crayta:

  • As an asset, a Lua  file that contains the script definition.
  • As a script component, which is the base Crayta class that loads a script asset and attaches it to an entity. This is what you can see and adjust in the editor Property window.
  • As a script instance, an instance of the object defined in the script and created on runtime for every entity that has this script attached as a script component.

If you are coming from a Unity background to Crayta you will feel right at home. 

The way a Unity C# script creates a component to extend the behavior of a GameObject, is similar in Crayta with a Lua script using a script component to add custom behavior to an Entity.

Creating scripts

There are several ways to create new scripts in Crayta. Here are two common ways to do so:

By right-clicking in any entity in the World Tree window and selecting Add -> Script->New Script from the contextual menu.

  • This action will create a new script asset and also attach it to the selected entity using a script component.
  • By navigating to Scripts in the Library and selecting Create New Asset.

A name has to be provided each time you create a new script.

Editing scripts

In the same manner editing any script can be done in two ways:

  1. By selecting the entity in the World Tree window, navigating its components to find the script in question and clicking the edit icon. 
  2. By finding the script in the Scripts Library and double clicking on it or right click -> Edit.

Both actions will open the Code Editor allowing you to easily edit your Lua scripts directly inside Crayta.

Anatomy of a script

Let’s analyze a basic Crayta script to better understand its structure:

local Rotate = {}

Rotate.Properties = {
    { name = "duration", type = "number", default = 2 }
}

function Rotate:Init()
    self.rotation = Rotation.New(0, 0, 0)
end

function Rotate:OnInteract(player)
    self.rotation.yaw = math.random(360)
    self:GetEntity():AlterRotation(self.rotation, self.properties.duration )
end

return Rotate

Let’s break down each section of the script.

Script declaration

local Rotate = {}

Declaring a Crayta script is as simple as defining a Lua table. The table can be stored to a local variable using any name, although a good practice is to keep them the same with the script name

Script properties

Rotate.Properties = {
    { name = "duration", type = "number", default = 2 }
}

This table (properties bag) can hold one or more script properties that are automatically parsed by Crayta and exposed to the editor. Each property is a table containing a number of attributes expected by Crayta.

Each property requires a name and a type attribute and there are optional attributes like min, max, default etc (explained in the Property Types Guide) that can be added to some property types.

Exposing script properties to the editor is powerful. It allows you to easily reuse the same script across different entities, and customize the behavior for each entity directly from the editor, allowing that way even designers/non-coders to adjust the game logic.

Initialize

function Rotate:Init()
    self.rotation = Rotation.New(0, 0, 0)
end

Each Crayta script can have an initializer method that is called automatically on each entity that the script is attached to. This method is called only once for each entity and is useful for preparation logic, defining variables and setting the initial state of the entity.

There are three types of initialize methods in Crayta: Init(), ClientInit(), LocalInit() which are be explained in the Events Functions Guide.

OnInteract

function Rotate:OnInteract()
    self.rotation.yaw = math.random(360)
    self:GetEntity():AlterRotation(self.rotation, self.properties.duration )
end

This is a method Crayta will call automatically when the Interact button is pressed on the entity (e.g. player targeting the entity and pressing keyboard E with the default mappings or the left face button on the controller).

There are plenty of useful methods that Crayta can call like OnTick, OnInteract, OnXActionPressed, OnUserLogin, OnTriggerEnter etc, also explained in the Events Guide.

Return

return Rotate

This is the default way to close a script and should come last after all the script methods.

The way Crayta parses and loads the script is by running it from top to bottom filling the Rotate table that is returned at the end of the script.

Even though Unity and Crayta use different programming languages for script authoring it is easy to spot several similarities.

Unity spawns a C# class to hold all properties/methods of a script, that would be the Lua table declared in the first line of the Crayta script.

In Unity public properties are exposed in the editor with various attributes, that would be the properties bag table in the Crayta script.

The Start() and Update() methods used by Unity to initialize and update the script, would be the Init() and OnTick() methods (in all their variants) in Crayta.

Debugging

Basic syntax errors in the code will generally highlight the relevant line in red to point it out:

Attempting to save a script that contains one or more syntax errors will produce an error in the Console window, the main tool used to debug Crayta scripts.

Errors printed to the console will provide the script name and a line number to locate the source of the problem in your code.

  • Your best friend to help with debugging any kind of error is using the Print and Printf commands.
  • Printf(string format, … varArgs), this function replaces instances of {1} with the first argument passed in, {2} with the second etc.
function Rotate:OnInteract(dt)
    self.rotation.yaw = math.random(360)
    
    Printf('Rotating {1} to {2} degrees.', self:GetEntity():GetName(), self.rotation.yaw);
    
    self:GetEntity():AlterRotation(self.rotation, self.properties.duration )
end

All messages printed in the console are searchable allowing you easily find something specific.