Categories
Advanced Tutorials

Adding Camera Shake

Camera shake can help sell the effect that the player is firing a powerful weapon or is near an explosion and is causing the landscape to move under their feet.

How do I trigger it?

You have two ways to trigger a camera shake effect; you can either trigger it on a certain player, or you can trigger it at a location in the world.

Play camera shake effect on a user

The PlayCameraShakeEffect has been added to the User API and can be used to trigger camera shake on a specific player. You can pass the camera shake asset you wish to play along with a Scale value to control how powerfully this should be applied.

user:PlayCameraShakeEffect(self.properties.cameraShakeAsset, 1)

Adding the effect on the player

Let’s add this to our player so each time you jump it will trigger the shake effect.

First, select the Player template from the Entities drop-down.

Right-click on the Player and select Create Child > Script Folder > New Script Folder and then rename this JumpShake.

Select this folder then select Entity > Add > Script > New Script and call this jumpShake.

Next add the below code to the new script you created:

local JumpShake = {}

-- Script properties are defined here
JumpShake.Properties = {
	{name = "cameraShake", type = "camerashakeasset", },
	{name = "shakeMultiplier", type = "number", default = 0.5, min = 0, max = 1, tooltip="Multiplier scale value based on jump height."},
}

--This function is called when jump is pressed.
function JumpShake:LocalOnJump()
	--Call the shake effect on the user and pass in the camera shake asset and the strength.
	self:GetEntity():GetUser():PlayCameraShakeEffect(self.properties.cameraShake, self:GetEntity().jumpHeightMultiplier * self.properties.shakeMultiplier)
end

return JumpShake

Finally, set the Camera Shake property to either Recoil or Explosion on the script properties. 

Here’s an example of this in action with a camera shake triggered every time the player jumps, with the Scale parameter tied to the jump height multiplier of the player:

Creative Control : Adjusting the jump height

Inside your Player template you should have access to the Character settings, try adjusting the jump height and then re-running the preview. You should notice the intensity of the shake changes depending on how high the player can jump.

Playing camera shake at a location

The PlayCameraShakeEffectAtLocation has been added to the World API, and can be used to create a rumble at a certain location in the world. The closer the player is to the epicentre, the greater the camera shake’s effect can be felt.

world:PlayCameraShakeEffectAtLocation(self.properties.cameraShakeAsset, location, innerRadius, outerRadius, falloff, orientToDirection )

The function let’s you specify an innerRadius and outerRadius, where the camera shake is felt at full force at distances equal to or less than the inner radius, blending out to seeing no camera shake at all if the character is at a distance of outerRadius or more.

The Falloff parameter controls how this falloff between the inner and outer radius is applied, for example setting this to 1 will apply the falloff linearly, while a value of 2 will result in the camera shake intensity falling off exponentially. orientToDirection will adjust the movement of the camera shake based on the direction from the epicentre.

Note: This won’t do anything noticeable if triggering a camera shake that vibrates the camera in all directions!

Adding the effect on a mesh

Let’s add this to one of our meshes, once you’re in the radius of the mesh the effect will be applied based on the location.

Firstly, select the Grenade (Jumbo) from the meshes and place it in the World.

Place this away from the initial player spawn and somewhere visible in the level.

Select this folder then select Entity > Add > Script > New Script and call this proximityShake

Next add the below code to the new script you created:

local ProximityShake = {}

-- Script properties are defined here
ProximityShake.Properties = {
	{ name = "cameraShake", type = "camerashakeasset", },
	{ name = "cameraShakeIntensity", type = "number", default = 2, },
	{ name = "innerRadius", type = "number", default = 0, },
	{ name = "outerRadius", type = "number", default = 800,},
	{ name = "falloff", type = "number", default = 1, },
	{ name = "orientateToDirection", type = "boolean", default = false,},
}

function ProximityShake:Init()
	self.p = self.properties
	self:Explosion()
end

function ProximityShake:Explosion()
	self:Schedule(
		function()
			while true do
				Wait(1)
				--Get the position from this entity	
				local pos = self:GetEntity():GetPosition()
				--Apply the camera shake at this location
				GetWorld():PlayCameraShakeEffectAtLocation(self.p.cameraShake, self.p.cameraShakeIntensity, pos, self.p.innerRadius, self.p.outerRadius, self.p.falloff, self.p.orientateToDirection)
			end
		end
	)
end

return ProximityShake

Don’t forget to assign the Camera Shake property to Explosion on the script properties.

Here’s an example of this in action with a camera shake triggered by how close you are to the location specified, as you get closer to the grenade the effect is amplified:

Creative Control : Adjusting the radius and falloff

By adjusting the radius values you can set how close or far away that the screen shake will be triggered on the player as they go towards the location. By adjusting the falloff, you can choose how harshly or smoothly the effect will increase when you’re in its radius. It’s worth playing with these to dial in the effect that you want.

Recap

Awesome! You should now have the screen shake effects in your game. Feel free to adjust the settings to suit your situation and make the effect as subtle or intense as you want.

Adding Polish

  • Try adding particle effects and/or sounds when the grenade explodes.
  • Add the shake effect when the player lands after a jump or is launched through the air.
Categories
Advanced Tutorials

Using the combat improvements

From September 2021 (Neon Coast) we have introduced improvements to how gun combat works, this guide will show you how to get started with these changes, and how you can get them working in your gun game.

You will need to either create a new gun game from the template games, or you should look in the packages and install the update if you already have a game with the Gun package imported.

For more information about these new features, you can read more about them on this blog post.

Per-bone collisions

The first change will allow you to get unique data about the player skeleton, you should be able to find out which area of the character you are hitting with a raycast. We’ve added a new 4th parameter, ‘highFidelityCollision‘.

If you set this to true, your Raycast will no longer collide with the bounding capsule around players – it will only collide with the smaller internal collision bounds of the character.  You can tell which part of a player you have hit by querying GetPartName() on your hit result.

For example, you can detect if your ray has hit the player’s head like so:

GetWorld():Raycast(
	rayStart, 
	rayStart + (rayDirection * rayLength), 
	entityToIgnore, 
	true, -- use high fidelity collision
	function(hitEntity, hit)
	if hitEntity then
		print(hitEntity.GetPartName()) -- prints "head" if you hit a character's head
	end
end

Note : High fidelity rays will continue to collide with voxels/meshes the same as they did before.  In such cases, GetPartName will return “bounds” to indicate you have hit the regular bounding collision of these entities.  Also, if not using high fidelity collision and you hit a player, GetPartName() will likely return “bounds“, but if an arm is extended out from this bounding capsule the ray will still hit this and return the name of the part of the arm instead.

Ammo boxes and limited ammo

We have added the option to have limited ammo clips on your weapons and then require the player to pick up ammo boxes. This adds a level of realism and a new mechanic to your gun games as you need to now pick up ammo.

How do I add limited ammo to my game?

All new games will see the new ‘Has Limited Clip’ parameter in gunScript for each of your guns.

Once you have ticked Has Limited Clip, you’ll be presented with a few more options:

  • Shots Per Clips – how many bullets can this gun hold at one time
  • Start with Full Clip – if true, your gun will start with the amount of bullets set in Shots Per Clip.  If false, you’ll start with zero bullets.
  • Ammo Template – This is the type of ammunition this gun requires.

What’s an Ammo Template?

The ammo template is no more than another inventory item which has been set as being a type of currency.  We’ve created a new Ammo stock package to help you which you can download from the Community tab

Once downloaded, you’ll see a new template called Ammo (which has been assigned as the ‘Ammo Template’ in the gunScript parameters above).  Ammo is simply an inventoryItemSpecScript with ‘IsCurrency‘ ticked. 

With the properties set above, we should now have a gun that can hold 30 bullets per clip, and an inventory that can store 120 more unloaded bullets of this type.  Note that if another gun uses the same Ammo template, then both guns will share the same pool of extra bullets in your inventory to reload with.

Here’s how the game UI should look with the above settings applied:

Note : The game starts with the gun containing a full clip of 30 bullets.  This is because ‘Shots Per Clip’ for this gun was set to 30, and ‘Start with Full Clip‘ was ticked.  The number under this shows how many of these bullets are currently in your inventory.  In this case it is zero, despite us setting “Max Currency” to 120, we haven’t actually added any bullets to the gun yet, we’ve only set the maximum number of these bullets your inventory can contain. 

To add bullets to your inventory, you can simply add the Ammo template to the list of items you have by default at the start of the game, like so:

Now when the game starts, your player should have the Gun in their inventory by default (as they already did before). Gun has been set to use Ammo as it’s ammunition type. We’ve also added 60 Ammo to the inventory.  So we should snow start with a gun with a full clip of 30 bullets, and start with 60 extra bullets (out of the maximum possible 120) in our inventory to reload with.

Ammo Pickup

The Ammo package also contains an ammo pickup template, which can be used as a pickup in your game that adds some much needed ammunition to your inventory during the game. This template simply has a pickupSpawnerScript on it from the stock Inventory package. In the example shown, this ammo pickup will spawn every 5 seconds, and add 30 bullets to your inventory each time it is collected. Note that it will not increase the number of bullets in your inventory over the maximum value of 120 we set earlier.

And with that, you have full setup the limited ammo and the ammo pickups within your game.

Adjusting your aim

AdjustAim is a new system available to creators that has been added to the character API.

What is it?

AdjustAim allows your game to control exactly where the player character needs to look, as well as providing control over how this change in look-at orientation is applied smoothly over time.

Why has this been added?

The eagle-eyed among you may recognize this behaviour as sounding quite similar to the existing SetLookOverride functionality in the User API

Using SetLookOverride, you can for example set your game to ignore any rotation input from the player’s mouse/gamepad, and instead force the player to rotate as if the analogue stick on the gamepad was set half way between the centre & full-right position, using the following code:

 user:SetLookOverride(Vector2D.New(0,0), Vector2D.New(0.5,0))

So what does AdjustAim allow us to do that SetLookOverride didn’t?

The subtle difference between the two is that SetLookOverride was a way to adjust the player’s rotation input from their mouse/gamepad which is being fed into the character (this is why it’s found in the User API), whereas AdjustAim alters the character entity’s rotation directly (which is why it’s found in the Character API).  This gives you far more control over precisely where you want the player to be looking towards!

How do I use it?

To trigger this new feature, a new function has been added to the character API – AdjustAim. The AdjustAim API uses a similar set of parameters used for Timelines, where you can specify a list of rotations to be performed one after another.

Example 1 – Passing entities as ‘look-at’ targets

character:AdjustAim(50, 
                    self.properties.oneMesh:GetLocation(), 
                    "easeinout",
					50, 
                    self.properties.twoMesh:GetLocation(), 
                    "easeinout",
                    50, 
                    self.properties.threeMesh:GetLocation(), 
                    "easeinout")

The above code will rotate the character at a speed of 50 degrees/sec until they are looking at the ‘oneMesh‘, and it’ll apply this movement over time with a nice smooth “easeinout“.  Once done, it’ll do the same thing but look at ‘twoMesh‘, and finally ‘threeMesh‘.  Here’s how that might look:

Example 2 – Rotate by angle

character:AdjustAim(180, 
                    Rotation.New(0, 360, 0), 
					45, 
                    Rotation.New(45, 0, 0), 
                    "easeinout",
					45, 
                    Rotation.New(-45, 0, 0), 
                    "easeinout",
                    180, 
                    Rotation.New(0, -360, 0))

The above code will rotate the character 360 degrees to the right at 180 degrees/sec (note the lack of any smoothing, such as “easeinout“, for the first entry – when no smoothing is specified, it’ll default to “linear”).  it’ll show them smoothly look up then down, before linearly rotating back to the left by 360 degrees at 180 degrees/sec:

Note: You can mix and match this list of parameters to include both entities to look at and/or angles to rotate by, so you could look at the oneMesh entity, then spin around 360 degrees.

character:AdjustAim(50, 
                    self.properties.oneMesh:GetLocation(), 
                    "easeinout",
					180, 
                    Roation.New(0, 360, 0), 
                    "linear")

Example 3 – tables as parameters

local aimTable = {}
for index = 1, self.properties.turnSections do
	table.insert(aimTable, 45) -- rotate at 45 degrees/sec
	table.insert(aimTable,	Rotation.New(0, 360 / self.properties.turnSections, 0),
	table.insert(aimTable, "easeinout")  -- smoothly turn during each segment)
end
character:AdjustAim(aimTable)

Just like timelines, tables of values can be passed too to support procedural generation.  In this example, I rotate the player 360 degrees at 45 degrees per second, but this rotation is split up into a number of individual turns, specified by the turnSections property on this script.  In this example video, turnSections is set to 4, so the player will rotate 360 degrees using four 90 degree turns:

Adjust Aim Handles

The AdjustAim API returns a handle to the active aim adjustment.

self.adjustAimHandle =  character:AdjustAim(50, 
                        self.properties.oneMesh:GetLocation(), 
                        "easeinout",
						180, 
                        Rotation.New(0, 360, 0), 
                        "linear")

You can query whether this adjusted aim is still in progress using IsActive().

if self.adjustAimHandle.IsActive() then

And you can use Cancel() to instantly stop the active adjust aim after it has begun, or by passing the handle into the equivalent function on the character API.

-- these 2 lines will do the same thing
self.adjustAimHandle.Cancel()
character:CancelAdjustAim(self.adjustAimHandle)

You can also query if any aim adjustment is currently active on the player.

character:IsAdjustAimActive()

Where can I see examples of this being used in Crayta?

We have added Recoil to the stock gun packages, this is a great example of the kind of thing that is now easy to do with the new AdjustAim API!

While we were at it, we also decided to improve the existing Aim Assist that was present in the stock Gun package to use AimAdjust.  We can’t wait to see what other uses you come up with for it!

Categories
Advanced Tutorials

The relationship between Player and Default camera

Preface

In most 3D game engines the concept of a camera is quite common. Cameras are the devices that capture and display the world to the player.

Crayta provides this mechanism: a default camera with a number of customization options to easily achieve your desired gameplay camera and the option to spawn any number of secondary cameras to be further used.

In this tutorial we will explore the relationship between the default camera and the player, and also how to create and use additional in-game cameras.

If you are coming from a Unity background you will be familiar with camera game objects.

What’s different in Crayta is that the default camera is directly attached to the Player entity. The default camera comes with ready to use controls for a first and third person camera setting.

In Unity you could do something similar either with implementing your own solution in code or using a starter kit.

Default Camera

In Crayta the default camera is basically the Character entity automatically spawned in the game world from the Player template, so it won’t be exposed separately in the Entities World Tree.

Crayta provides a default first or third person camera with a number of settings to customize for each type.

Camera Primitive

Additional cameras can be spawned in the game world to be used later during game play for cutscenes, level previews, as a background when showing game results etc.

Camera entities spawned in the world remain inactive until activated from a script.

How to select a camera?

When starting any Crayta game your spawned User entity in the game world automatically sets the selected camera to the default. And that would be your Character entity since in Crayta:

The default camera is the character entity.

At any point in a Crayta script you can change the selected camera to any other camera entity using the following code:

userEntity:SetCamera(cameraEntity)

You can also specify an optional second parameter as the transitionTime in seconds. That will automatically animate the view from the current camera to the newly selected one:

userEntity:SetCamera(cameraEntity, 3.0)

At any moment you can fallback to the default camera by calling the same method with no parameters:

userEntity:SetCamera()

And lastly you can query the active camera using a similar method:

userEntity:GetCamera()

If the default camera is selected, the Character entity will be returned. Otherwise a camera entity will be referenced.

It’s a simple and powerful mechanism that allows you to present dynamic views of your game world to the player.

Categories
Advanced Tutorials

Creating Voxel Meshes using Volume Select

Preface

Crayta provides a powerful voxel building system allowing you to create big and varied game worlds. You can quickly create your game level surfaces to start testing your gameplay and later go back and add cosmetic details easily.

In this tutorial we will explore how Volume Select, a Voxel tool available, works to edit and extract parts of a Voxel Mesh. Which you can later reuse in other parts of your Crayta world.

What is Volume Select?

When creating a new Crayta project you will find a single Voxel Mesh, in the form of a ground floor, already placed in the world for you. If you start drawing voxels on this surface, the voxels created will be automatically added to that original Voxel Mesh.

This stone door that we created is nice but what if our game requires moving it to a different location? Volume Select allows us to select a number of voxels from a Voxel Mesh to execute a number of methods on them:

Clicking Volume Select on the Voxel Tools toolbar allows us to create a selection volume in the 3D view to include our voxels. You can add a selection volume and further edit it using the gizmo to precisely select the required voxels.

Then you can execute one of the following methods:

  • Extract, creates a new Voxel Mesh from the selection containing the selected voxels. This will also have the voxels selected removed from the current Voxel Mesh.
  • Move, allows you to move the current voxels in the world, inside the current Voxel Mesh.
  • Duplicate, allows you to create a copy of the current voxels, inside the current Voxel Mesh.
  • Clear, will delete the selected voxels from the Voxel Mesh.

Let’s take a look at how each method works.

Moving Voxels

Using Volume Select to select a number of voxels and pressing Move will render the move gizmo at the center of the selected volume.

Using the gizmo you can move the voxels around and when finished pressing Apply on the toolbar will complete the action.

Duplicating Voxels

Duplicating Voxels is very similar to moving Voxels, with the only difference when finished moving the voxels around a new copy will be created.

Clearing Voxels

Clearing Voxels is even simpler: selecting Voxels with the Volume Select tool and pressing Clear will automatically remove those voxels from the Voxel Mesh.

Extracting Voxels

Extracting Voxels is a powerful tool that allows us to easily reuse voxels to enhance our game level.

When selecting Voxels using Volume Select and pressing Extract the output will be a new Voxel Mesh and Entity added to your project.

The new Voxel Mesh created will automatically be named based on the name of the original Voxel Mesh. A good practise is to remember to name the new extract to something sensible, helps a lot when starting having a lot of Voxel Meshes in the same level.

You can now easily reuse this new Voxel Mesh to make copies of the stone door in your level.

At any point you can merge back this new Voxels to a parent Voxel Mesh entity.

To do that move the Voxel Mesh entities, to be merged, as children to the parent Voxel Mesh entity. Right click on each entity and select Merge To Parent.

Each merged entity will be removed from the Entities Tree and the Voxels merged to the parent Voxel Mesh.

This is a powerful system to easily create levels and interesting worlds for your game.

Categories
Advanced Tutorials

Script variable scope

Preface

When building a game in Crayta you will often need to store information about the state of your game; game score, current level, number of enemies, etc are all examples of information you would store using variables. You may use these variables while coding your Lua scripts to build working blocks of code, much like you would in any other programming language.

While it is very easy to declare variables that hold supported Lua data, their scope will be different depending on how and where we declare said variable.

In this tutorial we explore the different scopes available for Lua variables in Crayta.

Variable scope is a subject of study and practice in any programming language. Something that the developer has to understand and use accordingly.

In Unity with C#, the scope of a variable or property is set using special keywords, public and private. These define if other script classes have access to variables or not.

In C# you can’t define global variables, since everything has to belong in classes.

Local Variables

In Lua there is a keyword used to declare variables with a local scope, named local. Local variables have their scope limited to the block where they are declared. A block is the body of a control structure, the body of a function, a for loop etc.

Here is an example to illustrate this:

function CollectableScript:Init()
  local name = self:GetEntity():GetName()

  if name == 'cottageChair' then
     -- works, variable name is available since it was declared in a parent scope
     local nameLowerCase = name:lower()

     -- works, variable nameLowerCase is available since it was declared in this scope
     Print(nameLowerCase)
  end

  -- doesn’t work, variable nameLowerCase not available in this scope
  Print(nameLowerCase)
end

Global Variables

Global variables in Lua do not need declarations. You simply assign a value to a global variable to create it. This variable is available in any scope of this script.

For example let’s test this code:

local CollectableScript = {}

function CollectableScript:Init()
    
  local name = self:GetEntity():GetName()

  if name == 'cottageChair' then
      myGlobalVar = 'aGlobalValue'
  end
End

function CollectableScript:OnInteract()
    Print(myGlobalVar)
end

If we assign this script to a number of entities, with one of them named cottageChair, then each time we interact with any of these entities we will get aGlobalValue printed in the console.

A global variable has the same value across all instances of the script.

Global variables have a greater performance impact compared to local variables, so as a preference local variables should be preferred. In addition global variables produce poor code architecture, breaking abstractions and object oriented paradigms, making maintaining a codebase difficult.

Self Variables

Crayta uses an object oriented approach in writing reusable code. Each Lua script contains the definition of a class-style type that can be reused (instantiated) by any number of entities that have a script component of this type attached.

The methods declared in this script will run on a different context each time. That of the entity the script is attached to.

For this reason it is quite useful to be able to declare variables that can be used by any method of the same script, but not between script instances.

Crayta makes available in any script a special variable named self. We can use the dot notation to declare variables that are available on a specific script instance this way.

Let’s rewrite the previous example to accomplish a different task using self.

local CollectableScript = {}

function CollectableScript:Init()
    
  local name = self:GetEntity():GetName()
  self.nameLowerCase = name:lower()

end

function CollectableScript:OnInteract()
    
    Print(self.nameLowerCase)
end

Now if we attach our script to a number of entities, whenever we interact with one of them we will get their name printed in lowercase to the console.

Using self.nameLowerCase we make sure that we declare a new variable unique to each script instance executing.

Accessing from other scripts

Local and global variables are accessible only within the same script. On the other hand variables defined using self can be easily accessed by other scripts. Variables in Lua can hold not only data values but also references to functions.

This is powerful and allows you to easily extend your code to read data and methods found in other scripts.

Here is an example of how we can access the nameLowerCase variable from another script:

local AnotherScript= {}

AnotherScript.Properties = {
      { name = "collectable", type = "entity"}
}

function AnotherScript:MyMethod()
    
    Print( collectable.collectableScript.nameLowerCase )
end

We can access the script instance of a script with its name, on any entity that has it attached. Any variables or methods declared on self are available now using the same notation.

Categories
Advanced Tutorials

Accessing table methods in Lua

Preface

Object Oriented Programming (OOP), is one the most used programming techniques used in the modern era of programming. There are a lot of programming languages that support OOP, and Lua provides a number of ways to practice OOP in your code. Lua isn’t a true OOP language itself, but the way it’s implemented in Crayta exposes several OOP concepts for use (tables/scripts, properties etc.).

Crayta uses the OOP paradigm in its scripts, allowing you to easily take advantage of it in your code.

In this tutorial we will explain how to access table values (properties and methods of an object, in OOP lingo) and common pitfalls to look for.

In Unity the C# language fully supports OOP. The Unity script classes, properties and method will look familiar in comparison to how a Crayta script is structured and used.

Objects in Lua

You implement OOP in Lua with the help of tables and first class functions. By placing functions and related data into a table, an object is formed.

Tables in Lua have the features of an OOP object like state and identity that is independent of its values. Two objects (tables) with the same value are different objects, whereas an object can have different values at different times, but it is always the same object. Like objects and tables have a life cycle that is independent of who created them or where they were created.

Each Crayta script is implemented as a table that can reference a number of properties and methods.

Accessing table methods

Understanding Colon and Dot Operators

Many programmers learning LUA tables for the first time struggle with the difference between a function call that uses a colon (:) to call table method and a call that uses a dot/period (.)

For example:

local length = string.len( "Hello World" )
enemyEntity:SetRotation(rotation)

Both methods will work and execute correctly, so when should you be using each one?

Deciding whether to use the colon or dot syntax, to access a method, can often be determined by whether you need a reference to the object within the function. This is irrelevant for standalone functions, but it’s quite important for functions which are defined as methods of objects.

The colon (:) is used for implementing methods that automatically pass self as the first parameter.

The following call:

enemyEntity:SetRotation(rotation)

Has the same result with calling this method:

enemyEntity.SetRotation(enemyEntity, rotation)

So basically the colon (:) is used as a short form of calling a method and having self appended as a parameter automatically by the compiler.

Deciding whether to use the colon or dot syntax can often be determined by whether you need a reference to the object within the function.

Example error

Here is an example of a Crayta error, when using dot instead of colon out of place. Normally to access the entity the current script is attached to, you can do it like this:

self:GetEntity()

If you use a dot instead of colon, you will get an error like the following:

self.GetEntity()

This happens because of the GetEntity() Crayta method is expecting a reference to the script instance. If you do the following, the method will work correctly:

self.GetEntity(self)

Of course using the colon instead of dot here will result in cleaner and more readable code.

Here is a full Crayta script demonstrating how colon and dot is used in place:

local CollectableScript = {}

function CollectableScript:Init()
    self.collected = false;
end

function CollectableScript:OnCollision(collidingPlayerOrEntity)
    
    if self.collected == true then return end

    self.collected = true
    local name = self:GetEntity():GetName()
    local nameLength = string.len(name)
    
    -- inform the Player/UI of the item collected
    if collidingPlayerOrEntity:IsA(Character) then
        collidingPlayerOrEntity:SendToLocal('Collected', name, nameLength)    
    end 
end

return CollectableScript

Accessing table properties

We should mention here, to avoid confusion, that table properties, like the ones a Crayta script exposes for editing, always use the dot (.) operator.

Health.Properties = {}

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

function Health:Init(){
   Print(self.properties.maxHealth);
}
Categories
Advanced Tutorials

Packages, migrating level components from one game to another

Preface

Unless you are developing a very small and basic game, you will quite often reach a point of complexity in your game’s setup that requires a shift in the way you build it. The ability to split your game in smaller pieces or components, that can be developed and tested separately can help a lot with reducing that complexity. Crayta provides a powerful feature that allows you to do this, called packages.

In this tutorial we will explore how we can use packages to migrate components from our game, like voxels and templates, to another game.

In Unity there is a similar tool that implements this concept of packaging parts of your game to be used in another scene or game altogether, called Packages as well.

You are able to create Packages that contain models, textures, prefabs etc, export them and import them in another project.

Unity provides an Asset Store where users can publicly share packages.

Packages

In Crayta, at any point, you are able to create new packages that contain any number of valid assets. Assets that can be included in the package are:

  • Voxel Meshes
  • Scripts
  • Widgets
  • Templates
  • Other packages (can be referenced as dependencies)

Core Crayta assets like Meshes or Sounds can’t be added in a package directly, although they can be part of a Template or World asset which can be added.

All new packages created are made available to all of your Crayta projects via the Library. When you are creating a new package you can make it public which in turn will make the package available not only to your projects but to all user created projects.

Packages Library

In Advanced Mode under the Library tab you can see a list of all the packages available to your project. Those include packages that have been installed from the Crayta library and those created in this project.

From here you can explore the contents of each package, review it, add it to your favourites and also update a package to its latest, if available, version.

Creating a Package

Clicking Create New Package will open the relevant window to start creating a new package.

Add a name and description for your package.

There are several fields available here to provide a name, description, optional tags etc. for the package with the most important field being the Assets drag and drop zone. There you can drag and drop any valid asset from the Library window to be included in the new package.

I’ve dragged in my bomb barrel template ready to package!

You can add as many assets as you like this way in the package, and they will all be listed in the Assets list.

You can also drag and drop other packages to the Package dependencies field to be included here and Crayta will automatically add them as a dependency. This means when you are importing this package to another project, Crayta will make sure to download and install any other package required as a dependency.

Hitting Create will finish the creation process and make this package available in Crayta’s Library.

Importing a Package

The Crayta Community tab will list your newly created package allowing you to download and install it to any other project.

By doing so the contents of the package will be made available in the project’s Library. A special icon indicates that this asset is part of an installed package.

You can now use this asset much like any other asset included in your project.

That way packages become a powerful tool to share components between games.

You can use this feature to split the development of your game in components that can be easily developed and tested separately and when ready incorporated back to the original game.

You can also have templates used in one game migrated and reused in another game. 

Crayta users can make their packages public and share them with other users building games.

Updating a Package

At any point you can edit any of your packages to push changes to existing assets or add new ones.

To edit a package you start by locating it in the Packages library window and clicking edit.

This will bring up a window similar to the package creation window, in which you may use a similar method to edit the package.

When finished, clicking Update will update the published the package with any changes you have made. Any projects using this package will show an active Download button which users may then click on to download the newest version of this package.

By clicking this button the latest version of the package will get downloaded and the included assets will be updated.

Categories
Advanced Tutorials

Voxel meshes and templates, when to use each

Preface

Crayta provides several tools to help you build large and interesting worlds for your games.

One of the most powerful features provided is the ability to use voxels in various sizes and materials for terrain, structures, objects, etc. At the same time Crayta allows you to organize parts of your scene as reusable components called templates. Templates can be easily duplicated multiple times, and any changes made to the original will automatically propagate to all instances.

In this tutorial we will explore how to use voxel meshes and templates and when to use each.

In other game engines like UE4 or Unity the world creation usually involves using polygon assets called meshes or models that get placed in the scene to assemble a world. Quite often a terrain or landscape surface is used in addition, painted using a number of brushes to help you create realistic worlds. 

There isn’t any notion of voxel building in UE4 or Unity so usually a large number of assets have to be prepared in advance in a separate 3d package for all parts of the scene to be put together in the editor. The only thing that might resemble Crayta voxel building is the BSP editor in UE4 or ProBuilder Unity extension that can do part of the scene polygon modelling inside the editor.

Crayta templates on the other hand are very much like UE4 Actor Blueprints or Unity prefabs.

Voxel Mesh

Voxel building in Crayta involves adding and editing voxel meshes. Voxel meshes are groups of voxels that contain from a handful (e.g. a simple rock) to millions of voxels (e.g. a complex structure).

When creating a new project a voxel mesh is automatically added, containing a simple terrain surface to start building on.

The terrain voxel always appears by default when you make a new game.

The voxel mesh is saved to a voxel mesh asset, that can be found in the Library under the Voxel Meshes category. You can create multiple voxel meshes from a single voxel mesh asset simply by dragging and dropping it in your world:

Here I have added three new instances of the Box Voxel mesh.

Editing any of these terrain surfaces using the voxel tools will result in changes rendering in all terrain surfaces. This initially can be confusing, the reason is simply because all the terrain surfaces reference the same voxel mesh asset. So any change to that voxel mesh asset is being saved to that asset and in turn updates all voxel meshes referencing that asset.

By editing one box, the red material appears on the others as well.

You can change that behaviour for any voxel mesh by clicking the Create a unique copy of this asset button in the voxel mesh entity properties. That will create a new voxel mesh asset from this voxel mesh and assign it to it. From there and on any change on this voxel mesh will update the new voxel mesh asset created.

By making this a unique mesh, you can see that the change only applies to the one box.

From the Library -> Voxel Meshes window you can also create new voxel mesh assets to be used to spawn voxel meshes in the world.

Template

Templates in Crayta is a powerful system that allows you to create reusable groups of entities that can be easily spawned anywhere in your world. At the same time retaining a link to the original template allowing you to make changes at any point to your template and have the changes propagate automatically to all the template instances.

A template can contain any kind of entities and components. You can use it for several things:

  • A specific group of meshes, sounds, effects to make an object or set of objects that can be reused.
  • Entities that contain scripts and are reusable in some way or spawned e.g. doors and projectiles.
  • Player characters, as most of the time you would want them to behave in the same way across all instances.
Templates can be used to group several entities that make up a reusable object in your game.

You can easily create a new template by using an entity that already exists in your world. Select it and then click Template -> Create New Template…

This will create a new Template with this entity at the root of the template. From there you can easily add children entities, components etc.

You may spawn instances of a template by using the Library → Templates window and drag/drop your selected templates into the world. Spawned entities will show the full hierarchy of the template in the World Tree editor window and will contain a toolbar with the following template actions:

  • Edit template, will open this template for editing.
  • Copy to template, any changes made to this instance with this button will be saved back to the template and propagate to all template instances.
  • Break template link, this will severe the connection to the template and make this entity a normal World entity. You can do further changes to the entity without having the template override anything.

Voxel Meshes and Templates

Voxel meshes should be used when doing world building and you require parts of your world to be reused. Parts that don’t require any special functionality like scripting or lighting etc.

When those parts require additional components to be added and spawned on each instance a template should be used. Voxel meshes can be added to template entities much like any other component (script, sound, effect etc).

The only thing requiring special attention in templates is the use of the Break template link button. This will break the link to the template, allowing the entity to be edited further without affecting the template.

This holds true for all changes except editing voxel meshes attached to the parent entity or to its children.

This happens because voxel meshes are created as instances, and will share any changes with the base Voxel Mesh you originally created. So, editing the voxel mesh on any entity that has a broken template link, will have potentially undesirable effects of editing the voxel mesh asset referenced by the voxel meshes in the template instances.

To stop this behavior, the solution is simple using the Create a unique copy of this asset button on those voxel meshes to create new voxel mesh assets that aren’t referenced by this template.

Categories
Advanced Tutorials

The relationship between Player and User

Preface

One of the most common concepts found in video games is that of a user controlled avatar. The player takes control of an avatar, which can be anything depending on the theme of the game (a human, a vehicle, an exotic spaceship) to navigate and interact with the game world.

In this tutorial we will explore how this concept is applied in Crayta and how to set it up in your game.

When getting on board Crayta one of the first things you are asked to do is to setup your Avatar using a range of controls to customize the body, hair, clothing etc.

Crayta games revolve around players that control human avatars. Crayta provides a camera system, player collisions, interaction etc, making it very easy to create first or third person games.

In UE4 there is a very similar concept of having a Character Class which in Crayta would be the Player template and a Player Controller Class which is similar to the User template in Crayta.

User

A User is a type of entity which references the account of a person connected to the game. When you connect to a Crayta game a User entity will be spawned from the available User template and assigned to you.

All Crayta games are required to have a User template available. Only a single one is needed, since there isn’t any way to choose or set the active User template. Crayta will automatically detect and use it.

A default User template is provided in all new projects created which can be used to attach scripts and widgets.

Player

The User entity doesn’t have any physical representation in the game World, so you can’t see or interact with other connected Users.

When a new user is connected to the game and a User entity is spawned. Crayta will also attempt to spawn a Character entity from a Player template, unless a OnUserLogin method  has been added to a script. If that is the case then Crayta will not spawn a Player template and it is up to your game logic to handle this.

All player templates are required to have a root Character entity (other entity types may become available in the future.)

Spawning a character entity will cause the game to render the user’s avatar and add a default camera.

A default Player template is added in all new projects created.

In contrast with the User template, a Player template exposes a list of properties allowing you to customize a number of settings like the jump height or running speed of the Player.

User/Player relationship

Each user connected to the game has at any instant a User entity spawned and referenced to their account and also a Player (Character) which is their avatar which is active in the game World.

Both entities are based from the User and Player templates available in the game.

Firstly, let’s take a look on how data is shared and used between the User and the Player. If you create a new empty game and search for the Crayta packages called Health and Auto-Respawn and install both of them.

If you look at the Templates section of the Library you will see Player Health and User Spawn. Feel free to add the Player Health to your Player template and the Spawn template to your User under the Entities panel.

Example of installing the userSpawn template on the User entity.

Now, let’s look at the scripts and open both the spawnUserScript and the healthScript. Firstly, in the spawnUserScript let’s look at the function OnPlayerDied() in it you should see where we pass in the player and then in the schedule we destroy the player avatar before we respawn the character based on the User data.

function SpawnUserScript:OnPlayerDied(player, fromEntity)
	
	self.properties.onUserDied:Send(self:GetEntity(), fromEntity)
	
	if not self.respawnTask then
		self.respawnTask = self:Schedule(
			function()
				Wait(self.properties.respawnDelayTime)
				if self.properties.respawnActive then
					player:Destroy()
					self:SpawnInternal(self.isLobby)
				end
			end
		)
	end
end

Now, let’s inspect the healthScript and look at the SendDeathNotificationToAll function. We don’t need to go too in-depth here, but we can can see variable called deadUser this stores the data from the User and then in the variable below called deadUsername we use this access to get the Username of the player who died.

function HealthScript:SendDeathNotificationToAll(fromEntity, selfMsg, otherMsg, youKilledMsg)
	
	-- who died
	local deadUser = self:GetEntity():IsA(Character) and self:GetEntity():GetUser() or nil
	local deadName = deadUser and deadUser:GetUsername() or self:GetEntity():FindScriptProperty("name") or "?"

There is no need to run the preview; but feel free to play around with the health and respawn packages to help you understand this relationship.

Note: You may have to make sure you have checked the option to Spawn on Login to the spawnUserScript to make it useable.

A game can have a single User template but can have multiple Player templates.

The User entity provides you with a method to change the active Player template that is used by each player:

This is quite powerful since it allows you to easily change the behavior of your game depending on the game state. For example you can have one Player template for the lobby area with an Orbit camera and players not being able to jump, a first person Player template for the main game action time and another one for players spectating and not actively playing.

You can easily add more Player (Character) templates to your game using the Primitives window. All you need to do is select it and right-click and then select Create Template …

Here is an example of how to change the Player template from a script attached to a User entity:

local MyUserScript = {}

MyUserScript.Properties = {
    { name = "playerTemplate", type = "template"}
}

function MyUserScript:ChangePlayerTemplate()
  self:GetEntity():SpawnPlayer(self.properties.playerTemplate)
end

And here is an example of getting a reference to the active Player (Character) from a script attached to a User entity:

function MyUserScript:GetActivePlayer()
   return self:GetEntity():GetPlayer()
end

And a more advanced case: getting a reference to the Player entity of all connected users from any script running on the server:

GetWorld():ForEachUser(
   function(userEntity)
       local username = userEntity:GetUsername()
       local player = userEntity:GetPlayer()
       -- do something with the player 
       -- e.g. access script methods or player properties
   end
)
Categories
Advanced Tutorials

Introduction to Widgets, building a game UI

Preface

Almost every game requires, in one form or the other, some sort of user interface (UI). For that reason most of the game engines or game creation systems go to great lengths in providing a robust UI solution to the developer.

In Crayta you can use a well established tech stack to build the UI for your game: HTML/CSS and JavaScript. If you are coming from a web dev background you will feel right at home. If not, in this tutorial we will attempt to provide simple steps to get you started.

If you are coming from Unity or Unreal to Crayta, initially you might find it difficult to adapt to the Crayta UI.

At the moment, there isn’t a visual editor to allow you to place elements visually, adjust the position, the properties etc. All Crayta UI elements are HTML elements that are styled using CSS and logic is added using JavaScript.

You can find a plethora of resources online to help you get started with HTML, CSS and JavaScript.

If you’ve used the new UIElements in Unity you will feel much more comfortable.

Widgets

All interface related code is being stored in a special asset named Widget which can be attached to entities. You add widgets to your entities in the same way that you add scripts.

There are three types of widgets that can render an interface:

  1. Screen: the UI will be rendered as a HUD on screen.
  2. World: the UI will be rendered as a texture in the 3D world on a 3D plane.
  3. World – Camera Facing: the UI will be rendered as a texture in the 3D world, as a camera facing 3D plane.

The type of a widget can be set using the type property.

The Widget references an asset in its html property that contains the HTML code used for rendering it, plus inline CSS and inline JavaScript.

You can add a new UI widget by right-click and selecting Add>UI Widget

What is HTML?

HTML or Hyper Text Mark-up Language is used to render web pages that has been used since the very first websites went public on the internet. Over time it has evolved to be able to handle 2D and 3D styles and also include complex logic using JavaScript.

<!DOCTYPE html>
<html>
<body>

<h1>My First Heading</h1>

<p>My first paragraph.</p>

</body>
</html>

What is CSS?

The tags (e.g. <p></p>) used to define the HTML mark-up structure have a basic styling of their own. However, the web standards (W3C) decided that to extend or change page styling, a special language would be created called CSS (Cascading Style Sheets).

This language provides you with a number of rules that extend the styling of any HTML element. Here is an example of inline (meaning in the same file) CSS:

<html>
  <head>
    <title>Playing with Inline Styles</title>
      <style>
         p{
            margin-left: 20px;
         }
      </style>
  </head>
  <body>
    <p style="color:blue;font-size:46px;">
      I'm a big, blue, <strong>strong</strong> paragraph
    </p>
  </body>
</html>

CSS can be added directly to any element using the style attribute or using a special HTML element <style>.

What is JavaScript?

JavaScript, language is used for adding logic and interactivity to web pages and even to write complex web applications.

Crayta includes the V8 JavaScript engine which is the same JavaScript compiler used by Chrome and other applications. This means that you are able to run and execute any kind of JavaScript code in Crayta.

Crayta exposes a clean API that can be used to communicate data back and forth to your Lua scripts in your JavaScript code.

    <body>
       <p style="color:blue;font-size:46px;">
           I'm a big, blue, <strong>strong</strong> paragraph
       </p>

        <script>
            engine.createJSModel('data',
            {
                bgColor: "rgb(20, 200, 100)",
                
                title: "Sign Title",
                titleSize: 20,
                titleColor: "rgb(255, 255, 255)",
                
                message: "Sign Message",
                messageSize: 10,
                messageColor: "rgb(20, 20, 20)",
            });
        </script>
    </body>

To write JavaScript in HTML you can use the special HTML element <script>, in the <head> and/or <body> elements.

It’s common practice to write your JavaScript code at the end of your <body> element, since this allows you to access any other HTML elements in the script.

Building a simple UI

A lot of the Crayta provided packages come with one or more widgets to draw a UI. You can use them to start learning how to build a UI of your own or as a starting point for your game UI.

But to better understand how widgets work in Crayta, we will explore how we can get started building a UI from scratch.

A simple game

Let’s take a moment and create a simple game and use it as the base for our interface.

  • You can create a new blank project or work on your existing one. 
  • Place a number of random Mesh entities around the world.
  • Create a new script called collectableScript and attach it to all the entities you’ve placed.
  • Add the following code to the script:
local CollectableScript = {}

function CollectableScript:OnCollision()
    self:GetEntity():Destroy()
end

return CollectableScript

If you go and play the game now, as soon as the Player collides with an object it will be removed from the world, like a collectible.

Now that’s not so much fun since the Player can’t see which items they’ve picked up. Our interface to the rescue!

Adding a widget

  • Select the UI Widgets category from your Library, and create a new asset named myInterfaceWidget

You can have all of your interface in a single widget, or split it up into smaller widgets that can act as parts or components of your interface. The latter approach is particularly beneficial If you have a complex interface or plan on reusing parts of it in other games in the long term.

For our tutorial project we will be using a single widget. Crayta will automatically fill the widget html file with boilerplate code that includes placeholders for CSS, HTML and basic JavaScript methods required to communicate with your Lua scripts.

  • Attach this widget to your Player template.

Running the game will result in this:

Let’s prepare the widget to host our interface code.

  • Remove all existing CSS code from your <style> element.
        <style type="text/css">
            /* YOUR CSS CODE HERE */
        </style>
  • Remove all html code from your <body> element, and JavaScript from the script element.
    <body>
        <!-- YOUR HTML CODE HERE -->

        <script>
            /* YOUR JAVASCRIPT CODE HERE */
        </script>
    </body>

Let’s add a simple list that will serve as a container for all the collectables the Player gathers.

  • Add the following HTML code where you’ve removed the boilerplate one (at the top of the body section and under the <!– YOUR HTML CODE HERE –> comment).
        <div class="my-items-container">
            <h1>My items</h1>
            <div class="my-items-list">
                <div class="my-item">
                    <span>1</span>
                    <p>Item Collected</p>
                </div>
            </div>
        </div>
  • Add some CSS to make it look pretty inside the style element, under the /* YOUR CSS CODE HERE */ comment:
    .my-items-container {
        width: 30vh;
        margin: 10px;
        padding: 10px;
        border-radius: 7px;
        background-color: rgba(255,255,255,0.5);
    }
    
    .my-items-container h1{
        text-align: center;
        text-shadow: 0px 0px 5px white;
    }

    .my-items-list {
        color: #ccc;
        list-style-type: none;
    }
    
    .my-item{
        position: relative;
        font: bold italic 70px/1.5 sans-serif;
        margin-bottom: 20px;
    }
    
    .my-item p {
        font: 20px/1.5 sans-serif;
        padding-left: 60px;
        color: #555;
    }
    
    .my-item span {
        position: absolute;
        color: darkcyan;
    }

Running the game at this point:

Although it is a nice list, right now it is just a graphic. Let’s add some logic to the interface.

Adding logic

Our goal for this list is to list any collectable the player picks up. For that we need a Lua script on the Player template that keeps tracks of the items collected.

  • Add a script to the Player template called myCollectablesScript.
  • And add the following code to it:
local MyCollectablesScript = {}

function MyCollectablesScript:Collected(collectableName)
    
    self:GetEntity().myInterfaceWidget.js:CallFunction("collected", collectableName)
end

return MyCollectablesScript

This code is the starting point of establishing communication between our Lua scripts and the Widget.

MyCollectablesScript:Collected is a custom method that we will be calling each time the Player has picked up a collectable. Let’s implement that now.

  • Add the following code to the collectableScript.
local CollectableScript = {}

function CollectableScript:Init()
    self.collected = false
end

function CollectableScript:OnCollision(collidingPlayerOrEntity)
    
    if self.collected == true then return end
    self.collected = true
    
    -- destroy the collectable
    self:GetEntity():Destroy()
    
    -- inform the Player/UI of the item collected
    if collidingPlayerOrEntity:IsA(Character) then
        collidingPlayerOrEntity:SendToLocal('Collected', self:GetEntity():GetName())    
    end
    
end

return CollectableScript

Using SendToLocal on the Player entity is an easy way to communicate from the server to the local Player the item that this Player has collected.

  • Add the following JavaScript code to the widget, replacing the current code.
      <script>
        /* YOUR JAVASCRIPT CODE HERE */

        engine.createJSModel('itemsModel',
        {
            items: []
        });

        engine.on("collected", function(collectableName) {
            itemsModel.items.push({
                index: itemsModel.items.length + 1,
                name: collectableName
            });
            engine.updateWholeModel(itemsModel);
            engine.synchronizeModels();
        });

    </script>

This Javascript code does two things:

  1. Creates a new model that contains an array property used to hold all of the collected items.
  2. Registers an event handler, that is a function that can be called from Lua code and can also accept arguments.

As soon as the Player picks up a collectable the widget will get notified, and the items array will get populated.

Dynamically updating the UI

Let’s explore how the interface can automatically get updated as the items are collected.

Crayta uses the Coherent Gameface UI library which allows you to add data bindings to any HTML element. This is powerful for the elements which can get updated/added/removed automatically reflecting your data.

  • Update the HTML code in the widget adding data attributes.
        <div class="my-items-container">
            <h1>My items</h1>
            <div class="my-items-list">
                <div data-bind-for="item:{{itemsModel.items}}"              class="my-item">
                    <span data-bind-value="{{item}}.index"></span>
                    <p data-bind-value="{{item}}.name"></p>
                </div>
            </div>
        </div>

There are a number of data-bind attributes available to use in your HTML code. Some common ones are:

  • data-bind-for: can be attached to an array and automatically clone this HTML element, together with its children, for each array item.
  • data-bind-if: can do conditionals to check and show/hide an HTML element based on that condition.
  • data-bind-value: automatically update the innerHTML value of any element with the value attached.

Now this HTML code will automatically reflect any changes to the itemsModel object.

Every item collected by the Player will now appear on the Interface.

Code

Here is the complete widget script:

<html>
    <head>
        <!-- Required includes -->
        <script type="text/javascript" src="coui://uiresources/js/crayta.js"></script>

        <style type="text/css">
            /* YOUR CSS CODE HERE */
            .my-items-container {
              width: 30vh;
              margin: 10px;
              padding: 10px;
              border-radius: 7px;
              background-color: rgba(255,255,255,0.5);
            }
            
            .my-items-container h1{
                text-align: center;
                text-shadow: 0px 0px 5px white;
            }

            .my-items-list {
              color: #ccc;
              list-style-type: none;
            }
            
            .my-item{
              position: relative;
              font: bold italic 70px/1.5 sans-serif;
              margin-bottom: 20px;
            }
            
            .my-item p {
              font: 20px/1.5 sans-serif;
              padding-left: 60px;
              color: #555;
            }
            
            .my-item span {
              position: absolute;
              color: darkcyan;
            }
        </style>
    </head>

    <body>
        <!-- YOUR HTML CODE HERE -->
        <div class="my-items-container">
            <h1>My items</h1>
            <div class="my-items-list">
                <div data-bind-for="item:{{itemsModel.items}}" class="my-item">
                    <span data-bind-value="{{item}}.index"></span>
                    <p data-bind-value="{{item}}.name"></p>
                </div>
            </div>
        </div>

        <script>
            /* YOUR JAVASCRIPT CODE HERE */

            engine.createJSModel('itemsModel',
            {
                items: []
            });

            engine.on("collected", function(collectableName) {
                itemsModel.items.push({
                    index: itemsModel.items.length + 1,
                    name: collectableName
                });
                engine.updateWholeModel(itemsModel);
                engine.synchronizeModels();
            });

        </script>
    </body>
</html>

Useful links

Coherent Gameface reference docs: