Advanced Tutorials

Basic Coding : Make A Highly Dangerous Soccer Game


Crayta allows you to create any game you can imagine. Sometimes thinking of a game completely from scratch can be a bit daunting, but what about taking a game that already exists and turning it on its head? Enter, highly dangerous soccer – a soccer game which involves guns, traps and the odd exploding barrel.


This tutorial will guide you through taking a Team Deathmatch level template and turning it into a soccer game using some Lua scripting in Crayta’s Advanced Editor

This tutorial is aimed at Crayta players who are comfortable with most of the tools in the Advanced Editor, but assumes no prior coding experience. If this is the first time you have opened the Advanced Editor then you may want to go through the Advanced Workflow Tutorial and the Learn To Code With Crayta tutorials to familiarise yourself with the interface. 

In this tutorial you will explore:

  • Changing the Team Deathmatch scoring and game mechanic using Lua Scripting
  • How to “kick” a football
  • Adding custom danger and explosives to your football pitch

The Beautiful Game

This isn’t going to be a typical football game. This is going to be a Crayta style soccer game. Firstly, let’s simplify the rules to give us a game where:

  1. Two teams of players kick a ball around a pitch
  2. The object of the game is to put the ball in the opposing team’s goal

Solving these problems is fairly straightforward.

Players can already kick a ball around the world (using the inbuilt physics engine) so you don’t need to code this yourself.

The Team Deathmatch game mode already has team management and scoring functionality to manage the team assignment and some of the scoring so you don’t need to code this yourself either!

This leaves the goal scoring mechanic and any level based aspects to code. Time to dive in.

Creating The Game

Start by creating a new Team Deathmatch game. Add a Ball (Football Jumbo) Prop to the game.

Use the Entity Editor to change its name to “football” and check the physicsEnabled checkbox.

Close the Entity Editor. Build a goal on your map using Voxel tools. As a rough guide your goal should be about 22 by 10 units. This goal is going to be tweaked and updated when you come to test and balance your game, so don’t spend too long here. 

To make it a bit easier to reuse this, add a Locator into the scene and then add the voxel goal as a child entity. Rename the locator to goalTemplate and then highlight it in the Entities panel.

You can then select Template > Create New Template … and then this will save a template that can be used in multiple places.

It’s good practice to keep the locator central to your voxel meshes or other entities.

Ball Respawn

When the ball is kicked into the goal it needs to allow a short time for player celebrations and then respawn at the center point. We are going to add a trigger to the template we created.

In the Entities panel, select the drop-down and change it from World to your Goal template. Now, select the Primitives and add a Trigger into the scene. Move the Trigger to the goal and increase its size until it fills the whole goal. Name the Trigger goalTrigger.

You will use the Trigger to call a function in the gameScript when the ball enters the goal. 

Add a Locator to the center of the game e.g. at location (0,0,360.) You will use this Locator as the spawn point for the ball. Name the Locator footballSpawnPoint.

Click on the gameController in the Entity Explorer and open the Code Editor for the gameScript.

This Script contains a lot of the logic for the game, including the scoring when a Player kills another Player (as this is the game logic for a Team Deathmatch game) and assigning Players to teams. You can look through the code to see how the game works. In this guide you will be repurposing this logic to work the way you want it to. 

The gameScript needs a reference to the footballSpawnPoint so that it can respawn the ball at this location. Add a new Property to the Property Bag with the name “footballSpawn” of type “entity”.

{ name = "footballSpawn", type = "entity" }

Use the Entity Explorer / Editor on the right hand side to drag the footballSpawnPoint to the footballSpawn Property on to the gameController / gameScript Property Bag.

To handle the logic when the ball enters the goal we will now write a function which is called by the Trigger from the OnTriggerEnter Event

We are going to add another property below the footballSpawn to keep track of the football itself. Repeat the above steps, but this time we want to call the property footballMesh.

Make sure both the spawn point and mesh have been referenced, or the script won’t work.
{ name = "footballMesh", type = "entity" }

You need to make sure that you drag the footBall-Jumbo mesh onto the “FootballMesh” property that is exposed in the editor panel, exactly the same way you did this for the spawn.

Add a function after the Property Bag called GoalScored.

function GameScript:GoalScored(other, theTrigger)


The function contains two arguments:

  1. other – this is a reference to the Entity which has entered the Trigger.
  2. theTrigger – this is a reference to the Trigger itself. We will use the same function for both goals, so we will need to use this reference to figure out which team has scored.

To respawn the ball the following steps should occur:

Fill out the function so it contains the following:

function GameScript:GoalScored(other, theTrigger)
	-- Check if the thing which entered the goal Trigger (other) was called football
	if other then

		-- Set ball position to ballSpawn position

		-- Halt ball velocity and spin

Use the Entity Explorer to call the GameScript:GoalScored function from the goalTeam1 Trigger OnTriggerEnter Event. 

Select the goalTeam1 Trigger in the Entity Explorer. Scroll to the onTriggerEnter dropdown box. Select the following:

  • Entity: gameController
  • Script: gameScript
  • Event: GoalScored

Test the game and check that when you score a goal with the ball, the ball is respawned at the footballSpawnPoint Locator position.

Adding Time For Celebrations

It’s not scoring a goal unless there is a little bit of time set aside for gloating. You can add a delay before the ball is respawned to the spawnBallLocator by using the inbuilt scheduler.

Tech Tip: Wait Off The Main Thread

The Wait() function in Crayta will stop the next line of code from running until after a certain amount of time, which can be very useful. However, this could be dangerous because it could cause the whole game to freeze whilst it waits for Wait() to complete. To avoid this, the Crayta developers have made sure this can’t happen – if you try to use Wait() on its own it just won’t do anything.

-- some code here
Wait(5.0) -- this Wait() will be ignored
-- some more code here

To use Wait() you will need to use self:Schedule(). This function moves the code within the brackets away from the main thread, so the game won’t lock up. This means you can now use the Wait() function. For example:

	Wait(5.0) -- wait for 5 seconds before moving to the next line
	-- Here is some code that is executed after the wait function

Change your GoalScored function to reflect the following:

function GameScript:GoalScored(other, theTrigger)
	-- Check if the thing which entered the goal Trigger (other) was called football
	if other == then
		-- Use the scheduler
				-- wait for 3 seconds

		-- Set ball position to ballSpawn position

		-- Halt ball velocity and spin

	end -- end the code called within the scheduler

	end -- end ball name check

end -- end GoalScored function

Test your changes to make sure the code still works, and that you have time to celebrate (feel free to tweak the length of the Wait()).

Adding The Score

The Team Deathmatch game template has some scoring logic already implemented. To score a point in Team Deathmatch you just need to kill another player. However, for the football game you want to score a point when the ball enters the goal, and not when a Player is killed. To do this you combine the goal scoring functionality you’ve already written with the scoring mechanic that is prebuilt.

If you scroll to the bottom of the gameScript you will see two functions:

  • AddTeamScore(team, num) – this function adds an amount to the team score. In the brackets; 
    • “team” refers to the team number (1 or 2) to add score to, and 
    • “num” refers to the number of points to add to their score (defaults to 1)
  • OnUserDied(userEntity, fromEntity, selfMsg, otherMsg, youKilledMsg) – this function is called when one Player kills another, and calls AddTeamScore. You will remove the call to AddTeamScore.

Within the OnUserDied() function, add a comment (–) to the start of the following lines:



So that the line appears as a comment. This is a technique called “commenting out” and is useful for developers to turn on or off a line of code without losing the code completely by deleting it. Your function should now read:

function GameScript:OnUserDied(userEntity, fromEntity, selfMsg, otherMsg, youKilledMsg)
	-- send game event to anyone who wanted, fromEntity, selfMsg, otherMsg, youKilledMsg)

	-- find user for player who killed us (if there is one)
	local fromUser = (fromEntity and fromEntity:IsA(Character)) and fromEntity:GetUser() or nil
	-- add score
	If fromUser then
-- fromUser:SendToScripts("AddScore",
If and > 0 then
	-- self:AddTeamScore(fromUser:FindScriptProperty("team"),

You have removed the logic which adds scores to the team scoreboard and the individual player score, which means you can now write your own.

To add the scoring mechanic to the GoalScored function, you will borrow one of the lines of code you just commented out. 

Copy the following line of code (Ctrl+C) from the OnUserDied function:


Return to your GoalScored function within gameScript and paste this before the Wait() function. Change the variable fromUser to theTrigger. 


Your GoalScored function should now read:

function GameScript:GoalScored(other, theTrigger)
	-- Check if the thing which entered the goal Trigger (other) was called football
	if other == then
		-- Use the scheduler

				-- Apply the team score

				-- wait for 3 seconds

		-- Set ball position to ballSpawn position

		-- Halt ball velocity and spin

	end -- end the code called within the scheduler

	end -- end ball name check

end -- end GoalScored function

The “theTrigger:FindScriptProperty(“team”)” part of the code is looking for a Property called “team” from any Script attached to the goalTeam1 Trigger. At the moment this Entity does not have any such Property

To add this, add a new Script to the goalTeam1 Trigger and name the Script goalTeamScript. The Script will be very simple and only include this Property. The whole code should read:

local GoalTeamScript = {}

GoalTeamScript.Properties = {
	{ name = "team", type = "number", default = 0 }

return GoalTeamScript

Set the Property to 1 in the Entity Editor.

Make sure you have saved the gameScript and the GoalTeamScript and test your game. After the game starts (there is a lobby countdown first) you should find that when you score your team gets a point. And the crowd goes wild!

Kicking The Ball

Dribbling in the game is possible with a bit of careful control, and this makes for quite a nice requirement of skill. Passing and shooting on the other hand are not currently possible. Let’s remedy this. 

To “kick” the ball you will need to check some conditions, and then apply some force to the ball in the direction the Player is facing. The way you will kick the ball is similar to how Russ builds a Blast Hammer in one  of the Crayta Livestreams:

First, open the Player in the Entity Editor using the dropdown (currently set to World).

Add a Trigger to the Player as a child object. Make its size 200 x 200 x 200 and move it just in front of the existing Trigger on the X axis (red arrow). Name the Trigger kickTrigger.

You will use this kickTrigger to determine if the football is close to the Player.

Create a Script Folder in the Player called KickLogic. Add a Script called KickLogicScript and open it in the Code Editor.

The main difference between Russ’ Blast Hammer Script is instead of launching a player it will apply an impulse force to the football. Start by adding some of the Properties that you will need:

KickLogicScript.Properties = {
	{ name = "strength", type = "number", default = 250 },
	{ name = "kickTrigger", type = "entity" },
	{ name = "launchVector", type = "vector" },
	{ name = "footballRef", type = "entity" },

Saving the Script should make these Properties appear in the Entity Editor for the KickLogic Script Folder.

Do remember to drag-and-drop the reference to the kickTrigger onto the Kick Trigger property that you created, and then expand the Football Ref properties drop-down and select the footBall-Jumbo this should be located under the Word tree.

You will use the OnButtonPressed function, which will be automatically called when the User presses a button, to call a kick function which performs all the logic necessary to kick the ball.

Add the following code to the Script:

-- Called when the player presses a button
function KickLogicScript:OnButtonPressed(buttonName)
    -- Check this is the secondary button
    if buttonName == "secondary" then
        -- Call the Kick function
-- Called from the OnButtonPressed function
function KickLogicScript:Kick()
-- Get a reference to the football
local ball = GetWorld():Find(
-- Check the ball exists
if ball then
    -- Check the ball is within the players kickTrigger
    if then
        -- Store the balls position in local variable
        local ballPosition = ball:GetPosition()
        -- Work out the vector from the ball to the player
        local relativeVector = self:GetEntity():GetLookAtPos() - ballPosition
        -- "Normalize" this vector, so that it is simply directional and doesn’t
            -- refer to the distance (we don’t need the distance)
            local normalizedRelative = relativeVector:Normalize()
            -- Combine the direction any height trajectory we set in the launchVector
            local combinedVector = normalizedRelative +
            -- Multiply by the strength of the kick
            local kickVector = combinedVector *
            -- Apply this vector to the ball as an impulse

Now set your Properties for this Script to the following:

  • strength: 60000
  • kickTrigger: kickTrigger (drag and drop the kickTrigger from the Player to set this)
  • launchVector: 0, 0, 1.2

Before we test this, we also need to remove the current functionality for the “secondary” button, which currently toggles iron sight mode on the gun you are holding.

Open the Gun Template using the Entity Explorer dropdown.

Use the Entity Editor to remove the ironSightItemScript from the Gun Template.

This will stop the “secondary” button from using the iron sight.

Now test your game and try kicking the ball. You can tweak the values in the Properties of the Script to suit how you want the game to “feel”.

Respawn Players When You Score

Scoring returns the ball to its start location, but what about the Players? To return the Players to their initial spawn point you can leverage the existing game mechanics. 

Add the following line of code within the gameScript:GoalScored function after you reset the physics on the football:

-- reset physics on the football
other: SetAngularVelocity(Rotation.Zero)

-- return players to original spawn points

Test your game and check that when the ball is respawned after scoring, so are the Players.

Building A Stadium

Now that you’ve coded the basic game mechanic it’s time to make this game a bit more interesting. Use the Voxel tools to build a stadium for your football game. Make sure to build a wall around the stadium to keep the ball in play and keep the tension mounting.

Think about theming the stadium. This game could be happening at any time, in any place. The fact that you are carrying a machine gun (which can still kill the opposing team) creates a somewhat…murderous…environment, so have fun with the setting.

To build another goal for the other team, extract your original goal across the pitch to make sure they are exactly opposite and exactly the same size. You can then remove a strip of blocks and extract it back to its original setting.

Copy the Trigger goalTeam1 Trigger. When you paste the duplicate you should find that it automatically changes the name to goalTeam2. Move it into the other goal, and remember to use the Entity Editor to change the Trigger’s GoalTeamScript to team 2.

…And that’s it! Remember to test your game, and try out both teams by inviting some friends along for a (deadly) kickabout.


Congratulations! In this tutorial you made a fun soccer game, made somewhat more sinister by the inclusion of automatic rifles.

You’ve developed skills in:

  • Respawning a ball and players to their original location
  • Modifying a game template to change the score mechanic of a team based game
  • Using triggers to score points

Adding Polish

Whilst this is the conclusion of the tutorial, there’s no reason to stop here. In fact this could be just the beginning. Flex your creative muscles by using what you’ve learned here, and in the other coding tutorials to:

  • Add a Script to the ball to store the last player to touch the ball and then Add Player Score to them when they score a goal
  • Add some spinning sawblades (like in Basic Tutorial: Events) or some laser meshes (such as in Basic Tutorial : Combat) to create even more danger in your game
  • Add player score when they hurt or kill another player to encourage more dangerous play
  • Add some exploding barrels (like in Coding Tutorial : Exploding Barrels) that explode when hit by the ball. You can use what you learned in the “Kicking The Ball” chapter to fire the ball across the field

If you’re still looking for more tutorials then you can get access to a categorised list of both written and video tutorials here.