#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# 3D Air Hockey

edited July 2013 Posts: 1,595

This is my first 3D project, it uses the 2D physics engine to create the physics needed for the table top effect.
Here's the code, its a two player game with a basic score system, the score doesn't have a limit although air hockey is 7.

``````--# Main
-- 3Dairhockey

-- Use this function to perform your initial setup
function setup()
displayMode(FULLSCREEN)

cube = {}
cube = Cube(vec3(0,0,145)*5,vec3(440,4,20)*5)
cube = Cube(vec3(210,0,100)*5,vec3(20,4,80)*5)
cube = Cube(vec3(210,0,-100)*5,vec3(20,4,80)*5)
cube = Cube(vec3(-210,0,100)*5,vec3(20,4,80)*5)
cube = Cube(vec3(-210,0,-100)*5,vec3(20,4,80)*5)
cube = Cube(vec3(0,0,-145)*5,vec3(440,4,20)*5)
cube = Cube(vec3(0,-10,0)*5,vec3(5,1,300)*5)
cube:setColors(50,50,255,100)
puck = Cylinder(vec3(0,0,0),60,15)
puckp = physics.body(CIRCLE,60)
puckp.position = vec2(0,0)
puckp.gravityScale = 0
puckp.restitution = 1
puckp.friction = 0
puckp.sleepingAllowed = false
puckp.interpolate = true
puckp.categories = {1}
floorv = {vec2(200,-60)*5,vec2(200,-135)*5,vec2(-200,-135)*5,vec2(-200,-60)*5}
floor = physics.body(CHAIN,false,unpack(floorv))
floor.categories = {0,1}
floor.restitution = 1
floorv = {vec2(200,60)*5,vec2(200,135)*5,vec2(-200,135)*5,vec2(-200,60)*5}
floor2 = physics.body(CHAIN,false,unpack(floorv))
floor2.categories = {0,1}
floor2.restitution = 1
t1 = Touch()
t2 = Touch()
ply1 = Cylinder(vec3(0,0,WIDTH-200),100,15)
plyp1 = physics.body(CIRCLE,100)
plyp1.position = vec2(WIDTH-200,0)
plyp1.gravityScale = 0
plyp1.friction = 0.2
plyp1.restitution = 0.2
plyp1.sleepingAllowed = false
plyp1.interpolate = true
plyp1.categories = {0,1}

ply2 = Cylinder(vec3(0,0,0),100,15)
plyp2 = physics.body(CIRCLE,100)
plyp2.position = vec2(-800,0)
plyp2.gravityScale = 0
plyp2.friction = 0.2
plyp2.restitution = 0.2
plyp2.sleepingAllowed = false
plyp2.interpolate = true
plyp2.categories = {0,1}

splitter = physics.body(EDGE,vec2(0,-135*5),vec2(1,135*5))
splitter.type = STATIC
back1 = physics.body(EDGE,vec2(1000,-60*5),vec2(1001,60*5))
back1.categories = {0}
back1.type = STATIC
back2 = physics.body(EDGE,vec2(-1000,-60*5),vec2(-1001,60*5))
back2.categories = {0}
back2.type = STATIC

score1 = 0
score2 = 0
end

function touched(t)
if t.state == BEGAN then
if t.x > WIDTH/2 then
t1:touched(t)
elseif t.x < WIDTH/2 then
t2:touched(t)
end
end

if t1.mov and t.id == t1.id then
t1:touched(t)
end

if t2.mov and t.id == t2.id then
t2:touched(t)
end

end

-- This function gets called once every frame
function draw()
if t1.state == MOVING then
if t1.x > WIDTH/2 then
t1.state = ENDED
end
plyp1:applyForce(vec2(t1.deltaX,-t1.deltaY)*1000)
end

if t2.state == MOVING then
if t2.x < WIDTH/2 then
t2.state = ENDED
end
plyp2:applyForce(vec2(t2.deltaX,-t2.deltaY)*1000)
end

if puckp.x > 1200 or puckp.x < -1200 then
if puckp.x > 1200 then
score1 = score1 + 1
end
if puckp.x < -1200 then
score2 = score2 + 1
end
puckp.position  = vec2(0,0)
puckp.linearVelocity = vec2(0,0)
plyp1.position = vec2(600,0)
plyp2.position = vec2(-600,0)
end

-- This sets a dark background color
background(40, 40, 50)

-- This sets the line thickness
pushStyle()
fill(255)
text("Player 1 Score:"..score1,120,75)
text("Player 2 Score:"..score2,WIDTH-120,75)
popStyle()
perspective(90, WIDTH/HEIGHT)

-- Position the camera up and back, look at origin
camera(0,900,1,  0, 0, 0, 0,1,0)

for k,v in pairs(cube) do
v:draw()
end
puckp.linearVelocity = puckp.linearVelocity*0.992
plyp1.linearVelocity = plyp1.linearVelocity*0.9
plyp2.linearVelocity = plyp2.linearVelocity*0.9
pushMatrix()
local puckpos = puckp.position
translate(puckpos.x,0,puckpos.y)
puck:draw()
popMatrix()

pushMatrix()
local plypos = plyp1.position
translate(plypos.x,0,plypos.y)
ply1:draw()
popMatrix()

pushMatrix()
local plypos = plyp2.position
translate(plypos.x,0,plypos.y)
ply2:draw()
popMatrix()

ortho()

-- Restore the view matrix to the identity
viewMatrix(matrix())

end

--# Cube
Cube = class()

function Cube:init(pos,size)
-- you can accept and set parameters here
self.pos = pos
self.size = size
self.verts = {
pos+vec3(-0.5*size.x, -0.5*size.y,  0.5*size.z), -- Left  bottom front
pos+vec3( 0.5*size.x, -0.5*size.y,  0.5*size.z), -- Right bottom front
pos+vec3( 0.5*size.x,  0.5*size.y,  0.5*size.z), -- Right top    front
pos+vec3(-0.5*size.x,  0.5*size.y,  0.5*size.z), -- Left  top    front
pos+vec3(-0.5*size.x, -0.5*size.y, -0.5*size.z), -- Left  bottom back
pos+vec3( 0.5*size.x, -0.5*size.y, -0.5*size.z), -- Right bottom back
pos+vec3( 0.5*size.x,  0.5*size.y, -0.5*size.z), -- Right top    back
pos+vec3(-0.5*size.x,  0.5*size.y, -0.5*size.z), -- Left  top    back
}

-- now construct a cube out of the vertices above
self.cverts = {
-- Front
self.verts, self.verts, self.verts,
self.verts, self.verts, self.verts,
-- Right
self.verts, self.verts, self.verts,
self.verts, self.verts, self.verts,
-- Back
self.verts, self.verts, self.verts,
self.verts, self.verts, self.verts,
-- Left
self.verts, self.verts, self.verts,
self.verts, self.verts, self.verts,
-- Top
self.verts, self.verts, self.verts,
self.verts, self.verts, self.verts,
-- Bottom
self.verts, self.verts, self.verts,
self.verts, self.verts, self.verts,
}
self.texverts = { vec2(0.03,0.24),
vec2(0.97,0.24),
vec2(0.03,0.69),
vec2(0.97,0.69) }

-- apply the texture coordinates to each triangle
self.texCoords = {
-- Front
self.texverts, self.texverts, self.texverts,
self.texverts, self.texverts, self.texverts,
-- Right
self.texverts, self.texverts, self.texverts,
self.texverts, self.texverts, self.texverts,
-- Back
self.texverts, self.texverts, self.texverts,
self.texverts, self.texverts, self.texverts,
-- Left
self.texverts, self.texverts, self.texverts,
self.texverts, self.texverts, self.texverts,
-- Top
self.texverts, self.texverts, self.texverts,
self.texverts, self.texverts, self.texverts,
-- Bottom
self.texverts, self.texverts, self.texverts,
self.texverts, self.texverts, self.texverts,
}
self.m = mesh()
self.m.vertices = self.cverts
self.m.texCoords = self.texCoords
self.m:setColors(255,255,255,255)
-- all the unique texture positions needed
end

function Cube:setColors(colr,colg,colb,alpha)
local a = alpha or 255
self.m:setColors(colr,colg,colb,a)
end

function Cube:draw()
self.m:draw()
end

function Cube:touched(touch)
-- Codea does not automatically call this method
end
``````

• Posts: 1,595

Cylinder and touch:

``````--# Cylinder
Cylinder = class()

-- you can accept and set parameters here
self.pos = pos
self.h = height

local vertices={}
local edges = {}
local numfaces=24
local top=height
local bottom=0

--corners every second set of faces
for face=1,numfaces do
end
local cubeverts = {}
for face=1,numfaces do
table.insert(cubeverts,vertices[(face*4)-3])
table.insert(cubeverts,vertices[(face*4)-2])
table.insert(cubeverts,vertices[(face*4)-1])
table.insert(cubeverts,vertices[(face*4)-3])
table.insert(cubeverts,vertices[(face*4)-1])
table.insert(cubeverts,vertices[(face*4)])
end

local texv={}
for i=0,numfaces-1 do
table.insert(texv,vec2(i/numfaces,0))
table.insert(texv,vec2((i+1)/numfaces,0))
table.insert(texv,vec2(i/numfaces,1))
table.insert(texv,vec2((i+1)/numfaces,1))
end

local cubetexCoords={}

for face=1,numfaces do
table.insert(cubetexCoords,texv[(face*4)-3])
table.insert(cubetexCoords,texv[(face*4)-2])
table.insert(cubetexCoords,texv[(face*4)])
table.insert(cubetexCoords,texv[(face*4)-3])
table.insert(cubetexCoords,texv[(face*4)])
table.insert(cubetexCoords,texv[(face*4)-1])
end
local edg = triangulate(edges)
for i=1,#edg do
edg[i] = vec3(edg[i].x,bottom,edg[i].y)
end
local bottomverts = edg

local edg2 = triangulate(edges)
for i=1,#edg2 do
edg2[i] = vec3(edg[i].x,top,edg[i].y)
end
local topverts = edg2

self.t = top

self.top = mesh()
self.top.vertices = topverts
self.top:setColors(200,200,200,255)

self.bottom = mesh()
self.bottom.vertices = bottomverts
self.bottom:setColors(200,200,200,255)

self.m = mesh()
self.m.vertices = cubeverts
self.m:setColors(150,150,150,255)
self.m.texCoords = cubetexCoords
end

function Cylinder:draw()
self.m:draw()
self.top:draw()
pushMatrix()
translate(0,self.t,0)
self.bottom:draw()
popMatrix()
end

function Cylinder:touched(touch)
-- Codea does not automatically call this method
end

--# Touch
Touch = class()

function Touch:init()
-- you can accept and set parameters here
self.x = 0
self.y = 0
self.deltaX = 0
self.deltaY = 0
self.state = nil
self.prevX = 0
self.prevY = 0
self.mov = nil
self.id = nil
end

function Touch:draw()
-- Codea does not automatically call this method
end

function Touch:touched(t)
if t.state == BEGAN and self.mov == nil then
self.mov = true
self.id = t.id
end
if self.mov then
self.t = t
self.x = t.x
self.y = t.y
self.deltaX = t.deltaX
self.deltaY = t.deltaY
self.state = t.state
self.prevX = t.prevX
self.prevY = t.prevY
end
if t.state == ENDED and self.mov then
self.mov = nil
self.id = nil
end
end
``````
• Posts: 391

@Luatee, very nice! I did quickly find a bug, however. The puck is able to get stuck on a wall or in a corner. It seems as though when the puck location matches the location of a wall, then it will not gain velocity to bounce off the wall.

• Posts: 437

Wow, very clever combination, why would you use chains in stead of polygons to create the physic bodies of the board?

It remains me of a game I made last year:

• edited July 2013 Posts: 1,595

@Slashin8r I know that, it's something I think I've fixed now but I'm still not sure ill need to check it out more

@juaxix Thanks I thought of it a few days ago, hadn't seen anyone doing it before, in the forums anyway. Well I'm just starting off in 3D so I've still got a few things to learn, well a lot of things such as lighting and shaders but what you've got looks amazing I'm doubting I'll be able to make anything like that soon, just a few obstacles..

video for people who don't want to copy and paste:

• Posts: 355

cool game , nice work @luatee

• edited August 2013 Posts: 59

How is it 3D?

• Posts: 1,595

It has a camera and a perspective, you'll notice the change in the pucks when moved

• Posts: 59

Oh, okay. I messed with the camera and now it looks totally 3D, though the puck doesn't work anymore.

• Posts: 1,595

Yeah the pucks work on each side of the screen in landscape, using the delta of your touch

• Posts: 59

Ok