Object Spawner

This lesson will show you how you can create a state machine. A state machine is a way to code behaviours that have different states:

  • A door has two states: OPENED and CLOSED.
  • An enemy can have multiple states, for example: WANDER, FOLLOW_PLAYER, ATTACK, DEAD.
  • A UI with smooth animation: HIDDEN, FADE_IN, VISIBLE, FADE_OUT.

To spawn our objects, we will make a state machine with 3 states:

  • SPAWN_OBJECT: This state is just spawning the object in the scene and switching to the WAITING_DESTROY state
  • WAITING_DESTROY: This state is constantly looking if the coin is still there. If the coin is collected, then this state is moving to the COOLDOWN state
  • COOLDOWN: Wait a few seconds before going back to the SPAWN_OBJECT state.

Using a state machine is a great way to make various elements in your game using a code that is similar, making it easier to debug and for other people to understand.

Spawning an object from a script

Before making the state machine, we need a function that can spawn an object in the scene.

  1. Create a group “Object Spawner” and add a new script called “ObjectSpawner” as a child.
  2. Select the “ObjectSpawner” script and drag & drop the Coin template from your Project Content to the Custom Properties.
Creating a new Custom Property with an Asset Reference
After dropping the Coin

Now that we have the Asset Reference of the object, meaning the ID of the template, we can spawn it in our code using SpawnAsset:

local COIN_TEMPLATE = script:GetCustomProperty("Coin")

World.SpawnAsset(COIN_TEMPLATE, { parent = script.parent })Code language: Lua (lua)

When starting the Preview, a Coin is spawned inside the Object Spawner group.

The coin is spawned in the game and set as a child of Object Spawner, the group which is the parent of the script ObjectSpawner

Now that we know how to spawn the coin, we can create the State Machine. The full code is available below but let’s look at the different parts of this script:

  • First, we get the Custom Property Coin to retrieve the reference to the asset.
  • We declare a constant value COOLDOWN_DURATION that will be the number of seconds that the script will have to wait before spawning a new coin.
  • We declare useful variables for the script, spawnObject and nextSpawnAt.
  • We declare the various states of the State Machine and set the currentState.
  • We declare a function SpawnObject that uses the line we made just above.
  • We declare a function IsObjectInScene to detect when the object has been destroyed.
  • We declare a function SetState that will contain the logic when we are switching from one state to another one.
  • We declare a function CheckStates that will contain the logic to detect when we should move to another state.
  • We declare a function Tick that is called each frame by the engine.
  • We call the function SetState to spawn the first coin.
local COIN_TEMPLATE = script:GetCustomProperty("Coin")

-- This constant is the number of seconds before the next call to SpawnObject
local COOLDOWN_DURATION = 2

-- spawnedObject: a reference to the spawned coin, it will be set when the object is spawned
local spawnedObject
-- nextSpawnAt: the value that stores the time of the next spawn
local nextSpawnAt = 0

-- The different states of the State Machine
local STATE_SPAWNING = 1
local STATE_WAITING_DESTROY = 2
local STATE_COOLDOWN = 3
-- currentState is the current state of the State Machine
local currentState

-- The function to spawn an object
function SpawnObject()
    spawnedObj = World.SpawnAsset(COIN_TEMPLATE, { parent = script.parent })
end

-- A function that returns true if the object exists.
-- if obj is nil, meaning not defined, it will return false
-- if obj is defined but obj:IsValid() returns false, it means that the coin has been destroyed
-- IsObjectInScene returns false if the object is destroyed
function IsObjectInScene(obj)
    return obj and obj:IsValid()
end

-- Updating states
function SetState(newState)
    if newState == STATE_SPAWNING then
        SpawnObject()
    elseif newState == STATE_WAITING_DESTROY then
        --do nothing
    elseif newState == STATE_COOLDOWN then
        -- Saving the nextSpawnTime which is the number of seconds + the cooldown
        nextSpawnAt = time() + COOLDOWN_DURATION
    end
    currentState = newState
end

function CheckStates()
    -- If the state is "spawning", then we should move to waiting destroy
    if currentState == STATE_SPAWNING then
        SetState(STATE_WAITING_DESTROY)
		
    -- If we are waiting for the object to be collected and
    -- if IsObjectInScene returns false ("not false" means true), the condition is valid
    -- We are starting the cooldown
    elseif currentState == STATE_WAITING_DESTROY and not IsObjectInScene(spawnedObj) then
        SetState(STATE_COOLDOWN)
		
    -- If the time() is greater than nextSpawnAt, we should spawn the coin
    elseif currentState == STATE_COOLDOWN and time() >= nextSpawnAt then
        SetState(STATE_SPAWNING)
    end
end

-- This function is called at each frame
function Tick()
    CheckStates()
end

SetState(STATE_SPAWNING)Code language: Lua (lua)
The coin is spawned, collected by the player, then we wait 2 seconds and it is spawned again.

Object Spawner as a template

Now that the object spawner is working, we can Create a New Template From This and put several instances of them on the map.

5 spawners on the map
Post a comment

Leave a Comment

Scroll to Top