Skip to content

Instantly share code, notes, and snippets.

@NathanFlurry
Created October 7, 2013 00:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NathanFlurry/6861006 to your computer and use it in GitHub Desktop.
Save NathanFlurry/6861006 to your computer and use it in GitHub Desktop.
Today, I wanted to learn in depth how to use the 3D aspect of Codea, so I embarked on making a simple 3D prototype of my game StackIt. Please note, there's tons of bugs, but I needed to keep working on the actual StackIt, so I stopped the development there.
--# Main
displayMode(FULLSCREEN)
supportedOrientations(PORTRAIT_ANY)
function setup()
bg = getGradient(vec2(WIDTH,HEIGHT),color(0,90,255),color(50,190,255))
Game:init()
end
-- This function gets called once every frame
function draw()
background(0, 0, 0)
pushMatrix()
translate(WIDTH/2,HEIGHT/2,-10)
bg:draw()
popMatrix()
Game:draw()
end
function touched(t)
if Game.touched then
Game:touched(t)
end
end
shader = shader([[
attribute vec4 position;
attribute vec3 normal;
attribute vec3 color;
uniform mat4 modelViewProjection;
varying vec3 eyenormal;
varying vec3 diffuse;
void main(void)
{
eyenormal = mat3(modelViewProjection) * normal;
diffuse = color;
gl_Position = modelViewProjection * position;
}
]], [[
uniform highp vec3 light;
varying mediump vec3 eyenormal;
varying lowp vec3 diffuse;
void main(void)
{
highp vec3 N = normalize(eyenormal);
highp vec3 L = normalize(light);
highp vec3 E = vec3(0, 0, 1);
highp vec3 H = normalize(L + E);
highp float df = max(0.0, dot(N, L)) * 0.3;
highp float sf = max(0.0, dot(N, H));
sf = pow(sf, 250.0);
if(abs(N.z) < 0.2) sf = sf + (1.0 - abs(N.z)*(1.0/0.2));
sf = sf * 0.5;
lowp vec3 color = vec3(1,1,1) * 0.7 * diffuse + df * diffuse + sf * vec3(1,1,1);
gl_FragColor = vec4(min(1.0,color.x), min(1.0,color.y), min(1.0,color.z), 1.0);
}
]]
)
--# Game
Game = class()
function Game:init()
self.paddle = Paddle()
self.oManager = ObjectManager()
end
function Game:draw()
pushMatrix()
perspective(45, WIDTH/HEIGHT)
camera(0,500,-500, 0,0,0, 0,1,0)
-- Paddle
self.paddle:draw()
self.oManager:draw()
popMatrix()
-- HUD
ortho()
viewMatrix(matrix())
fill(255)
font("MyriadPro-Bold")
fontSize(30)
--text("Hello", WIDTH/2, HEIGHT - 30)
end
function Game:touched(t)
self.paddle:touched(t)
end
--# Paddle
Paddle = class()
function Paddle:init()
self.x = 0
self.y = 0
self.size = vec3(125,25,125)
self.m = getCube()
self.m.colors = shadeCube(color(255))
end
function Paddle:draw()
if self.x < -WIDTH/2 then self.x = -WIDTH/2
elseif self.x > WIDTH/2 then self.x = WIDTH/2
elseif self.y < -HEIGHT/2 then self.y = -HEIGHT/2
elseif self.y > HEIGHT/2 then self.y = HEIGHT/2 end
pushMatrix()
resetMatrix()
translate(-self.x,0,self.y)
scale(self.size.x,self.size.y,self.size.z)
self.m:draw()
popMatrix()
end
function Paddle:touched(t)
self.x = self.x + t.deltaX
self.y = self.y + t.deltaY
end
--# ObjectManager
ObjectManager = class()
function ObjectManager:init()
self.oList = {}
self.colors = {
color(165,195,255),
color(255,240,45),
color(255,110,85),
color(185,130,210),
color(160,255,165),
color(255,255,155),
color(255,170,250)
}
self.range = WIDTH/2
self:addObject()
self.oSpawnTimer = Timer(4, function() self:addObject() end)
end
function ObjectManager:draw()
self.oSpawnTimer:update()
for i,o in ipairs(self.oList) do
o:draw()
if o.height < -HEIGHT then
table.remove(self.oList,i)
end
end
end
function ObjectManager:addObject()
local o = Object()
o.x = (math.random()-.5)*self.range
o.y = (math.random()-.5)*self.range
o.m.colors = shadeCube(self.colors[math.random(#self.colors)])
table.insert(self.oList,o)
end
--# Object
Object = class()
function Object:init()
self.x = 0
self.y = 0
self.height = HEIGHT*.3
self.speed = 1
self.size = vec3(25,25,25)
self.landingNode = nil
self.id = math.random()
self.m = getCube()
end
function Object:draw()
if not self.landingNode then
self.height = self.height - self.speed
self:testCollisions()
else
self.x = Game.paddle.x + self.landingNode.x
self.y = Game.paddle.y + self.landingNode.y
end
shadowBlur = self.height/10
pushMatrix()
resetMatrix()
translate(-self.x,self.height,self.y)
scale(self.size.x,self.size.y,self.size.z)
self.m:draw()
popMatrix()
if self.x > Game.paddle.x-Game.paddle.size.x/2 and self.x < Game.paddle.x+Game.paddle.size.x/2 and self.y > Game.paddle.y-Game.paddle.size.x/2 and self.y < Game.paddle.y+Game.paddle.size.x/2 and self.height > 0 then
pushMatrix()
pushStyle()
resetMatrix()
translate(-self.x,Game.paddle.size.y/2+1,self.y)
rotate(90,1,0,0)
scale(shadowBlur)
fill(0,0,0,50)
noStroke()
rect(-self.size.x/2/shadowBlur,-self.size.y/2/shadowBlur,self.size.x/shadowBlur,self.size.y/shadowBlur)
popStyle()
popMatrix()
end
end
function Object:testCollisions()
for i,v in ipairs(Game.oManager.oList) do
local xO = math.abs(self.x-v.x) < (self.size.x+v.size.x)/2
local yO = math.abs(self.y-v.y) < (self.size.z+v.size.z)/2
local zO = math.abs(self.height-v.height) < (self.size.y+v.size.y)/2
if self.id ~= v.id and (xO and yO and zO) then
self.landingNode = vec2(self.x-Game.paddle.x,self.y-Game.paddle.y)
end
end
local xO = math.abs(self.x-Game.paddle.x) < (self.size.x+Game.paddle.size.x)/2
local yO = math.abs(self.y-Game.paddle.y) < (self.size.z+Game.paddle.size.z)/2
local zO = self.height < (self.size.y+Game.paddle.size.y)/2
if xO and yO and zO then
self.landingNode = vec2(self.x-Game.paddle.x,self.y-Game.paddle.y)
end
end
--# Timer
Timer = class()
function Timer:init(interval,callback)
self.interval = interval
self.callback = callback
self.time = 0
self.occurances = 0
self.paused = false
end
function Timer:update()
if self.paused ~= true then
self.time = self.time + DeltaTime
if self.time >= self.interval then
self.occurances = self.occurances + 1
self.time = 0
if type(self.callback) == "function" then
self.callback(self.occurances)
end
return true
else
return false
end
end
end
function Timer:reset()
self.time = 0
self.occurances = 0
end
function Timer:pause()
self.paused = true
end
function Timer:resume()
self.paused = false
end
function Timer:isPaused()
return self.paused
end
function Timer:getTime()
return self.time
end
function Timer:getCount()
return self.occurances
end
--# Functions
-- Test 3D Bounds
function testCollisions(b1p,b1s,b2p,b2s)
local xO = math.abs(b1p.x-Game.paddle.x) < (self.size.x+Game.paddle.size.x)/2
local yO = math.abs(b1p.y-Game.paddle.y) < (self.size.z+Game.paddle.size.z)/2
local zO = self.height < (self.size.y+Game.paddle.size.y)/2
if self.id ~= Game.paddle.id and (xO and yO and zO) then
self.landingNode = vec2(self.x-Game.paddle.x,self.y-Game.paddle.y)
end
end
-- Explode table
function _values(t)
local i = 0
return function() i = i + 1; return t[i] end
end
function _explodeNext(iter, t)
local v = iter(t)
if v ~= nil then
return v, _explodeNext(iter, t)
else
return
end
end
function explodeTable(t)
local iter = _values(t)
return _explodeNext(iter, t)
end
-- Unreference table
function cleanTable(t)
local nt = {}
for i,v in pairs(t) do
if type(v) == "table" then
nt[i] = cleanTable(v)
else
nt[i] = v
end
end
return nt
end
-- Gradient
function getGradient(size,color1,color2)
local m = mesh()
m.setSize = function(size)
m.vertices = {
vec2(-size.x/2,-size.y/2),
vec2(-size.x/2,size.y/2),
vec2(size.x/2,-size.y/2),
vec2(size.x/2,size.y/2),
vec2(-size.x/2,size.y/2),
vec2(size.x/2,-size.y/2)
}
end
m.setGradient = function(color1,color2)
m.colors = {
color2,
color1,
color2,
color1,
color1,
color2
}
end
m.setSize(size)
m.setGradient(color1,color2)
return m
end
-- 3D Cubes
function getCube()
local m = mesh()
m.vertices, m.texCoords = getCubeVerts()
return m
end
function getCubeVerts()
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
local cubeverts = {
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
local texvertices = { vec2(0.03,0.24),
vec2(0.97,0.24),
vec2(0.03,0.69),
vec2(0.97,0.69) }
local cubetexCoords = {
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
return cubeverts, cubetextCoords
end
-- Shade cube
function shadeCube(oC)
local sA = 100
local c = {}
-- Far
for i = 1,6 do
table.insert(c,color(oC.r-sA,oC.g-sA,oC.b-sA))
end
-- Left
for i = 1,6 do
table.insert(c,color(oC.r-sA,oC.g-sA,oC.b-sA))
end
-- Close
for i = 1,6 do
table.insert(c,color(oC.r-sA/2,oC.g-sA/2,oC.b-sA/2))
end
-- Right
for i = 1,6 do
table.insert(c,color(oC.r+sA/4,oC.g+sA/4,oC.b+sA/4))
end
-- Top
for i = 1,6 do
table.insert(c,oC)
end
-- Bottom
for i = 1,6 do
table.insert(c,color(oC.r-sA,oC.g-sA,oC.b-sA))
end
return c
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment