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 in Code Sharing 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[1] = Cube(vec3(0,0,145)*5,vec3(440,4,20)*5) cube[2] = Cube(vec3(210,0,100)*5,vec3(20,4,80)*5) cube[3] = Cube(vec3(210,0,-100)*5,vec3(20,4,80)*5) cube[4] = Cube(vec3(-210,0,100)*5,vec3(20,4,80)*5) cube[5] = Cube(vec3(-210,0,-100)*5,vec3(20,4,80)*5) cube[6] = Cube(vec3(0,0,-145)*5,vec3(440,4,20)*5) cube[7] = Cube(vec3(0,-10,0)*5,vec3(5,1,300)*5) cube[7]: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.mask = {1} 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() -- Do your drawing here 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.shader = shadr 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.shader = shadr puck:draw() popMatrix() pushMatrix() local plypos = plyp1.position translate(plypos.x,0,plypos.y) ply1.shader = shadr ply1:draw() popMatrix() pushMatrix() local plypos = plyp2.position translate(plypos.x,0,plypos.y) ply2.shader = shadr 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[1], self.verts[2], self.verts[3], self.verts[1], self.verts[3], self.verts[4], -- Right self.verts[2], self.verts[6], self.verts[7], self.verts[2], self.verts[7], self.verts[3], -- Back self.verts[6], self.verts[5], self.verts[8], self.verts[6], self.verts[8], self.verts[7], -- Left self.verts[5], self.verts[1], self.verts[4], self.verts[5], self.verts[4], self.verts[8], -- Top self.verts[4], self.verts[3], self.verts[7], self.verts[4], self.verts[7], self.verts[8], -- Bottom self.verts[5], self.verts[6], self.verts[2], self.verts[5], self.verts[2], self.verts[1], } 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[1], self.texverts[2], self.texverts[4], self.texverts[1], self.texverts[4], self.texverts[3], -- Right self.texverts[1], self.texverts[2], self.texverts[4], self.texverts[1], self.texverts[4], self.texverts[3], -- Back self.texverts[1], self.texverts[2], self.texverts[4], self.texverts[1], self.texverts[4], self.texverts[3], -- Left self.texverts[1], self.texverts[2], self.texverts[4], self.texverts[1], self.texverts[4], self.texverts[3], -- Top self.texverts[1], self.texverts[2], self.texverts[4], self.texverts[1], self.texverts[4], self.texverts[3], -- Bottom self.texverts[1], self.texverts[2], self.texverts[4], self.texverts[1], self.texverts[4], self.texverts[3], } self.m = mesh() self.m.vertices = self.cverts self.m.texCoords = self.texCoords --self.m.texture = readImage("Documents:checker") 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

Comments

  • Posts: 1,595

    Cylinder and touch:


    --# Cylinder Cylinder = class() function Cylinder:init(pos,radius,height) -- you can accept and set parameters here self.pos = pos self.rad = radius self.h = height local vertices={} local edges = {} local numfaces=24 local top=height local bottom=0 local r=radius --corners every second set of faces for face=1,numfaces do rot=face*math.rad(360/numfaces) adj=math.rad(180/numfaces)--was6 edges[face] = vec2(r*math.sin(rot-adj),r*math.cos(rot-adj)) vertices[(4*face)-3]=vec3(r*math.sin(rot-adj), bottom, r*math.cos(rot-adj)) -- Left bottom front vertices[(4*face)-2]=vec3( r*math.sin(rot+adj), bottom, r*math.cos(rot+adj)) -- Right bottom front vertices[(4*face)-1]=vec3( r*math.sin(rot+adj), top, r*math.cos(rot+adj)) -- Right top front vertices[(4*face)]=vec3(r*math.sin(rot-adj), top, r*math.cos(rot-adj)) -- Left top front 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.bottom.shader = shadr self.m = mesh() self.m.vertices = cubeverts self.m:setColors(150,150,150,255) self.m.texCoords = cubetexCoords --self.m.texture = readImage("Documents:colour") self.m.shader = shadr 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

  • 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

  • Ok

Sign In or Register to comment.