Chipmunk work pretty well for simple top down - just dial gravity down to zero. This was my first shot at using it for a simple billiards sim. Definitely primitive but maybe worth a look.
Strict
Import BaH.Chipmunk
Rem
Public domain source code
courtesy of <a href="http://PHYSLE.com" target="_blank">http://PHYSLE.com</a>
requires Brucey's chipmuck mod
<a href="http://maxmods.googlecode.com" target="_blank">http://maxmods.googlecode.com</a>
EndRem
Const FRICTION:Float=.99
Const COLLISION:Float=.99
Const MAXSPEED:Float=15
Const BALLSIZE:Float=20
Const HOLESIZE:Float=50
Const POWERUP:Float=.5
Const TABLEW:Int=800
Const TABLEH:Int=500
Global game:Tgame=New Tgame
game.Rack()
'''------- classes
Type TBody Extends CPBody
Field x:Float,y:Float
Method New()
Create(10,1)
EndMethod
Method sync()
Local v:CPVect=GetPosition()
x=v.GetX()
y=v.GetY()
EndMethod
Method setXY(_x,_y)
x=_x
y=_y
SetPosition(Vec2(x,y))
EndMethod
Method stop()
SetVelocity(Vec2(0,0))
EndMethod
Method move(a:Float,s:Float)
SetVelocity(Vec2(s*Sin(a),-s*Cos(a)))
EndMethod
Method ato:Float(b:TBody)
Local n:Float=b.x-x
If n=0 n=.0001 ''dont divide by 0
Local a2:Float=ATan((b.y-y)/n)
If (b.x-x)<0 a2:+180
Return a2+90
EndMethod
Method dto:Float(b:TBody)
Local _h:Float = Abs(x-b.x)
Local _w:Float = Abs(y-b.y)
Return Sqr((_w*_w)+(_h*_h))
EndMethod
Method draw()
Plot(x,y)
EndMethod
EndType
Type Tmouse Extends TBody
Method setXY(_x,_y)
x=_x
y=_y
EndMethod
EndType
Type Tbumper Extends TBody
Field w,h
Field shape:CPPolyShape
Method New()
Create(INFINITY,INFINITY)
EndMethod
Method draw()
SetColor(0,45,0)
DrawRect(x-(w/2),y-(h/2),w,h)
SetColor(255,255,255)
EndMethod
EndType
Type Thole Extends TBody
Field d
Method New()
d=HOLESIZE
EndMethod
Method setXY(_x,_y)
''no need to setPosition - this isnt a real physics object
x=_x
y=_y
EndMethod
Method draw()
SetColor(0,0,0)
DrawOval(x-(d/2),y-(d/2),d,d)
SetColor(255,255,255)
EndMethod
EndType
Type Tball Extends TBody
Field number
Field shape:CPCircleShape
Field d:Float
Method New()
Create(10,1)
d=BALLSIZE
EndMethod
Method Free() 'ARAAGGHH!!
shape.Free()
Super.Free()
EndMethod
Method color()
Select number
Case 1 SetColor(230,200,0)
Case 2 SetColor(0,0,190)
Case 3 SetColor(200,0,0)
Case 4 SetColor(100,0,100)
Case 5 SetColor(0,200,0)
Case 6 SetColor(200,100,0)
Case 7 SetColor(100,0,0)
Case 8 SetColor(0,0,0)
Case 9 SetColor(230,200,0)
Case 10 SetColor(0,0,190)
Case 11 SetColor(200,0,0)
Case 12 SetColor(100,0,100)
Case 13 SetColor(0,200,0)
Case 14 SetColor(200,100,0)
Case 15 SetColor(100,0,0)
EndSelect
EndMethod
Method draw()
sync()
If number > 8
DrawOval(x-(d/2),y-(d/2),d,d)
color()
DrawRect(x-(d/2)+2,y-6,d-4,12)
SetColor(255,255,255)
DrawOval(x-(d/4),y-(d/4),d/2,d/2)
SetColor(0,0,0)
If number=9
DrawText(number,x-(d/4),y-(d/4)-1)
Else
DrawText(number,x-(d/4)-3,y-(d/4)-1)
EndIf
SetColor(255,255,255)
ElseIf number
color()
DrawOval(x-(d/2),y-(d/2),d,d)
SetColor(255,255,255)
DrawOval(x-(d/4),y-(d/4),d/2,d/2)
SetColor(0,0,0)
DrawText(number,x-(d/4),y-(d/4)-1)
SetColor(255,255,255)
Else
DrawOval(x-(d/2),y-(d/2),d,d)
EndIf
EndMethod
EndType
Type Tgame
Field space:CPSpace
Field maxCuePower:Float=MAXSPEED
Field cuePower:Float
Field cue:Tball
Field mouse:Tmouse
Field placing,placed
Field texta:Float
Field holes:TList
Method New()
InitChipmunk()
space=New CPSpace
space.Create()
space.setDamping(FRICTION)
holes=New TList
cue=New Tball
placing=True
texta=2.0
mouse=New Tmouse
EndMethod
Method Rack()
newBumper(TABLEH-HOLESIZE,HOLESIZE/2,TABLEW,HOLESIZE)
newBumper(HOLESIZE/2,TABLEH/2,HOLESIZE,TABLEH)
newBumper(TABLEW-HOLESIZE/2,TABLEH/2,HOLESIZE,TABLEH)
newBumper(TABLEH-HOLESIZE,TABLEH-HOLESIZE/2,TABLEW,HOLESIZE)
newHole(HOLESIZE,HOLESIZE)
newHole(HOLESIZE,TABLEH-HOLESIZE)
newHole(TABLEW-HOLESIZE,HOLESIZE)
newHole(TABLEW-HOLESIZE,TABLEH-HOLESIZE)
newHole(TABLEW/2,HOLESIZE)
newHole(TABLEW/2,TABLEH-HOLESIZE)
cue=newBall(0,600,250)
newBall(1,300,250)
newBall(2,279,240)
newBall(9,279,261)
newBall(10,258,230)
newBall(8,258,251)
newBall(4,258,272)
newBall(7,237,220)
newBall(13,237,241)
newBall(3,237,262)
newBall(11,237,283)
newBall(15,216,210)
newBall(12,216,231)
newBall(5,216,252)
newBall(14,216,273)
newBall(6,216,294)
EndMethod
Method updateUI()
If placing
cue.setXY(MouseX(),MouseY())
If MouseX()>TABLEW-HOLESIZE Or MouseX()<HOLESIZE
FlushMouse()
ElseIf MouseY()>TABLEH-HOLESIZE Or MouseY()<HOLESIZE
FlushMouse()
ElseIf MouseHit(1)
cue.stop()
placing=False
placed=True
EndIf
Else
If MouseDown(1) And placed=False
If cuePower<maxCuePower cuePower:+POWERUP
ElseIf placed=True And MouseDown(1)=False
placed=False
ElseIf cuePower>0 And Not placed
mouse.setXY(MouseX(),MouseY())
cue.move(cue.ato(mouse)-180,cuePower)
cuePower=0
EndIf
EndIf
EndMethod
Method drawUI()
SetAlpha(texta)
DrawText("[SPACE] to see angle",12,2)
DrawText("[ESC] to rack balls",12,14)
DrawText("[Q] to quit game",12,26)
If texta > .01
texta:-.005
Else
texta=0
EndIf
SetAlpha(1)
If MouseDown(1) And Not placed
Local n:Int=Min(Int((cuePower/maxcuepower)*100),100)
SetColor(Min(500.0*Float(n)/100.0,255.0)-30.0,Min(500.0-(500.0*Float(n)/100.0),255.0)-30.0,0)
DrawText(n+"%",MouseX()+12,MouseY()+18)
ElseIf KeyDown(KEY_SPACE)
DrawLine(MouseX(),MouseY(),cue.x,cue.y)
EndIf
SetColor(255,255,255)
EndMethod
Function doBalls(o:Object, data:Object)
If Tball(o)
Local b:Tball=Tball(o)
b.draw()
For Local h:Thole=EachIn game.holes
If b.dto(h) < h.d/2
If b=game.cue
game.placing=True
FlushMouse()
Else
b.Free()
'FIXED
'b.setXY(-10000,-10000)
EndIf
EndIf
Next
EndIf
EndFunction
Function doBumpers(o:Object, data:Object)
If Tbumper(CPShape(o).GetBody())
Tbumper(CPShape(o).GetBody()).draw()
EndIf
EndFunction
Method run()
space.GetStaticShapes().ForEach(doBumpers)
For Local h:Thole=EachIn holes
h.draw()
Next
space.EachBody(doBalls)
space.DoStep(1)
updateUI()
drawUI()
EndMethod
EndType
'''------- helper functions
Function newBall:Tball(n,x,y)
Local b:Tball=New Tball
b.number=n
b.shape=New CPCircleShape
b.shape.Create(b,b.d/2,Vec2(0,0))
b.shape.SetElasticity(COLLISION)
b.setXY(x,y)
game.space.AddShape(b.shape)
game.space.AddBody(b)
Return b
EndFunction
Function newBumper:Tbumper(x,y,w,h)
Local br:Tbumper=New Tbumper
br.w=w
br.h=h
br.shape=New CPPolyShape
br.shape.Create(br, [Vec2(x-w/2,y-h/2),Vec2(x-w/2,y+h/2),Vec2(x+w/2,y+h/2),Vec2(x+w/2,y-h/2)], Vec2(0,0))
br.shape.SetElasticity(COLLISION)
br.setXY(x,y)
game.space.AddStaticShape(br.shape)
Return br
EndFunction
Function newHole:Thole(x,y)
Local h:Thole=New Thole
h.setXY(x,y)
game.holes.AddLast(h)
Return h
EndFunction
'''------- main procedure
Graphics(TABLEW,TABLEH)
SetClsColor(0,60,0)
SetBlend(ALPHABLEND)
While Not AppTerminate()
Cls
game.run()
If KeyHit(KEY_ESCAPE)
game.space.Free()
game=New Tgame
game.Rack()
FlushMouse()
ElseIf KeyHit(KEY_Q)
Exit
EndIf
Flip
Wend