-
-
Notifications
You must be signed in to change notification settings - Fork 186
Frame‐Based Behavior with Coroutines
Nu has it's own specialized implementation of coroutines via F# computation expressions, similar to those as provided by Unity. Nu's coroutines provides a simple yet expressive way to compose frame-based game behavior.
The Nu coroutine's let you describe actions that unfold over several frames. Whether you need to delay an action (a "sleep"), run code on the next frame ("pass"), execute arbitrary side-effects, or combine multiple operations into a single behavior, the coroutine abstraction provides the building blocks to do so. This system helps maintain clean sequential flow in scenarios where the underlying behavior is naturally time-stepped.
Below is a pretty arbitrary example of how you might use this system in a Nu-based world:
coroutine (world.LauncherWhile entity.GetExists) { // coroutine is launched and continues while the given entity exists
do! Coroutine.sleep 10L // wait for 10 frames (in StaticFrameRate mode)
for i in 0 .. dec 30 do // do the following 30 times...
do! Coroutine.pass // once per frame...
if i % 2 = 0 then // on every other frame...
World.playSound 0.25f Assets.Default.Sound world // play this default sound quietly.
do! Coroutine.sleep 10L // wait another 10 frames
Log.info "Wheee!" // execute an arbitrary effect (log info)
do! Coroutine.cancel // end the coroutine...
World.playSound 1.0f Assets.Default.Sound world } // because we're already cancelled, this last line never happens!- Launching with a World Predicate:
coroutine (world.LauncherWhile entity.GetExists) { ... }- The coroutine is launched using a custom launcher that continues executing as long as
entity.GetExistsreturns true. This ensures that all actions within the coroutine occur only if the associated entity is still present in the game world. If the entity is removed at any point, the predicate check will cancel the coroutine.
- Initial Delay:
do! Coroutine.sleep 10L // wait for 10 frames (in StaticFrameRate mode)- The coroutine first pauses for 10 frames using the
sleepcommand. In a StaticFrameRate environment,10Lcorresponds to 10 full frames. This delay sets the stage, allowing any subsequent actions to occur after a controlled waiting period.
- Looping Through Frame-by-Frame Actions:
for i in 0 .. dec 30 do // do the following 30 times...
do! Coroutine.pass // once per frame...
if i % 2 = 0 then // on every other frame...
World.playSound 0.25f Assets.Default.Sound world // play this default sound quietly.- The
forloop iterates 30 times (withdec 30ensuring a proper count by decrementing 30 as needed). -
Frame Progression:
Each iteration begins withdo! Coroutine.pass, which effectively delays the execution until the next frame. This construct enables per-frame processing. -
Conditional Sound Playback:
If the loop counteriis even (if i % 2 = 0), a quiet sound is played by callingWorld.playSoundwith a volume of0.25fand referencing the default sound asset. This setup shows how the coroutine can interleave condition-based side effects along with frame-by-frame progression.
- Additional Delay and Logging Action:
do! Coroutine.sleep 10L // wait another 10 frames
Log.info "Wheee!" // execute an arbitrary effect (log info)- After the loop, the coroutine pauses again for 10 frames, allowing a brief period of inactivity or synchronization.
- Following the delay, an arbitrary action is executed - logging the message "Wheee!". This is done through a lambda expression, demonstrating that the coroutine can seamlessly integrate any custom effect or side-effect, not just those related to gameplay timing.
- Cancellation and Unreachable Code:
do! Coroutine.cancel // end the coroutine...
World.playSound 1.0f Assets.Default.Sound world // because we're already cancelled, this last line never happens!- The coroutine explicitly calls
cancelto terminate its own execution. This cancellation is important, as it is intended to stop any further processing of actions in the sequence. - Although a final sound play command is written after the cancellation, it is never reached. This underscores the mechanism’s ability to prevent unintended actions once a cancellation condition is triggered.
Nu's coroutines provide a powerful yet accessible way to control frame-based behavior. With the combination of clearly defined coroutine primitives and an expressive computation expression builder, developers can write readable, maintainable, and robust game logic that spans multiple frames. This provides a solid foundation for orchestrating complex interactions in dynamic game worlds and interactive systems.