Corope is a Lua module to help organize your asynchronous code in games or any situation with a timed main loop. No need for callbacks or other ugly code to do asynchronous code on a single thread. Using Lua coroutines, Corope allows you to easily write AI behaviors, animations, scripting systems, and timing related code in a simple style.
The main idea behind Corope is that you create a bundle of ropes to organize your asynchronous tasks. Each rope is like a thread, but with more built-in goodies. A bundle, also called a dispatcher in some contexts, keeps track of all of your ropes and can create new ropes. Each rope can wait, or do block operations, without halting other ropes, or relying on ugly constructs like callbacks.
Here is an example using LOVE. Note that Corope does not require LOVE or indeed any environment other than a default Lua environment to work properly.
-- Require the module and create our bundle object
local corope = require 'corope'
local bundle = corope()
function love.update(dt)
bundle:update(dt)
end
-- Run this asynchronously
bundle(function(rope)
rope:wait(0.5) -- wait half a second
for i = 1, 10 do
print(i)
rope:wait(1)
end
end)
-- Also run this asynchronously
bundle(function(rope)
for i = 1, 10 do
print('Hey! ' .. i)
rope:wait(1)
end
end)
Besides a simple wait function, Corope also provides tweening and signals for communication between ropes. Corope can do more than what is shown here. Docs coming soon.
local corope = require 'corope'
local bundle = corope(options)
The Corope module is a function used to create a bundle. Takes one optional argument, an options table.
The current options are:
errhand
- A function to handle errors when a rope throws an error. Defaults toprint
.
Creates a new rope. The first argument, fn
, is mandatory and contains the logic of the rope in
a function. It should take at least one argument, the first argument being the new rope object for
use inside the dispatch function. It returns the new rope object as well. The remaining optional
arguments are passed to the dispatch function fn
, after the original rope object.
Update all ropes in the Bundle. Place this function in your main loop and ensure that it is called
every frame/interval. If this is not called, literally nothing will happen with your ropes. The only
parameter is dt
, which is the number of seconds that have passed since the previous update. dt
does
not strictly have to be in seconds, but some of the arguments to Rope:wait
expect dt
to be in seconds.
Suspend execution of a rope. This essentially just removes the rope from a list of ropes that are update every frame/interval and marks it as paused. Only use this for ropes belong to the bundle. Using other bundle's ropes will throw an error. The rope can be resumed later, or it can be eventually garbage collected.
Restart a Rope that has been suspend. Only use this for ropes belong to the bundle. Using other bundle's ropes will throw an error.
Rope functions should only be called inside the rope dispatch function. Calling them outside the dispatch function or inside another coroutine will throw an error.
Wait a certain amount of time before continuing. Note the resolution of the wait is entirely
dependent on how frequently you update the Bundle. amount
can be either a number, string, function,
or nothing
- nothing/nil - waits a single frame regardless of dt.
- number - waits the specified number of seconds
- function - waits until the function evaluates to truthy.
- string - waits an amount of time specified by a number followed by a unit.
Units are as follows:
- 's/seconds' - Seconds
- 'm/minutes' - Minutes
- 'h/hours' - Hours
- 'f/frames' - Number of frames (ignores dt)
Convenience function for rope.bundle(fn, ...)
. Makes a new rope that belongs to
the same bundle as the current rope.
Useful for inter-rope communication within a Bundle. The rope blocks until a signal of the given name is broadcasted. Returns the data associated with the signal.
Broadcasts a signal to all other ropes in the Bundle. If they are listening, they resume with the data in the current update cycle. This call does not block the rope.
A very useful method for doing animations or other tweening over a period of time. The tweening is performed by continually updating a value in a table over a period of time. This method blocks until the tweening is completed. Takes one argument, options, which is a table of parameters for tweening a value in an object.
object
- The table to tween in. Required.key
- The key to tween in the givenobject
. Required.from
- The starting (numeric) value of the tween. Defaults the currentobject[key]
.to
- The final value for the tween. Required.time
- The total amount of time to tween. Takes the same arguments as Rope:wait. Required.ease
- The easing method to use. Uses the same methods as flux, plus some extras. Defaults to linear.
The same as Rope:tween, but does not block. Useful for setting off a bunch of animations at the same time. (Or you can use Rope:fork).
Create any number of knew rope (like Rope:fork), but block until they are all complete or one errors out. Returns success in the first argument, or false and an error message in the second argument if a rope throws an error. Takes a variable number of dispatch functions, each of which will be used to create a new Rope.
Examples are in the examples directory. Currently there are only examples for the LOVE game engine. To run the examples, just cd into the specific example directory and run with love.
- Unit tests
- Travis
- Can ropes be in multiple bundles? Can ropes change bundles? (Not currently, but should they be able to?)
- More utility functionality with ropes.
- Allow rope function inside coroutines.