Chipmunk Free() problem

BlitzMax Forums/BlitzMax Programming/Chipmunk Free() problem

I ported a simple billiards game I had made to chipmunk

http://www.blitzbasic.com/Community/posts.php?topic=73636


Everything is working except freeing shapes and bodies.

I realize you have to free shapes before freeing objects but this does not solve it. Some reference still sticks around in the space which is referenced in EachBody(). I noticed there was a destroy() function associated with bodies in the c api which is not in the wrapper. Any ideas??

Strict 
Import BaH.Chipmunk

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!!
	If shape
		shape.Free()	
	EndIf
	If cpObjectPtr Then
		cpBodyFree(cpObjectPtr)
		cpObjectPtr = Null
	End If
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
					'this creates a memory disaster
					'b.Free()
					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







problems spots are

Function doBalls(o:Object, data:Object)

and

Tball.Free()

Apologies :-)

Seems the shapes/objects need to be removed from the space too, which I've neglected to handle. I'll add that to the Free() method so you don't need to worry about it.

I've committed the fixes to the repository.

You'll otherwise have to wait til I get home to sort out building a proper release :-p

Nice little game btw ;-)

You can change doBalls() to use b.Free() again, and ball.Free() can look something like this:
Method Free()
	If shape
		shape.Free()	
	EndIf

	Super.Free()
EndMethod

The reason you should call Super.Free() is because it makes sure the body is removed from the parent space too. (like what should have happened!)

:o)

Cool - thanks for looking at it. Chipmunk seems pretty high maintenance in terms of memory management - e.g. freeing every shape on a rag doll to blow up a space zombie. Guess its nothing some higher level code couldnt fix.