How to Use Update SchedulingOn this pageHow to Use Update Scheduling Dora SSR provides a flexible Update scheduling mechanism that allows developers to easily control game logic updates. This tutorial will guide you through how to use Update scheduling, including coroutine tasks for asynchronous processing and update functions that are called every frame. 1. Understanding Game Update Scheduling Game update scheduling is a mechanism in game engines used to manage and execute game logic updates. It allows developers to execute code on each frame or under specific conditions, creating a dynamic gaming experience. The essence of a game engine is an uninterrupted loop program, with each cycle referred to as a frame, during which game logic updates occur. A typical game logic update process is outlined below. Under an engine executing at 60 frames per second, this process runs 60 times per second: 2. Differences Between Coroutine Tasks and Update Functions Coroutine Tasks: Asynchronous processing: Can pause and resume between multiple frames. Use cases: Suitable for handling operations that need to wait, such as loading resources or timers. Implementation: Achieved via thread, threadLoop, once, and loop functions. Update Functions: Synchronous processing: Called every frame. Use cases: Ideal for handling logic that needs to be updated every frame, such as movement or collision detection. Implementation: Achieved via node:schedule() and node:onUpdate() functions. 3. Using Coroutine Tasks for Asynchronous Processing Coroutine tasks allow you to pause during execution, wait for conditions to be met or for a certain time, and then continue execution. This lets you perform time-consuming operations without blocking the main thread, which is very useful for handling asynchronous events. 3.1 Global Coroutine Tasks vs. Node-Associated Coroutine Tasks In Dora SSR, coroutine tasks can be executed in two ways: Global Coroutine Tasks: Coroutine tasks created using thread(function() ... end) are managed by the engine's global scheduling module. These tasks will keep running unless manually removed and are cleaned up when the engine exits. Node-Associated Coroutine Tasks: Coroutine tasks created using node:schedule(once(function() ... end)) are associated with the node’s lifecycle. When the node is cleared, the scheduled functions will also stop and be cleaned up. Example: Executing a Global Coroutine Task LuaTealTypeScriptYueScriptlocal thread <const> = require("thread")local sleep <const> = require("sleep")local Content <const> = require("Content")thread(function() print("Start loading resources asynchronously") local resourceData = Content:loadAsync("path/to/resource") sleep(2) -- Simulate additional time-consuming operation print("Resource loading completed") -- Continue logic after resource loading is finishedend)local thread <const> = require("thread")local sleep <const> = require("sleep")local Content <const> = require("Content")thread(function() print("Start loading resources asynchronously") local resourceData = Content:loadAsync("path/to/resource") sleep(2) -- Simulate additional time-consuming operation print("Resource loading completed") -- Continue logic after resource loading is finishedend)import { thread, sleep, Content } from "Dora";thread(() => { print("Start loading resources asynchronously"); const resourceData = Content.loadAsync("path/to/resource"); sleep(2); // Simulate additional time-consuming operation print("Resource loading completed"); // Continue logic after resource loading is finished});_ENV = Dorathread -> print "Start loading resources asynchronously" resourceData = Content\loadAsync "path/to/resource" sleep 2 -- Simulate additional time-consuming operation print "Resource loading completed" -- Continue logic after resource loading is finished In the example above, thread(function() ... end) creates a coroutine task, loadAsync asynchronously loads resources, and sleep(2) simulates a time-consuming operation. The coroutine ensures long-running tasks don't block the main thread, and the task's lifecycle is managed globally by the engine. TipIn Dora SSR, functions with the async suffix are asynchronous functions, such as Content.loadAsync and Cache.loadAsync, which need to be executed asynchronously in coroutine tasks. Example: Node-Associated Coroutine Task LuaTealTypeScriptYueScriptlocal Node <const> = require("Node")local sleep <const> = require("sleep")local node = Node()node:once(function() print("Start node-associated asynchronous operation") sleep(2) -- Simulate time-consuming operation print("Node-associated asynchronous operation completed")end)local Node <const> = require("Node")local sleep <const> = require("sleep")local node = Node()node:once(function() print("Start node-associated asynchronous operation") sleep(2) -- Simulate time-consuming operation print("Node-associated asynchronous operation completed")end)import { Node, sleep } from "Dora";const node = Node();node.once(() => { print("Start node-associated asynchronous operation"); sleep(2); // Simulate time-consuming operation print("Node-associated asynchronous operation completed");});_ENV = Doranode = Node!node\once -> print "Start node-associated asynchronous operation" sleep 2 -- Simulate time-consuming operation print "Node-associated asynchronous operation completed" Here, node:once(function() ... end) creates a coroutine task associated with the node. When the node is cleared, the task is automatically stopped and cleaned up. 3.2 Differences in Using the Two Types of Coroutine Tasks Lifecycle Management: Global Coroutine Tasks: Their lifecycle is independent of any node, managed by the engine's global scheduler, and must be manually removed or cleared when the engine exits. Node-Associated Coroutine Tasks: Their lifecycle is tied to the associated node, automatically stopping and cleaning up when the node is destroyed. Use Cases: Global Coroutine Tasks: Suitable for tasks that need to run throughout the game's lifecycle, such as global event listeners. Node-Associated Coroutine Tasks: Suitable for tasks associated with specific scenes or objects, such as character animation control. 3.3 Using Coroutine Control Functions In Dora SSR, coroutine tasks execute asynchronously, and you can use specific control flow functions to manage task execution order and pausing within coroutines. These functions include wait, cycle, and sleep. Their main purpose is to pause or loop the coroutine until certain conditions are met. wait Function The wait function pauses the coroutine task until a condition is met. The condition is a function that returns true. Example: Continuing Execution When a Condition is Met LuaTealTypeScriptYueScriptlocal thread <const> = require("thread")local wait <const> = require("wait")thread(function() print("Waiting for condition to be met...") wait(function() -- Continue execution when the player's score is greater than 100 return player.score > 100 end) print("Condition met, continuing execution")end)local thread <const> = require("thread")local wait <const> = require("wait")thread(function() print("Waiting for condition to be met...") wait(function(): boolean -- Continue execution when the player's score is greater than 100 return player.score > 100 end) print("Condition met, continuing execution")end)import { thread, wait } from "Dora";thread(() => { print("Waiting for condition to be met..."); wait(() => { // Continue execution when the player's score is greater than 100 return player.score > 100; }); print("Condition met, continuing execution");});_ENV = Dorathread -> print "Waiting for condition to be met..." wait -> -- Continue execution when the player's score is greater than 100 player.score > 100 print "Condition met, continuing execution" cycle Function The cycle function repeatedly calls a function every frame over a specified time period, suitable for scenarios where you need to perform repeated logic over a certain time span. Example: Updating an Object's Position Every Frame Over 3 Seconds LuaTealTypeScriptYueScriptlocal cycle <const> = require("cycle“)local Node <const> = require("Node")local node = Node()node:once(function() local startX = node.x print("Start moving") cycle(3, function(time) -- Update the object's position every frame over 3 seconds node.x = startX + 500 * time -- time's value will go from 0 to 1 end) print("Movement ended")end))local cycle <const> = require("cycle“)local Node <const> = require("Node")local node = Node()node:once(function() local startX = node.x print("Start moving") cycle(3, function(time: number) -- Update the object's position every frame over 3 seconds node.x = startX + 500 * time -- time's value will go from 0 to 1 end) print("Movement ended")end)import { cycle, Node } from "Dora";const node = Node();node.once(() => { const startX = node.x; print("Start moving"); cycle(3, time => { // Update the object's position every frame over 3 seconds node.x += startX + 500 * time; // time's value will go from 0 to 1 }); print("Movement ended");});_ENV = Doranode = Node!node\once -> startX = node.x print "Start moving" cycle 3, (time) -> -- Update the object's position every frame over 3 seconds node.x += startX + 500 * time -- time's value will go from 0 to 1 print "Movement ended" sleep Function Usage The sleep function is used to pause execution in a coroutine task for a specified amount of time. It can serve as a simple timer, allowing the coroutine to resume after a certain delay. Example: Executing a Task After a 2-Second Delay LuaTealTypeScriptYueScriptlocal sleep <const> = require("sleep")local thread <const> = require("thread")thread(function() print("Task started...") sleep(2) -- Pause coroutine for 2 seconds print("Task resumed after 2 seconds")end)local sleep <const> = require("sleep")local thread <const> = require("thread")thread(function() print("Task started...") sleep(2) -- Pause coroutine for 2 seconds print("Task resumed after 2 seconds")end)import { sleep, thread } from "Dora";thread(() => { print("Task started..."); sleep(2); // Pause coroutine for 2 seconds print("Task resumed after 2 seconds");});_ENV = Dorathread -> print "Task started..." sleep 2 -- Pause coroutine for 2 seconds print "Task resumed after 2 seconds" In this example, sleep(2) pauses the coroutine for 2 seconds, after which the remaining logic is executed. You can use sleep to easily create timer functionality. 4. Using Per-Frame Update Functions In some cases, you need to execute certain logic every frame, such as detecting user input or updating an object's position. Example: Updating an Object's Position Every Frame LuaTealTypeScriptYueScriptlocal Node <const> = require("Node")local node = Node()node:schedule(function(deltaTime) -- When the right arrow key is pressed if Keyboard:isKeyPressed("Right") then -- Update the object's position node.x = node.x + 10 * deltaTime -- Returning true stops the update endend)local Node <const> = require("Node")local node = Node()node:schedule(function(deltaTime: number): boolean -- When the right arrow key is pressed if Keyboard:isKeyPressed("Right") then -- Update the object's position node.x = node.x + 10 * deltaTime -- Returning true stops the update endend)import { Node, KeyName } from "Dora";const node = Node();node.schedule(deltaTime => { // When the right arrow key is pressed if (Keyboard.isKeyPressed(KeyName.Right)) { // Update the object's position node.x += 10 * deltaTime; // Returning true stops the update } return false;});_ENV = Doranode = Node!node\schedule (deltaTime) -> -- When the right arrow key is pressed if Keyboard\isKeyPressed "Right" -- Update the object's position node.x += 10 * deltaTime -- Returning true stops the update node:schedule(function(deltaTime) ... end) schedules a function to be called every frame, where deltaTime is the time interval between the previous and current frame. 4.1 Differences Between node:schedule() and node:onUpdate() Similarities: The scheduled function is called every frame, and returning true will stop the execution. You can also schedule a coroutine task, and use return true or coroutine.yield(true) to stop execution. Differences: node:schedule(): Used for scheduling the main update function or coroutine tasks. Repeated calls will overwrite the previously scheduled function. node:onUpdate(): Used for scheduling multiple functions or coroutine tasks to run every frame. You can call it multiple times, allowing several functions to be scheduled simultaneously. Example: LuaTealTypeScriptYueScript-- Using node:schedule() to schedule the main update functionnode:schedule(function(deltaTime) -- Main update logic if condition then return true -- Return internal state to stop scheduling -- Or use node:unschedule() to actively cancel scheduling endend)-- Using node:onUpdate() to schedule multiple update functionsnode:onUpdate(function(deltaTime) -- Update logic Aend)node:onUpdate(function(deltaTime) -- Update logic Bend)-- Using node:schedule() to schedule the main update functionnode:schedule(function(deltaTime: number): boolean -- Main update logic if condition then return true -- Return internal state to stop scheduling -- Or use node:unschedule() to actively cancel scheduling endend)-- Using node:onUpdate() to schedule multiple update functionsnode:onUpdate(function(deltaTime: number): boolean -- Update logic Aend)node:onUpdate(function(deltaTime: number): boolean -- Update logic Bend)// Using node.schedule() to schedule the main update functionnode.schedule(deltaTime => { // Main update logic if (condition) { return true; // Return internal state to stop scheduling // Or use node.unschedule() to actively cancel scheduling } return false;});// Using node.onUpdate() to schedule multiple update functionsnode.onUpdate(deltaTime => { // Update logic A return false;});node.onUpdate(deltaTime => { // Update logic B return false;});-- Using node\schedule() to schedule the main update functionnode\schedule (deltaTime) -> -- Main update logic if condition return true -- Return internal state to stop scheduling -- Or use node\unschedule() to actively cancel scheduling-- Using node\onUpdate() to schedule multiple update functionsnode\onUpdate (deltaTime) -> -- Update logic Anode\onUpdate (deltaTime) -> -- Update logic B 5. Abbreviation of Functions In Dora SSR, there are several shorthand versions of functions provided to simplify code writing: thread(function() end) is a shorthand for Routine(once(function() end)). threadLoop(function() end) is a shorthand for Routine(loop(function() end)). ** node:once(function() end)** is a shorthand for node:schedule(once(function() end)). node:loop(function() end) is a shorthand for node:schedule(loop(function() end)). 6. Summary Through this tutorial, you should have learned how to use the Update scheduling mechanism in the Dora SSR game engine. You can utilize coroutine tasks for asynchronous processing to ensure smooth operation of the main thread, and use per-frame update functions to handle logic that needs continuous updates. Understanding the differences between node:schedule() and node:onUpdate() and the shorthand versions of functions can help you write more efficient code. We hope this tutorial helps you master Dora SSR’s Update scheduling and develop outstanding games!