Coroutines
From OrbEdit Wiki
Contents |
Introduction to Coroutines
Coroutines are a special type of Lua function that can be paused and resumed. Coroutines, in combination with Events, are the heart of Orb's scripting system. They allow you to perform one operation and pause until that operation completes, then resume and perform another operation. Pausing returns control to the caller, just as if the function had exited. Resuming begins again at the statement immediately following the pause. This gives you a huge amount of flexibility to do things like:
- Design paths for characters to walk along
- Create particle emitters
- Chain together spell effects
- Make a "listener" that checks for certain conditions every frame
- Write an AI script that polls for new input at certain intervals
Starting Coroutines
The two most important functions to know when working with Orb coroutines are:
-
orb_run(func, id, ...)Starts a new coroutine. As discussed in the Events section, this function associates the function func with the Event ID id and immediately calls func with an arbitrary set of parameters. However, unlike calling func directly, it is called as a coroutine, meaning it can be paused and resumed at any time. -
pause(n)Pauses the execution of a coroutine for n seconds, when called from within a coroutine (that is, from within a function started byorb_run()). When called with no parameter, the coroutine will be paused until the Event ID associated with it is fired. Callingpause()with a value of 0 will pause for exactly 1 frame.
An now, a simple example:
-- Create a coroutine that will print 2 pieces of text with a pause between them orb_run(function() print("But enough talk"); -- Print some text pause(2); -- Pause for 2 seconds print("Have at you!"); -- Print some more text return -1; -- Return -1 to indicate that the coroutine is done end, orb.gueid());
A more complicated example:
-- Create a new Sprite local mySprite = Sprite:new(); -- Obtain an Event ID local id = orb.gueid(); -- Create a coroutine that will slowly move the Sprite back and forth forever orb_run(function() -- Create an infinite loop while(true) do mySprite:move(100, 0, 20, id); -- Move 100 pixels to the right at 20 pixels/second and fire id when done pause(); -- Pause until id is fired mySprite:move(-100, 0, 20, id); -- Move 100 pixels to the left at 20 pixels/second and fire id when done pause(); -- Pause until id is fired end return -1; -- Unreachable code, since the above loop goes forever end, id);
Note that the same Event ID is used throughout the whole coroutine and is passed to each of the move() methods to fire when they complete. Each call to pause() causes the coroutine to wait until the Event ID is fired. Without the ability to pause, the above example would crash your game; the script would go into an infinite loop and the engine would grind to a halt. Pausing, however, gives control back to the engine to continue with its other tasks until it's time to resume.
Stopping Coroutines
Coroutines can end in one of two ways:
- Normally: When the code-path of a coroutine comes to an end, it is cleaned up just like a normal function. Coroutines should always return -1 to indicate that they are done.
- Forced: You can force a coroutine to terminate through the use of
orb_cancel(id), passing in the Event ID of the coroutine to be cancelled. For example, you might want to terminate an animation loop early in order to play another animation in its place. Canceling a coroutine will not stop it immediately, it will only cause it to not be resumed after its next pause. This is only a problem if a coroutine cancels itself viaorb_cancel(), but in that case, it should just return.
To illustrate this point, an example, building on the previous example:
-- <code from previous example> -- Create a second coroutine that will wait 10 seconds and then cancel the coroutine in the above example orb_run(function() pause(10); orb_cancel(id); return -1; end, orb.gueid());
Although a rather contrived example, this illustrates how to easily cancel a coroutine, simply by calling orb_cancel(id) with the Event ID of the coroutine to be cancelled.
Nested Coroutines
Coroutines can be nested without a problem. However, nested coroutines have no effect on their outer coroutine, as seen in the example below:
-- Create an outside coroutine orb_run(function() -- Loop 10 times for i = 0, 10 do pause(2); -- Pause for 2 seconds print("Outside"); -- Print "Outside" -- Create another coroutine orb_fun(function() pause(1); -- Pause for 1 second print("Inside"); -- Print "Inside" return -1; -- Finished end, orb.gueid()); end return -1; end, orb.gueid());
In this example, the call to pause() made from the inside coroutine has no effect on the outer coroutine. Each call to orb_run() creates its own separate thread that is unaffected by pauses from other threads. Each time through the outer loop creates a new coroutine which pauses for one second from the time it was created, and then prints "Inside". Nesting is used frequently in the Tutorials.
