Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Going Souh - Codea Cook Off code
--# Clouds
Clouds = class()
function Clouds:init(x)
local s = shader([[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
uniform vec3 pos;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main() {
vColor = color;
vTexCoord = texCoord;
if(position.y < (pos.y-1000.)) {
// ugly solution to hide clouds close to camera
gl_Position = vec4(0.);
} else {
gl_Position = modelViewProjection * position;
}
}
]], [[
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main() {
lowp vec4 res = texture2D(texture, vTexCoord);
if(res.a == 0.0) { discard; }
gl_FragColor = res;
}
]])
local m = Mesh():up(vec3(0,0,1)):defaults()
math.randomseed(10)
for i=0,2000 do
local x = math.random(-500,500)
local y = math.random()*math.random()*400 + 60
local z = 2000 - i
local a = math.random() * 360
local size = (math.random()*math.random()*1.5 + .5)*640
local p = vec3(x,z,y)*10
m:rotate(a):quad(p,size)
end
self.m = m:construct(s)
self.m.texture = asset("GSClouds")
end
function Clouds:draw(pos)
self.m.shader.pos = pos
self.m:draw()
end
--# Intro
-- Intro screen showing logos with ripple effect and then info text
Intro = class()
function Intro:init()
self.m = mesh()
self.m.shader = shader("Effects:Ripple")
-- parameter to shader which will be animated
-- also use it to animate logo transparency
self.freq = .1
self.m.texture = asset("GSTnlogy")
self.m:addRect(WIDTH/2,HEIGHT/2,512,256)
tween.delay(2, function()
tween(.8, self, {freq=1}, tween.easing.quadInOut, function ()
self.m.texture = asset("GSLogo")
tween(.8, self, {freq=0}, tween.easing.quadInOut,
function ()
tween.delay(1, function()
director:load(Game())
self.showInfo = true
end)
end)
end)
end)
end
function Intro:draw()
self.m.shader.freq = self.freq
self.m.shader.time = ElapsedTime
self.m:setColors(color(255, 255, 255, (1-self.freq)*255))
self.m:draw()
if self.showInfo then
-- flash info text
fill(222, 195, 41, 255 - math.sin(ElapsedTime*3)*140)
font("Futura-CondensedMedium")
fontSize(32)
text("tap to start", WIDTH/2, HEIGHT/2 - 130)
end
end
function Intro:touched(touch)
if self.showInfo then
tween(.4, self, {freq=1}, tween.easing.quadInOut, function ()
director:remove(self)
end)
end
end
--# Game
Game = class()
function Game:init()
physics.gravity(0,0)
self.birds = {}
math.randomseed(10)
for i=1,20 do
local x,y = math.random(-400,400), math.random(-400,400)+i*200
local z = math.random(-40,40)
local b = Bird(vec3(x,y,z))
table.insert(self.birds, b)
end
-- keep a list of birds that will move on user touch
self.followers = {}
-- add the first bird as the player and position camera on it
local player = self.birds[1]
self:addFollower(player.body)
self.pos = player:position()
self.sky = Skybox()
self.clouds = Clouds()
self.cz = 9000
tween(4,self, {cz=0},tween.easing.quadInOut)
end
function Game:draw()
perspective(45,WIDTH/HEIGHT,.1,500000)
camera(self.pos.x, self.pos.y-1500,self.pos.z+600+self.cz,
self.pos.x, self.pos.y, self.pos.z,
0,0,1)
pushMatrix()
translate(self.pos.x, self.pos.y, self.pos.z)
self.sky:draw()
popMatrix()
self.clouds:draw(self.pos)
-- draw flock of birds and calculate new camera center
local center = vec3(0,0,0)
for i,b in ipairs(self.birds) do
b:draw()
if b.body.info.following then
center = center + b:position()
end
end
center = center/#self.followers
-- move camera to new center with a short animation
self.pos = (self.pos*19 + center) * .05
end
function Game:touched(touch)
-- move all following birds on touch event
self.pos.x = self.pos.x + touch.deltaX*.04
self.pos.y = self.pos.y + touch.deltaY*.04
local dv = vec2(touch.deltaX, touch.deltaY)
if dv:len() > 4 then
for i,b in ipairs(self.followers) do
b:move(dv * (1-math.random()*0.1))
end
end
end
function Game:collide(c)
-- if a following bird collides with a non-following bird
-- then add it to the list of following birds.
local follows = function (b) return b.info.following end
local b
if follows(c.bodyB) and not follows(c.bodyA) then
b = c.bodyA
elseif follows(c.bodyA) and not follows(c.bodyB) then
b = c.bodyB
end
if b then self:addFollower(b) end
end
function Game:addFollower(f)
if not f.info.following then
f.info.following = true
table.insert(self.followers, f.info.bird)
end
end
--# Main
-- Birds
function setup()
displayMode(FULLSCREEN)
director = Director(Intro())
end
function draw()
director:draw()
end
function touched(touch)
director:touched(touch)
end
function collide(contact)
director:collide(contact)
end
--# Item
Item = class()
-- get and set angle with vec2 instead of angle
function Item:getDir()
return vec2(0,1):rotate(math.rad(-self.body.angle))
end
function Item:setDir(dir)
self.body.angle = math.deg(dir:angleBetween(vec2(0,1)))
end
function Item:destroy()
self.body:destroy()
self.body = nil
end
--# Bird
Bird = class(Item)
function Bird:init(pos)
local w,h = 50,80 -- wing size
local t = asset("GSWing")
-- discard pixels with alpha 0, slow but easy fix.
local s = shader("Basic:Blend Images")
s.fragmentProgram = [[
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main() {
lowp vec4 res = texture2D(texture, vTexCoord);
if(res.a == 0.0) { discard; }
gl_FragColor = res * vColor;
}
]]
self.left = mesh()
self.left.texture = t
self.left.shader = s
self.left:addRect(w/2,0,w,h)
self.left:setRectTex(1, -1,1,-1,1)
self.right = mesh()
self.right.texture = t
self.right.shader = s
self.right:addRect(-w/2,0,w,h)
-- run a looping tween animating the wing. start it at random time
-- with tween.delay to make the birds seem more individual
self.a = 0
tween.delay(math.random(0,2), function()
tween(0.4, self, {a=80},
{loop=tween.loop.pingpong,tween=tween.easing.quadOutIn})
end)
self.body = physics.body(CIRCLE, 40)
self.body.angularDamping = 4
self.body.linearDamping = 1
-- self.body.restitution = .5
self.body.x, self.body.y = pos.x, pos.y
self.body.info = {bird=self}
self.z = pos.z
self.tail = mesh()
self.tail.texture = asset("GSTail")
self.tail.shader = s
self.tail:addRect(0,-15,10,30)
end
function Bird:position()
return vec3(self.body.x, self.body.y, self.z)
end
function Bird:draw()
-- position bird
pushMatrix()
translate(self.body.x, self.body.y, self.z)
rotate(-self.body.angle)
-- draw wings
pushMatrix()
rotate(-self.a, 0,1,0)
self.left:draw()
popMatrix()
pushMatrix()
rotate(self.a, 0,1,0)
self.right:draw()
popMatrix()
-- draw the five tail feathers, expanded depending on velocity
translate(0,-7)
local v = 20-self.body.linearVelocity:len()*.1
v = math.min(math.max(v,5), 20)
for i=-2,2 do
pushMatrix()
rotate(i*v)
self.tail:draw()
popMatrix()
end
popMatrix()
end
function Bird:move(dv)
self.body.linearVelocity = dv*50
if dv:len() > 1 then
self:setDir((self:getDir() + dv:normalize()*2):normalize())
end
end
--# Director
Director = class()
function Director:init(scene)
self.scenes = {}
self:load(scene)
end
function Director:load(scene)
table.insert(self.scenes, 1, scene)
end
function Director:remove(scene)
for i,v in ipairs(self.scenes) do
if v == scene then
return table.remove(self.scenes, i)
end
end
end
function Director:draw()
background(0, 0, 0, 255)
local c = #self.scenes > 1
for i,scene in ipairs(self.scenes) do
scene:draw()
if c then
-- reset projection to default between scenes
ortho()
viewMatrix(matrix())
end
end
end
function Director:touched(touch)
for i,scene in ipairs(self.scenes) do
scene:touched(touch)
end
end
function Director:collide(contact)
for i,scene in ipairs(self.scenes) do
if scene.collide then
scene:collide(contact)
end
end
end
--# Assets
-- utility function to download assets for the first run.
-- returns an empty image with the correct size while waiting
-- for http.request result.
local baseUrl = "http://goingsouth.meteor.com/"
local c = ContentScaleFactor
local imageMap = {
GSWing = {256,256,"wing.png"},
GSTail = {64,256,"tailfeather.png"},
GSTnlogy = {512,256,"tnlogy.png"},
GSLogo = {512,256,"goingsouth.png"},
GSClouds = {256,256, "clouds.png"},
-- Using skybox graphics by Jochum Skoglund
GSSkynx = {c*512,c*512,"skybox/nx.jpg"},
GSSkyny = {c*512,c*512,"skybox/ny.jpg"},
GSSkynz = {c*512,c*512,"skybox/nz.jpg"},
GSSkypx = {c*512,c*512,"skybox/px.jpg"},
GSSkypy = {c*512,c*512,"skybox/py.jpg"},
GSSkypz = {c*512,c*512,"skybox/pz.jpg"},
}
function asset(name)
local img = readImage("Documents:" .. name)
if img == nil then
local w,h,url = unpack(imageMap[name] or {1,1})
img = image(w,h)
if url then
http.request(baseUrl .. url, function (data)
pushStyle()
setContext(img)
spriteMode(CORNER)
sprite(data,0,0,w,h)
setContext()
popStyle()
saveImage("Documents:" .. name, data)
end)
end
end
return img
end
--# Skybox
Skybox = class()
function Skybox:init()
local s = 20000
local hs = s/2
self.ms = {
Mesh("GSSkyny"):quad(vec3(0,0,-hs),s):construct(),
Mesh("GSSkypy"):up(vec3(0,-1,0)):
quad(vec3(0,0,hs),s):construct(),
Mesh("GSSkynz"):right(vec3(-1,0,0)):
up(vec3(0,0,1)):quad(vec3(0,-hs,0),s):construct(),
Mesh("GSSkypz"):up(vec3(0,0,1)):
quad(vec3(0,hs,0),s):construct(),
Mesh("GSSkypx"):right(vec3(0,-1,0)):up(vec3(0,0,1)):
quad(vec3(hs,0,0),s):construct(),
Mesh("GSSkynx"):right(vec3(0,1,0)):up(vec3(0,0,1)):
quad(vec3(-hs,0,0),s):construct()
}
end
function Skybox:draw()
for i,v in ipairs(self.ms) do
v:draw()
end
end
function Skybox:touched(touch)
end
--# Mesh
Mesh = class()
function Mesh:init(texture)
self.vs, self.uvs = {}, {}
self.buffers = {}
self.texture = texture
self.ds = {
right = vec3(1,0,0),
up = vec3(0,1,0),
size = vec2(1,1),
center = true
}
self:reset()
end
function Mesh:reset()
self.ps = {}
for k,v in pairs(self.ds) do
self.ps[k] = v
end
return self
end
function Mesh:defaults()
for k,v in pairs(self.ps) do
self.ds[k] = v
end
return self
end
function Mesh:buffer(name, val, n)
if not self.buffers[name] then
self.buffers[name] = {}
end
for i=1,n do
table.insert(self.buffers[name], val)
end
return self
end
function Mesh:right(v) self.ps.right = v;return self end
function Mesh:up(v) self.ps.up = v;return self end
function Mesh:size(v) self.ps.size = v;return self end
function Mesh:rotate(a) self.ps.rotate = a;return self end
function mult(m, v) -- matrix multiplication
return vec3(
m[1]*v.x+m[2]*v.y+m[3]*v.z,
m[5]*v.x+m[6]*v.y+m[7]*v.z,
m[9]*v.x+m[10]*v.y+m[11]*v.z
)
end
function Mesh:quad(pos, w, h)
local ps = self.ps
w,h = w or ps.size.x, h or w or ps.size.y
local right = ps.right * w
local up = ps.up * h
if ps.rotate then
local n = right:cross(up):normalize()
local m = matrix():rotate(ps.rotate, n.x,n.y,n.z)
right, up = mult(m,right), mult(m,up)
end
if ps.center then
pos = pos - (right+up)*.5
end
table.insert(self.vs, pos)
table.insert(self.vs, pos+right)
table.insert(self.vs, pos+up)
table.insert(self.vs, pos+right)
table.insert(self.vs, pos+up+right)
table.insert(self.vs, pos+up)
table.insert(self.uvs, vec2(0,0))
table.insert(self.uvs, vec2(1,0))
table.insert(self.uvs, vec2(0,1))
table.insert(self.uvs, vec2(1,0))
table.insert(self.uvs, vec2(1,1))
table.insert(self.uvs, vec2(0,1))
self:reset()
return self
end
function Mesh:construct(s)
local m = mesh()
if s then m.shader = s end
m.vertices, m.texCoords = self.vs, self.uvs
if self.texture then
m.texture = asset(self.texture)
end
for k,v in pairs(self.buffers) do
(m:buffer(k)):set(v)
end
m:setColors(color(255, 255, 255, 255))
return m
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment