Chipmunk 2D Physics Module

BlitzMax Modules Forums/Brucey's Modules/Chipmunk 2D Physics Module

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