Timers & Delays
Need something to happen later, or on a repeating schedule? Here's how to handle time-based logic in HELIX.
One-Shot Delays
Run code after a specified delay:
- Blueprint
- Lua
- JavaScript
C++ equivalent (for reference)
// Use "Set Timer by Event" node with Time = 3.0 and Looping = false
// Connect a Custom Event to the delegate pin
// Or use the "Delay" node directly in your execution flow
-- Using the Timer API
Timer.SetTimeout(function()
print("This runs after 3 seconds")
end, 3000)
-- Using UE's built-in timer
UE.UKismetSystemLibrary.K2_SetTimerDelegate(
{ self, self.OnDelayComplete }, 3.0, false
)
// Using standard JavaScript timers (available in PuerTS)
setTimeout(() => {
console.log("This runs after 3 seconds");
}, 3000);
// Or use async/await for sequential delays
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function countdown() {
console.log("3...");
await delay(1000);
console.log("2...");
await delay(1000);
console.log("1...");
await delay(1000);
console.log("Go!");
}
Repeating Timers
Run code on a regular interval:
- Blueprint
- Lua
- JavaScript
Use Clear Timer by Handle with the return value from Set Timer by Event to stop a repeating timer.
C++ equivalent (for reference)
// Use "Set Timer by Event" with Looping = true
// Set Time = 1.0 (fires every second)
// To stop: use "Clear Timer by Handle" with the return value
-- Repeating timer (returns a handle for cancellation)
local handle = Timer.SetInterval(function()
print("Tick! Current time: " .. os.clock())
end, 1000)
-- Stop the timer later
Timer.ClearInterval(handle)
// Repeating timer
const handle = setInterval(() => {
console.log("Tick!", Date.now());
}, 1000);
// Stop the timer later
clearInterval(handle);
Coroutines in Lua
Lua coroutines let you write sequential-looking code that pauses and resumes across frames:
function M:ReceiveBeginPlay()
-- Start a coroutine
self.SpawnRoutine = coroutine.create(function()
for i = 1, 5 do
self:SpawnEnemy(i)
print("Spawned enemy " .. i .. ", waiting...")
coroutine.yield() -- pause until resumed
end
print("All enemies spawned!")
end)
-- Resume it every 2 seconds
self.TimerHandle = Timer.SetInterval(function()
if coroutine.status(self.SpawnRoutine) ~= "dead" then
coroutine.resume(self.SpawnRoutine)
else
Timer.ClearInterval(self.TimerHandle)
end
end, 2000)
end
Promises in JavaScript
For complex async flows, use Promises or async/await:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function waitForPlayers(minPlayers) {
return new Promise((resolve) => {
const handle = setInterval(() => {
// Check connected players (example — adjust to your game logic)
const count = getConnectedPlayerCount();
if (count >= minPlayers) {
clearInterval(handle);
resolve(count);
}
}, 500);
});
}
async function startGame() {
console.log("Waiting for players...");
const count = await waitForPlayers(4);
console.log(`${count} players ready. Starting!`);
await delay(3000); // 3 second countdown
Helix.emit("GameStarted");
}
Tips
- Timer values in the Lua Timer API use milliseconds (1000 = 1 second). UE's built-in timer nodes use seconds.
- Always store timer handles and clear them when your actor is destroyed. Orphaned timers cause bugs that are tough to track down.
- Avoid running heavy logic on fast intervals (< 100ms). If you need per-frame updates, use
Tick/ReceiveTickinstead. - The Blueprint Delay node is convenient but pauses that entire execution thread. For multiple independent delays, use separate timer handles.