Codea is by far my favorite app ever to be released on the app store. Recently I was playing with it and really wished for more advanced features, like a more fully featured class system, hierarchical scene management, tweening, and some other things. Cocos2d-iphone is a great fully-featured game engine in Obective-C for iOS that provides these things very nicely. Since it’s likely that nobody will ever add Lua bindings for all of cocos2d-iphone and include it in Codea, I decided to port Cocos2d to Lua/Codea. I’ve been working feverishly over the last week to get as much as I could in, and it’s gotten to a point where I don’t want to keep it to myself anymore. I’ve got most of the core ported (minus a couple of features). There’s still a lot missing, like sprite sheet support, batched sprite drawing, tile maps, bitmapped fonts, particles, and effects, but it’s in a state where you can really start doing some cool things with it. It’s not a straight port; I’ve taken liberties with some of the API, added some low hanging fruit that was missing, and implemented some of the functionality in a different way (for example, using Codea’s rendering system under the hood).
Currently it’s not documented, there may be some bugs, and the code is highly volatile. There are a few crappy examples, but I’m going to spend the next couple of hours trying to add some more to show off what CocosCodea can do.
It’s a lot of files, I know. Hopefully this will not matter anymore once TLL includes project referencing in Codea
Please give me any comments and suggestions. If you want to contribute, that would be lovely. If not, that’s okay too. I’ve had a blast really digging into Codea and Lua, and it’s been a great learning experience so far. I’ll update here periodically with progress. Cheers!
Wow that’s an amazing project (and concept). Love to see where you take this.
Cocos2D is a great API - though I think we can achieve the tween stuff more elegantly in Lua (I.e., tween.lua). Modeling a scene graph on Cocos would be cool.
I like tween.lua, but I find cocos2d actions to much more powerful. I do think the cocos2d actions API is a bit cumbersome though, and would not mind improving it in CocosCodea. Lua has actually improved the API some, but I’m definitely hoping to improve it further. Incidentally, you should be able to use tween.lua alongside CocosCodea pretty easily if you prefer it. Alternatively, some parts of CocosCodea can be used independently, like the scheduler, the class/mixin system, and the targeted touch system.
@toadkick John has done a version of tween with sequences, callbacks (just insert any function/closure into a sequence), and loops. I think it’s just as versatile as the Cocos2D actions (and a lot less code to write).
@Simeon: oh cool, I haven’t seen his version. I will look for it so I can check it out for inspiration, I’m definitely looking for ways to reduce code volume where possible to make CocosCodea easier to use with the iPad’s keyboard. I’ve shortened several of the action’s names, and I’ve been playing around with a new way to define complex sequences of actions inspired by some of the stuff in tween.lua. Can’t wait to find John’s version to see how he handles sequences.
Here’s an example of what I’m playing with so far, to make an action for a character that moves to (100, 100) and back with CCEaseSineInOut easing, and repeats 2 times:
local s = CCSequence(CCMoveBy(2, 100, 100), CCMoveBy(2, -100, -100))
node:runAction(CCRepeat(CCEaseSineInOut(s), 2))
It’s still more verbose than I’d like, but I think it’s an improvement. Another interesting thing to note is that Lua enabled me to improve the CCCall* actions somewhat so they require a less boilerplate code.
Hey guys, just an update to say that I’ve got CCSpriteBatchNode implemented (using Codea’s mesh() object), which means it is now possible to batch draw sprites with CocosCodea.
Currently there is no support for tools like TexturePacker or Zwoptex, but you can use a sprite sheet and manually set the texture coordinates for your sprites at the moment. Then next step for me will probably be to implement CCSpriteFrame and CCSpriteFrameCache, which is a step in the direction of TP/Zwoptex implementation, and will make it easier to deal with batched sprites/sprite sheets in general. Cheers!
It attaches itself to the draw loop automatically. I’ll post an example main file in a sec.
tween = {}
tween.list = {}
tween.easeTypes = {}
tween.ease = "linear"
tween.easeTypes.linear = function(t,b,c,d)
return c*t/d + b
end
tween.easeTypes.inOutQuad = function(t,b,c,d)
t = t / (d/2)
if t < 1 then return c/2*t*t + b end
t = t - 1
return -c/2 * (t*(t-2) - 1) + b
end
function tween._update()
for k,tw in ipairs(tween.list) do
tw.time = tw.time + DeltaTime
tw.update(tw.time)
if tw.time >= tw.duration then
if tw.repeats and (tw.repeats == -1 or tw.count < tw.repeats) then
tw.time = 0
tw.count = tw.count + 1
tw.update(0)
else
table.remove(tween.list, k)
end
end
end
end
function tween._tweenForToken(token)
for k,tw in ipairs(tween.list) do
if tw.token == token then
return tw
end
end
return nil
end
function tween.sequence(...)
local seq = {}
seq.list = {}
seq.duration = 0.0
seq.time = 0.0
seq.offset = 0.0
seq.index = 1
-- count duration of sequence
for k,token in ipairs(arg) do
if type(token) ~= "function" then
local tw = tween._tweenForToken(token)
if tw == nil then tw = token end
tween.remove(tw)
table.insert(seq.list, tw)
seq.duration = seq.duration + tw.duration
else
table.insert(seq.list, token)
end
end
seq.current = seq.list[1]
seq.update = function(time)
if time == 0 then
seq.offset = 0
seq.index = 1
seq.current = seq.list[1]
end
seq.current.time = time - seq.offset
seq.current.update(seq.current.time)
if seq.current.time >= seq.current.duration and seq.current.repeats and
(seq.current.repeats == -1 or seq.current.count < seq.current.repeats) then
seq.current.time = 0
seq.current.count = seq.current.count + 1
seq.current.update(0)
seq.time = seq.time - seq.current.duration
elseif seq.current.time >= seq.current.duration and seq.index < #seq.list then
-- non-loop tween, goto the next tween
seq.offset = seq.offset + seq.current.duration
seq.index = seq.index + 1
seq.current = seq.list[seq.index]
if type(seq.current) == "function" then
seq.current()
if seq.index < #seq.list then
seq.index = seq.index + 1
seq.current = seq.list[seq.index]
seq.current.update(0)
end
else
seq.current.update(0)
end
end
end
table.insert(tween.list, seq)
return seq
end
function tween.remove(tw)
for k,v in ipairs(tween.list) do
if v == tw then
table.remove(tween.list, k)
return
end
end
end
function tween.loop(tw, repeats)
local loop = {}
loop.tween = tw
loop.duration = tw.duration
loop.time = 0.0
loop.count = 1
loop.repeats = repeats or -1
tween.remove(tw)
loop.update = function(time)
loop.tween.update(time)
end
table.insert(tween.list, loop)
return loop
end
function tween.delay(d)
local delay = {}
delay.update = function(time) end
delay.time = 0
delay.duration = d
table.insert(tween.list, delay)
return delay
end
function tween._setup()
local mt = {}
mt.__call = function(caller,target, args)
local token = {}
local mt2 = {}
mt2.__newindex = function(t, key, value)
-- check if the key being animated can be interpolated
local start = target[key]
local tp = type(start)
local valid = false
if tp == "number" then
valid = true
elseif tp == "table" or tp == "userdata" then
mt = getmetatable(start)
if mt.__add and mt.__sub and mt.__mul then
valid = true
end
end
assert(valid, "cannot tween value (must define add/subtract/multiply operators)")
local tweener = {}
tweener.update = function(time)
if time == 0 then
tweener.valueA = target[key]
tweener.delta = tweener.valueB - tweener.valueA
end
target[key] = tweener.ease(time, tweener.valueA, tweener.delta, tweener.duration)
end
tweener.duration = (args and args.duration) or 1.0
tweener.time = 0.0
tweener.target = target
tweener.key = key
tweener.valueA = start
tweener.valueB = value
tweener.ease = tween.easeTypes[(args and args.ease) or tween.ease]
tweener.update(0)
tweener.token = token
table.insert(tween.list, tweener)
end
mt2.__index = function(t, key)
return target[key]
end
setmetatable(token, mt2)
return token
end
local _draw = draw
draw = function()
tween._update()
_draw()
end
setmetatable(tween, mt)
end
tween._setup()
-- Use this function to perform your initial setup
function setup()
print("Hello World!")
circle = {pos = vec2(WIDTH/2,HEIGHT/2), radius = 50, col = color(255, 0, 0, 255)}
t1 = tween(circle, {ease = "inOutQuad"})
t1.pos = vec2(0,0)
t2 = tween(circle)
t2.pos = circle.pos
l1 = tween.loop(tween.sequence(t1, t2, function() print("blah!") end), 3)
tween.sequence(l1, function() print("blob!") end)
t3 = tween(circle, {duration = 5})
t3.radius = 75
--tween(circle.col) = {r = 0, g = 0, b = 0}
--circle.test = "blah"
--tween(circle).test = "something"
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
fill(circle.col)
ellipse(circle.pos.x, circle.pos.y, circle.radius)
-- Do your drawing here
end
The interesting thing about this tween library is that tween(object) returns a new object which you can set a property to animate. You can also pass these tween tokens to some other functions, such as tween.sequence (run tweens one after the other), and tween.loop (run tweens multiple times). There are some things missing, such as pausing, stopping and finding tweens by name. There may also be some stability issues and issues with deep nesting of sequences and loops.
For my part I’d be happy if Juice (when it’s released) includes scene graph support (maybe it already does?). Now that we have tweening and faster sprite rendering built in, frankly I really don’t have much use for 90% of what’s in CocosCodea anymore One day I would like to revisit this though and streamline it a bit…after much more experience hacking around in Lua I’m pretty sure most of what’s in CocosCodea could be done more elegantly, with a more Lua-ish feel.