[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Use of Lua corotines in games
- From: Steven Johnson <temujin98007@...>
- Date: Mon, 19 Mar 2007 23:33:43 -0700 (PDT)
On Mon, 19 Mar 2007 05:26:38 -0600, Jose Marin <jose_marin2@yahoo.com.br> wrote:
> Hi.
> I'm studying Lua corotines, and looks very interesting.
> Is there any sample of use of Lua corotines in games?
Hi.
I use something like the following in my own projects. It was originally just supposed to be for cut scenes, but
has since spared me from lots of really messy state machines when scripting stuff like game mode logic and AI.
The update parameter in each operation (Wait, WaitForSignals, WaitUntil) is optional, but lets you do stuff while
the operation is in progress. You can return true from it for an early out, in which case the operation returns
nothing; otherwise the operation returns true when it finishes. The data parameter is just meant for passing
input to a predefined update function, where you can't trap needed upvalues.
Also, in the Wait operation, the "Pump" and "Timeline" instances are a message pump and Flash-ish timelines
that I use to set up and run optional events during the wait. They're a secondary thing here, though, so I won't
clutter this up with their implementations. Everything else should be fairly straightforward and at least make
sense with the examples.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
local error = error
local GetLapse = GetLapse -- Returns the time lapse since the last frame
local IsCallable = IsCallable -- Argument is a function or has a __call metamethod
local ipairs = ipairs
local new = class.new -- Instantiates a class of the type in argument #1; constructor takes arguments #2-N
local yield = coroutine.yield
local function Call (func, ...)
if func then
return func(...)
end
end
local function Body (update, body, done, finish)
while not done() do
-- Update any user-defined logic. On a true result, do an early out.
if Call(update) then
Call(finish)
return
end
-- Update any body logic.
Call(body)
-- Pause the operation until the next turn.
yield()
end
-- Do any cleanup and report success.
Call(finish, params)
return true
end
function Wait (duration, builders, update, data)
local pump, time, timelines = new("Pump"), 0, {}
-- Build and install each timeline, providing the pump as a helper.
for _, func in ipairs(builders or {}) do
timelines[#timelines + 1] = new("Timeline")
func(timelines[#timelines], pump, data)
end
-- Wait for the duration to pass.
return Body(update and function()
return update(time, duration, pump, data)
end or nil, function()
for _, timeline in ipairs(timelines) do
timeline:Update(time - timeline:GetTime())
end
time = time + GetLapse()
end, function()
return time > duration
end)
end
function WaitForSignals (signals, count, how, update, data)
local func, test
-- If the signals are not callable, build an indexing function. Build a table if
-- nothing is provided.
func, signals = IsCallable(signals) and signals or function(index)
return signals[index]
end, signals or {}
-- Build the test function.
if how == "any" then
test = function()
for index = 1, count do
if func(index) then
return true
end
end
end
elseif how == "every" then
test = function()
for index = 1, count do
if not func(index) then
return
end
end
return true
end
else
error("Unsupported test behavior")
end
-- Wait for the operation to succeed.
return Body(update and function()
return update(signals, count, data)
end or nil, nil, test)
end
function WaitUntil (test, update, data)
return Body(update and function()
return update(data)
end or nil, nil, function()
return test(data)
end)
end
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A bunch of toy examples to show how it works. The caps/underscores were just used in the documentation to
distinguish from functions that actually exist. And of course, these must all be called within the body of a function
passed to coroutine.create/wrap.
-- Wait two seconds.
Wait(2)
-- Run the game for five minutes, but quit if everybody leaves the game.
if Wait(300, nil, function(time)
if EVERYBODY_LEFT() then
return true
end
UPDATE_GAME(time)
end) then
SHOW_WINNERS()
end
GO_BACK_TO_LOBBY()
-- Wait five seconds; update objects during the second half.
Wait(5, {
function(timeline, pump)
timeline:Add(2.5, function()
pump:Add(function()
UPDATE_OBJECTS()
return true
end)
end)
end
}, function(_, _, pump)
pump:Run()
end)
-- Wait for any opponent to show and then attack.
WaitForSignals(function(index)
return me ~= players[index] and IS_IN_THE_OPEN(players[index])
end, #players, "any")
CHOOSE_AND_ATTACK(me, players)
-- Wait until every lever is turned, then open the door.
WaitForSignals(nil, #levers, "every", function(signals)
for index, lever in ipairs(levers) do
signals[index] = signals[index] or IS_BEING_TURNED(lever)
end
end)
OPEN_DOOR()
-- Run until all 50 tiles are flipped. The creatures will be flipping tiles the whole time. Since
-- they also do this before the WaitForSignals call, the tiles set is passed as input, instead of
-- letting WaitForSignals create a temporary table.
local tiles = {}
local function UpdateCreatures ()
for creature in GET_CREATURES() do
local index = GET_TILE(creature)
tiles[index] = not tiles[index]
end
end
Wait(10, nil, UpdateCreatures ) -- Mill around for 10 seconds
WaitForSignals(tiles, 50, "every", function(signals, count)
local index = math.random(count)
signals[index] = not signals[index]
-- Creatures wander over nearby tiles, flipping them.
UpdateCreatures()
end)
-- Wait for the light, then go.
WaitUntil(SEMAPHORE_IS_GREEN)
GO()
-- Wait for the hunter to reach its prey, then attack. Look for another target if it gets away.
WaitUntil(PREY_IS_IN_RANGE, function()
if PREY_GOT_AWAY() then
return true
end
MOVE_HUNTER_CLOSER()
end) and ATTACK() or GO_HUNTING()
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Similar:
http://lua-users.org/lists/lua-l/2005-03/msg00109.html
The last few Game Programming Gems books are probably worth a look, too.
____________________________________________________________________________________
Food fight? Enjoy some healthy debate
in the Yahoo! Answers Food & Drink Q&A.
http://answers.yahoo.com/dir/?link=list&sid=396545367