Skip to content

Instantly share code, notes, and snippets.

@simsaens
Created June 1, 2013 14:19
Show Gist options
  • Save simsaens/5690550 to your computer and use it in GitHub Desktop.
Save simsaens/5690550 to your computer and use it in GitHub Desktop.
Juice Library - Codea Project Gist Created with AutoGist
juice = {}
juice.move = class()
function juice.move:init(p, a, s)
self.pos = p or vec2(0,0)
self.angle = a or 0
self.scale = s or vec2(1,1)
end
function juice.move:combine(m)
return juice.move(
self.pos + m.pos,
self.angle + m.angle,
vec2(self.scale.x * m.scale.x,
self.scale.y * m.scale.y)
)
end
-- A helper method added to the tween library
tween.delay = function(duration, callback)
if duration > 0 then
local t={}
tween(duration, t, {}, tween.easing.linear, callback)
else
if callback then callback() end
end
end
juice.ellipse = class(juice.mover)
function juice.ellipse:init(x, y, w, h)
juice.mover.init(self)
self.pos = vec2(x,y)
self.w = w or 0
self.h = h or w
end
function juice.ellipse:drawObject()
ellipse(0, 0, self.w, self.h)
end
function juice.ellipse:size()
return vec2(self.w, self.h)
end
juice.mover = class(juice.object)
function juice.mover:init()
juice.object.init(self)
self.highlightAmount = 0
end
function juice.mover:spin(rotations, duration, easing, callback)
rotations = rotations or 3
duration = duration or 0.5
easing = easing or tween.easing.quadOut
local a = self.angle - 360 * rotations
self:rotateTo(a, duration, easing,
function()
if callback then callback(self) end
self:fixAngleRange()
end)
end
function juice.mover:bounce(height, hold, call1, call2)
height = height or 100
hold = hold or 0.1
local m,mk = self:startMove()
local savedScale = m.scale
local savedPos = m.pos
local squash = vec2(m.scale.x * 1.5, m.scale.y * 0.75)
local moveDown = savedPos.y - self:size().y/2 * (1 - squash.y)
tween(0.15, m, {scale = squash}, tween.easing.quadInOut)
tween(0.15, m, {pos = vec2(0, moveDown)}, tween.easing.quadInOut,
function()
local moveUp = savedPos.y + height * 0.9
local unsquash = vec2(savedScale.x * 0.85, savedScale.y * 1.25)
tween(0.15, m, {scale = unsquash}, tween.easing.quadInOut)
tween(0.2, m, {pos = vec2(0, moveUp)}, tween.easing.quadOut,
function()
-- Reached top of jump
if call1 then call1(self) end
local holdHeight = savedPos.y + height
tween(hold, m, {pos = vec2(0, holdHeight)}, tween.easing.linear,
function()
tween(0.15, m, {scale = savedScale}, tween.easing.quadInOut)
tween(0.15, m, {pos = savedPos}, tween.easing.quadIn,
function()
self:finishMove(mk)
if call2 then call2(self) end
end )
end)
end )
end )
end
function juice.mover:pulse(amount, repeats, hold, call1, call2)
local s = self.scale
if type(amount) == "number" then
amount = vec2(amount + s.x, amount + s.y)
else
amount = amount or vec2(0.3, 0.3)
amount = amount + s
end
local m,mk = self:startMove()
hold = hold or 0
repeats = repeats or 1
call1 = call1 or function() end
call2 = call2 or call1
tween(0.15, m, {scale=amount}, tween.easing.quadIn,
function()
call1(self)
tween.delay(hold, function()
tween(0.15, m, {scale=s}, tween.easing.quadOut,
function()
call2(self)
self:finishMove(mk)
if repeats > 1 or repeats < 0 then
self:pulse(amount - vec2(1,1), repeats - 1,
hold, call1, call2)
end
end)
end)
end)
end
function juice.mover:squash(amount, hold, duration, call1, call2)
duration = duration or 0.3
local d = duration/2
amount = amount or 0.5
hold = hold or 0.1
local m,mk = self:startMove()
local sx = (1 + amount)
local sy = (1 - amount * 0.5)
local sz = self:size()
local savedScale = m.scale
local savedPos = m.pos
local squash = vec2(sx * 0.9, sy * 0.9)
local diff = (sz.y - (sz.y * squash.y))/2
local moveDown = savedPos.y - diff
tween(d, m, {pos = vec2(0,moveDown)}, tween.easing.quadOut)
tween(d, m, {scale = squash}, tween.easing.quadOut,
function()
if call1 then call1(self) end
local diff = (sz.y - (sz.y * sy))/2
local moveDown = savedPos.y - diff
tween(hold, m, {pos = vec2(0,moveDown)}, tween.easing.quadOut)
tween(hold, m, {scale = vec2(sx,sy)}, tween.easing.quadOut,
function()
tween(d, m, {pos = savedPos}, tween.easing.backOut)
tween(d, m, {scale = savedScale}, tween.easing.backOut,
function()
self:finishMove(mk)
if call2 then call2(self) end
end )
end)
end)
end
function juice.mover:knock(dir, duration, callback)
duration = duration or 0.4
self:moveBy(dir, duration, tween.easing.backOut, callback)
end
function juice.mover:fadeTo(a, duration, callback)
duration = duration or 0.3
tween(duration, self, {alpha = a}, tween.easing.linear,
function()
if callback then callback(self) end
end )
end
function juice.mover:fadeOut(duration, callback)
self:fadeTo(0, duration, callback)
end
function juice.mover:fadeIn(duration, callback)
self:fadeTo(1, duration, callback)
end
function juice.mover:rotateTo(a, duration, easing, callback)
duration = duration or 0.3
easing = easing or tween.easing.quadInOut
local m,mk = self:startMove()
local dest = a - self.angle
tween(duration, m, {angle=dest}, easing,
function()
self:finishMove(mk)
if callback then callback(self) end
end)
end
function juice.mover:rotateBy(a, duration, easing, callback)
self:rotateTo(self.angle + a, duration, easing, callback)
end
function juice.mover:moveTo(p, duration, easing, callback)
duration = duration or 0.3
easing = easing or tween.easing.quadInOut
local m,mk = self:startMove()
local dest = p - self.pos
tween(duration, m, {pos=dest}, easing,
function()
self:finishMove(mk)
if callback then callback(self) end
end)
end
function juice.mover:moveBy(p, duration, easing, callback)
self:moveTo(self.pos + p, duration, easing, callback)
end
function juice.mover:scaleTo(s, duration, easing, callback)
if type(s) == "number" then
s = vec2(s, s)
end
duration = duration or 0.3
easing = easing or tween.easing.quadInOut
local m,mk = self:startMove()
local as = self.scale
local dest = vec2(s.x/as.x, s.y/as.y)
tween(duration, m, {scale=dest}, easing,
function()
self:finishMove(mk)
if callback then callback(self) end
end )
end
function juice.mover:scaleBy(s, duration, easing, callback)
self:scaleTo(self.scale + s, duration, easing, callback)
end
function juice.mover:flash(hold, repeats, call1, call2)
hold = hold or 0
repeats = repeats or 1
local unflash = function()
tween(0.15, self, {highlightAmount=0}, tween.easing.linear,
function()
if call2 then call2(self) end
if repeats > 1 or repeats < 0 then
self:flash(hold, repeats - 1, call1, call2)
end
end)
end
tween(0.15, self, {highlightAmount=1}, tween.easing.linear,
function()
if call1 then call1(self) end
tween.delay(hold, unflash)
end)
end
function juice.mover:draw()
self:startDraw()
self:drawObject()
if self.highlightAmount > 0 then
local hc = color(255,255,255,255*self.highlightAmount)
blendMode(ADDITIVE)
fill(hc)
tint(hc)
self:drawObject()
end
self:finishDraw()
end
juice.object = class()
function juice.object:init()
-- you can accept and set parameters here
self.fill = color(255)
self.stroke = color(0,0,0,0)
self.strokeWidth = 3
self.smooth = false
self.alpha = 1.0
self.pos = vec2(0,0)
self.angle = 0
self.scale = vec2(1,1)
self.moves = {}
self.moveKey = 0
self.lastMoveCache = juice.move()
end
function juice.object:fixAngleRange()
self.angle = self.angle % 360
end
function juice.object:addMove(p, a, s)
local m = juice.move(p,a,s)
local mk = self.moveKey
self.moves[mk] = m
self.moveKey = mk+1
return m, mk
end
function juice.object:startMove()
return self:addMove()
end
function juice.object:applyMove(m)
self.pos = self.pos + m.pos
self.angle = self.angle + m.angle
self.scale = vec2(self.scale.x * m.scale.x,
self.scale.y * m.scale.y)
end
function juice.object:finishMove(mk)
local m = self.moves[mk]
self:applyMove(m)
self.moves[mk] = nil
end
function juice.object:setupStyle()
pushStyle()
tint( self.fill.r, self.fill.g, self.fill.b,
self.fill.a * self.alpha )
fill( self.fill.r, self.fill.g, self.fill.b,
self.fill.a * self.alpha )
stroke( self.stroke.r, self.stroke.g, self.stroke.b,
self.stroke.a * self.alpha )
strokeWidth(self.strokeWidth)
if self.smooth then
smooth()
else
noSmooth()
end
blendMode(NORMAL)
spriteMode(CENTER)
rectMode(CENTER)
ellipseMode(CENTER)
textMode(CENTER)
end
function juice.object:finishStyle()
popStyle()
end
function juice.object:getActualPosition()
local p = self.pos
for k,v in pairs(self.moves) do
p = p + v.pos
end
return p
end
function juice.object:getActualAngle()
local a = self.angle
for k,v in pairs(self.moves) do
a = a + v.angle
end
return a
end
function juice.object:getActualScale()
local s = self.scale
for k,v in pairs(self.moves) do
s = vec2(s.x * v.scale.x, s.y * v.scale.y)
end
return s
end
function juice.object:setupTransform()
pushMatrix()
local p = self.pos
local a = self.angle
local s = self.scale
for k,v in pairs(self.moves) do
p = p + v.pos
a = a + v.angle
s = vec2(s.x * v.scale.x, s.y * v.scale.y)
end
self.lastMoveCache = juice.move(p,a,s)
translate(p.x, p.y)
rotate(a)
scale(s.x, s.y)
end
function juice.object:finishTransform()
popMatrix()
end
function juice.object:startDraw()
juice.object.setupTransform(self)
juice.object.setupStyle(self)
end
function juice.object:finishDraw()
juice.object.finishStyle(self)
juice.object.finishTransform(self)
end
function juice.object:draw()
self:startDraw()
self:drawObject()
self:finishDraw()
end
function juice.object:size()
return vec2(0,0)
end
juice.rect = class(juice.mover)
function juice.rect:init(x, y, w, h)
juice.mover.init(self)
self.pos = vec2(x,y)
self.w = w or 0
self.h = h or w
end
function juice.rect:drawObject()
rect(0, 0, self.w, self.h)
end
function juice.rect:size()
return vec2(self.w, self.h)
end
juice.sprite = class(juice.mover)
function juice.sprite:init(tex, x, y, w, h)
juice.mover.init(self)
self.texture = tex
self.pos = vec2(x,y)
local szx,szy = spriteSize(tex)
local aspect = szy / szx
if w then
self.w = w
self.h = h or w * aspect
else
self.w = szx or 0
self.h = szy or 0
end
end
function juice.sprite:drawObject()
sprite(self.texture, 0, 0, self.w, self.h)
end
function juice.sprite:size()
return vec2(self.w, self.h)
end
juice.text = class(juice.mover)
function juice.text:init(str, x, y)
juice.mover.init(self)
self.pos = vec2(x,y)
self.string = str
self.font = "HelveticaNeue"
self.fontSize = 22
end
function juice.text:drawObject()
font(self.font)
fontSize(self.fontSize)
textMode(CENTER)
text(self.string, 0, 0)
end
function juice.text:size()
pushStyle()
font(self.font)
fontSize(self.fontSize)
local w,h = textSize(self.string)
popStyle()
return vec2(w,h)
end
-- Juice
-- Use this function to perform your initial setup
function setup()
r = juice.rect(WIDTH/2,HEIGHT/2,70)
parameter.integer( "ObjType", 1, 4, 1, objChanged )
parameter.action( "Spin", function() r:spin(2) end )
parameter.action( "Bounce", function() r:bounce(150, 0.3) end )
parameter.action( "Bounce & Spin",
function()
r:bounce(150, 0.3, function() r:spin(1, 0.3) end )
end )
parameter.action( "Knock",
function()
r:knock( vec2(-100,0) )
end)
parameter.action( "Pulse", function() r:pulse(0.3, 3) end )
parameter.action( "Flash", function() r:flash(0, 3) end )
parameter.action( "Squash",
function()
r:squash(1, 0.3)
end )
parameter.action( "Bounce, Spin & Squash",
function()
r:bounce(150, 0.3, function() r:spin(1, 0.3) end, function() r:squash(0.3,0.01,0.3) end)
end)
parameter.action( "Fade Out", function() r:fadeOut() end )
parameter.action( "Fade In", function() r:fadeIn() end )
end
function objChanged(val)
if val == 1 then
print("juice.rect")
r = juice.rect(WIDTH/2,HEIGHT/2,70)
r.fill = color(234, 139, 41, 255)
elseif val == 2 then
print("juice.ellipse")
r = juice.ellipse(WIDTH/2,HEIGHT/2,70)
r.fill = color(234, 139, 41, 255)
elseif val == 3 then
print("juice.sprite")
r = juice.sprite("Platformer Art:Guy Standing",WIDTH/2,HEIGHT/2,50)
r.fill = color(255)
elseif val == 4 then
print("juice.text")
r = juice.text("juice", WIDTH/2, HEIGHT/2)
r.fill = color(255, 95, 0, 255)
r.fontSize = 54
end
r.stroke = color(244, 180, 100, 255)
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(2)
-- Do your drawing here
stroke(255)
line(0, HEIGHT/2 - 35, WIDTH, HEIGHT/2 - 35)
r:draw()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment