Help with Gravity/Bounce Code...

Blitz3D Forums/Blitz3D Programming/Help with Gravity/Bounce Code...

I whipped up this little demo to show basic gravity and a ball bouncing. The problem is that it's buggy as heck. Sometimes it bounces super high. Other times it bounces twice and stops. See anything wrong?

btw: if you want to run it you need the vector.bb lib from the archives

;// Gravity and Bounce Example
Graphics3D 800,600,16
SetBuffer BackBuffer()

Include "vector.bb"

;// Set Gravity meters per sec
g# = -9.8

;// Ball Parameters
ball = CreateSphere()
ScaleEntity ball,0.5,0.5,0.5
mass = 10

;// Set Initial Forces and Vectors
vFor.vector = vector()				;force vector
vAcc.vector = vector()				;acceleration vector
vVel.vector = vector()				;velocity vector
vPos.vector = vector(0,20,0)		        ;position vector

;// Timing 
NewTime% = MilliSecs()
OldTime% = NewTime

;// Camera and Lights
camera = CreateCamera()
PositionEntity camera,0,0,-20
light  = CreateLight()

;// Main Loop
While Not KeyHit(1)
Cls

;// Find Delta Time
NewTime = MilliSecs()
delta# = Float (NewTime - OldTime) / 1000
OldTime = NewTime
FPS = 1 / delta

;// Reset Forces to Zero Each Cycle
Vector_Reset vFor

;// Calculate Gravity
vFor\y = g * mass

;// Collision
If vPos\y < 0 Then Collided = True
If Collided = True
	speed# = Vector_Magnitude(vVel)
	collided = False
	vFor\y = -vFor\y * (Float (speed / 2)) * mass
	vVel\y = 0
EndIf

;// Up Arrow Key applies 100 force upwards
If KeyDown(200) Then vFor\y = 100

;// Calc New Acc, Vel, and Pos
Vector_DivideScalar vAcc,vFor,mass
Vector_AddTimeStep vVel,vAcc,delta
Vector_AddTimeStep vPos,vVel,delta

;// Position the Ball
Vector_PositionEntity ball,vPos

UpdateWorld
RenderWorld

;// Show What's Happening
Text 5,5,"FPS=" + FPS
Text 5,50,"Speed=" + Int(Vector_Magnitude(vVel))
Vector_Show(vFor,5,100,"Force")
Vector_Show(vAcc,5,175,"Acc")
Vector_Show(vVel,5,250,"Vel")
Vector_Show(vPos,5,325,"Pos")

Flip
Wend
End


No problems yet and I ran it about 12 times... same results every time...
{{EDIT}}OK after I post that I see what you mean... it starts going wonky...

It looks like the velocity is being ADDED to rather than being subtracted in a constant ratio... I changed the drop height to 15 (added a mirror plane as well for fun) and it sometimes adds and adds... it is probably something in the vector.bb... let me look...

Oh my code:
;// Gravity and Bounce Example
Graphics3D 800,600
SetBuffer BackBuffer()

Include "vector.bb"

;// Camera and Lights
camera = CreateCamera()
PositionEntity camera,0,1,-20
light=CreateLight()
RotateEntity light,90,0,0

;// Set Gravity meters per sec
g# = -9.8

;// Ball Parameters
ball = CreateSphere()
ScaleEntity ball,0.5,0.5,0.5
mass = 10

; for a bit of fun
plane=CreatePlane()
grass_tex=LoadTexture( "tmp11.jpg" )
EntityTexture plane,grass_tex
EntityAlpha plane,0.5
PositionEntity plane,0,0,-.5
; make plane a mirror
mirror=CreateMirror()

;// Set Initial Forces and Vectors
vFor.vector = vector()				;force vector
vAcc.vector = vector()				;acceleration vector
vVel.vector = vector()				;velocity vector
vPos.vector = vector(0,15,0)		        ;position vector

;// Timing 
NewTime% = MilliSecs()
OldTime% = NewTime

;// Main Loop
While Not KeyHit(1)
Cls

;// Find Delta Time
NewTime = MilliSecs()
delta# = Float (NewTime - OldTime) / 1000
OldTime = NewTime
FPS = 1 / delta

;// Reset Forces to Zero Each Cycle
Vector_Reset vFor

;// Calculate Gravity
vFor\y = g * mass

;// Collision
If vPos\y < 0 Then Collided = True
If Collided = True
	speed# = Vector_Magnitude(vVel)
	collided = False
	vFor\y = -vFor\y * (Float (speed / 2)) * mass / 2
	vVel\y = 0
EndIf

;// Up Arrow Key applies 100 force upwards
If KeyDown(200) Then vFor\y = 100

If KeyHit(57) = True Then 
	vPos.vector = vector(0,15,0)
	;Vector_PositionEntity ball,vPos
EndIf

;// Calc New Acc, Vel, and Pos
Vector_DivideScalar vAcc,vFor,mass
Vector_AddTimeStep vVel,vAcc,delta
Vector_AddTimeStep vPos,vVel,delta

;// Position the Ball
Vector_PositionEntity ball,vPos

UpdateWorld
RenderWorld

;// Show What's Happening
Text 5,5,"FPS=" + FPS
Text 5,50,"Speed=" + Int(Vector_Magnitude(vVel))
Vector_Show(vFor,5,100,"Force")
Vector_Show(vAcc,5,175,"Acc")
Vector_Show(vVel,5,250,"Vel")
Vector_Show(vPos,5,325,"Pos")

Flip
Wend
End


I made a small change to the vector.bb it is slightly more accurate and realistic change the funtion thus:
Function Vector_Magnitude#(v.Vector)
	Return Sqr((v\x * v\x) + (v\y * v\y) + (v\z * v\z)) / 2
End Function
and run it now!!!
Then I did this to see:
;// Gravity and Bounce Example
Graphics3D 800,600
SetBuffer BackBuffer()

Include "vector.bb"

;// Camera and Lights
camera = CreateCamera()
PositionEntity camera,0,1,-20
light=CreateLight()
RotateEntity light,90,0,0

;// Set Gravity meters per sec
g% = -9 ; changed it to flat number

;// Ball Parameters
ball = CreateSphere()
ScaleEntity ball,0.5,0.5,0.5
mass% = 10

; for a bit of fun
plane=CreatePlane()
grass_tex=LoadTexture( "tmp11.jpg" )
EntityTexture plane,grass_tex
EntityAlpha plane,0.5
PositionEntity plane,0,0,-.5
; make plane a mirror
mirror=CreateMirror()

;// Set Initial Forces and Vectors
vFor.vector = vector()				;force vector
vAcc.vector = vector()				;acceleration vector
vVel.vector = vector()				;velocity vector
vPos.vector = vector(0,15,0)		        ;position vector

NewTime = MilliSecs()
OldTime = NewTime



;// Main Loop
While Not KeyHit(1)
Cls

NewTime = MilliSecs()
delta# = Float (NewTime - OldTime) / 1000
OldTime = NewTime
FPS = 1 / delta

;// Reset Forces to Zero Each Cycle
Vector_Reset vFor

;// Calculate Gravity
grav% = g * mass ; so you can show it
vFor\y = g * mass 

;// Collision
If vPos\y < 0 Then Collided = True
If Collided = True
	speed% = Vector_Magnitude(vVel)
	collided = False
	vFor\y = -vFor\y * speed% / 2  * mass% 
	vVel\y = 0
EndIf

;// Up Arrow Key applies 100 force upwards
If KeyDown(200) Then vFor\y = 100

If KeyHit(57) = True Then 
	vPos.vector = vector(0,15,0)
	;Vector_PositionEntity ball,vPos
EndIf

;// Calc New Acc, Vel, and Pos
Vector_DivideScalar vAcc,vFor,mass
Vector_AddTimeStep vVel,vAcc,delta
Vector_AddTimeStep vPos,vVel,delta

;// Position the Ball
Vector_PositionEntity ball,vPos

UpdateWorld
RenderWorld

;// Show What's Happening
Text 5,5,"FPS=" + FPS
Text 5,30,"Speed=" + Int(Vector_Magnitude(vVel))
Text 5,60,"Grav: "+grav

Vector_Show(vFor,5,100,"Force: ")
Vector_Show(vAcc,5,175,"Acc: ")
Vector_Show(vVel,5,250,"Vel: ")
Vector_Show(vPos,5,325,"Pos: ")

Flip
Wend
End

MASS seems to be a part of the problem... monkey with that and see...

For some reason velocity adds at some point(possibly because of mass be applied wrongly I am uncertain!)... which is why I squelched magnitude a bit...

RZ

Change Flip to Flip False
The ball drops like a stone.
This may give you the idea what's wrong ... delta calculation could be wrong was my first thought when I looked at the code

but it is weird that the bouncing would reduce normally and then spike to a positive 40 to 60 + up a bounce...

BUT since MASS and GRAVITY effect DeltaV in his program it could just be something so simple...

Hmmm

RZ

You consider the ball has collided if the y-position is below 0 right?
Now, what will happen if the ball is still below 0 in the next frame?
When you detect the collision, try positioning the ball at 0 or a little bit above, like 0.001 or something, and see if that helps.

[Edit] To "visualize" the problem, add a CollisionCount variable that you increase by one at every collision and print it on screen.

Yep it was registering multiple collisions on a single bounce. Setting the vPos\y to 0 after a successful collision fixed it.

See... it was something very simple... Good catch Sweenie!

Well it works for a simple bouncing off a floor (actualy a limiting value). Now make it so you can have 2 balls bounce off each other with the right trajectory. Not simple.

Doing a pool type game? There MIGHT be some sample code...

RZ

It is pretty simple really ... I'm sure darkeagle / swift and others have examples in the archives or over at bcoder.

In fact here's one ..
http://www.blitzbasic.com/codearcs/codearcs.php?code=670

You might have read this already but I post it anyway.
It's a great tutorial on the topic.
http://www.gamasutra.com/features/20020118/vandenhuevel_01.htm

This demo - not by me, BTW - may be of some use?

;
; Bouncing ball physics - by Vincent DeCampo 2/1/04
;

Const GRAVITY#=-.05
Const BALLS=1

Graphics3D 640,480

;*** Type for Ball ***
Type Ball_Object
	Field Entity
	Field Pivot
	Field x#
	Field y#
	Field z#
	Field dx#
	Field dy#
	Field dz#
	Field xMin#
	Field xMax#
	Field yMin#
	Field yMax#
	Field zMin#
	Field zMax#
	Field elasticity#
	Field friction#
End Type

;*** Lets create the floor ***
Room=CreateTerrain(64)
PositionEntity room,-32,0,-32
EntityColor Room, 0, 150, 0

;******* Create Initial Ball ******
Blitz.Ball_Object = New Ball_Object
InitBallVars(Blitz)
Collisions BALLS, BALLS, 1, 2

;***** Create Camera *****
camera=CreateCamera()
PositionEntity camera,0,5,-40
PointEntity camera, Blitz\Entity

;**********************************
;			 Main Loop
;**********************************
;
While Not KeyHit(1)

	If KeyHit(57) Then

		;***** Seed RND Generator *****
		SeedRnd (MilliSecs())
		
		;*** Create New Ball ***
		Blitz.Ball_Object = New Ball_Object	
		InitBallVars Blitz

	End If

	;**** Perform Room Physics ****
	For Ball.Ball_Object = Each Ball_Object
		BallPhysics (Ball)		
	Next

	UpdateWorld	

	;**** Perform Ball-on-Ball Physics ****
	CheckCollisions (Blitz)
		
	RenderWorld 
RenderWorld
	Text 0,5,"Press SPACE to throw another ball"
	Text 0,15,"Press ESC to Exit" + TrisRendered()

	Flip 1
Wend

End
Function Flipper() 
While ScanLine()<>0: Wend 
Flip False 
End Function 
;**********************************

Function CheckCollisions (Ball.Ball_Object)

	For C.Ball_Object = Each Ball_Object
				
		If EntityCollided(c\Entity, BALLS)
					
			For i = 1 To CountCollisions(c\Entity)

				Ent=CollisionEntity (c\Entity, i)

				;Get the normal of the surface which the entity collided with. 
				Nx# = CollisionNX(c\Entity, i) 
				Ny# = CollisionNY(c\Entity, i) 
				Nz# = CollisionNZ(c\Entity, i) 
			
				;Compute the dot product of the entity's motion vector And the normal of the surface collided with. 				
				VdotN# = (c\dx# * Nx# + c\dy# * Ny# + c\dz# * Nz#)
									
				;Calculate the normal force. 
				NFx# = -2.0 * Nx# * VdotN# 
				NFy# = -2.0 * Ny# * VdotN# 
				NFz# = -2.0 * Nz# * VdotN# 
										
				;Add the normal force to the direction vector. 
				c\dx# = c\dx# + NFx#/2
				c\dy# = c\dy# + NFy#/2	;Force /2 assumes balls equal in mass
				c\dz# = c\dz# + NFz#/2
								
				For Hit.Ball_Object = Each Ball_Object

					If Hit\Entity = Ent Then
						
						;Add negative force to collision entity vector		
						Hit\dx# = Hit\dx# - NFx#/2
						Hit\dy# = Hit\dy# - NFy#/2	;Force /2 assumes balls equal in mass
						Hit\dz# = Hit\dz# - NFz#/2		
											
					End If
																									
				Next

			Next
												
		EndIf
		
	Next
							
End Function

;**********************************

Function InitBallVars(Ball.Ball_Object)

	;Create Ball
	Ball\Entity=CreateSphere()
	ScaleEntity Ball\Entity, 1,1,1
	EntityType Ball\Entity, BALLS

	;txt=LoadTexture("blitzlogo.bmp")
	;EntityTexture Ball\Entity, txt
	
	;Create Pivot
	Ball\Pivot=CreatePivot()
	
	;*** Variables for Ball ***	
	Ball\x#=0
	Ball\y#=20	;set ball 20 units above floor
	Ball\z#=0
	Ball\dx#=Rand(5)
	Ball\dy#=0	;at rest
	Ball\dz#=Rand(5)
	Ball\yMin#=1
	Ball\yMax#=9999
	Ball\xMin#=-32
	Ball\xMax#=32
	Ball\zMin#=-32
	Ball\zMax#=32
	Ball\elasticity#=.75	;value from >0 to <1 (1=pure elastic collision)
	Ball\friction#=.01
	;**************************

End Function

;**********************************

Function BallPhysics (Ball.Ball_Object)

	If ball\y   =< Ball\yMin# Then
		Ball\y  =  Ball\yMin#
		Ball\dy = -Ball\dy
		Ball\dy =  Ball\dy * Ball\elasticity
	End If

	If Ball\x   =< Ball\xMin# Then
		Ball\x  =  Ball\xMin#
		Ball\dx = -Ball\dx
		Ball\dx =  Ball\dx * Ball\elasticity
		UpdatePivot (Ball)
	End If

	If  Ball\x >=  Ball\xMax# Then
		Ball\x  =  Ball\xMax#
		Ball\dx = -Ball\dx
		Ball\dx =  Ball\dx * Ball\elasticity
		UpdatePivot (Ball)
	End If
	
	If Ball\z   =< Ball\zMin# Then
		Ball\z  =  Ball\zMin#
		Ball\dz = -Ball\dz
		Ball\dz =  Ball\dz * Ball\elasticity
		UpdatePivot (Ball)
	End If

	If Ball\z  >=  Ball\zMax# Then
		Ball\z  =  Ball\zMax#
		Ball\dz = -Ball\dz
		Ball\dz =  Ball\dz * Ball\elasticity
		UpdatePivot (Ball)
	End If
	
	If Ball\y  >= Ball\yMin# Then
		Ball\dy = Ball\dy + GRAVITY
	End If	

	Ball\x = Ball\x + Ball\dx
	Ball\z = Ball\z + Ball\dz
	Ball\y  = Ball\y + Ball\dy

	Ball\dx = Ball\dx - (Ball\dx*Ball\friction)
	Ball\dz = Ball\dz - (Ball\dz*Ball\friction)

	;Move ball to new coords
	PositionEntity  Ball\Entity, Ball\x, Ball\y, Ball\z, 0

	;Rotate mesh to give appearance of rolling	
	TurnEntity Ball\Entity, Ball\dz*50,0,-Ball\Dx*50, 1
			
End Function

Function UpdatePivot (Ball.Ball_Object)

	;Move pivot to coords + deltas
	PositionEntity Ball\Pivot, Ball\x+Ball\dx, Ball\y+Ball\dy, Ball\z+Ball\dz
	
	PointEntity Ball\Entity, Ball\Pivot, 0		

End Function


[edit] Actually, I've just seen some 'sticky' collisions happening when 2 balls hit, so there must be a bugette somewhere. Like I said, not my code so I ain't fixing it. :)

I think if you translate the 2 balls involved in the collisions by a small fraction of the collision normals then the stickyness will be eliminated.

a) Move the balls apart along the collisionnormal until they are separated(as Stevie says) and apply the collisionresponse

or

b) Only allow collisionsresponse to happen when the balls are intersecting and moving towards each other(non separating) and ignore the collisionresponse if they are moving away from each other(separating)