Sphere Physics... Again

Blitz3D Forums/Blitz3D Beginners Area/Sphere Physics... Again

Buggy(Posted 2006) [#1]
Hi! It's me again with the by-now-stale topic of sphere physics, which I had previously thought I had all figured out. My problem is this:

I cannot figure out how much to turn my sphere so that it looks like it's turning the right amount. Simple? not for me!

I can get it to turn fine under most circumstances, where since it is usually only moving on the Z axis (and of course Y, but this is fairly irrelevant), I just use the line:
TurnEntity sphere, zInertia#*30, 0, 0

This is usually fine except when the sphere is on a slope. It falls down through sliding collisions, but since the player isn't necessarily moving the sphere on their own, the zInertia# is 0 and thus the ball doesn't turn as it "folls" down a hill. This would be easily fixable if it were not for the fact that the player isn't necessarily completely perpendicular to the face of the slope, so the computer must somehow determine both the speed of the ball's descent and the direction of the slope versus the direction of the ball, and use this to determine how much the ball should turn in each direction. Normals, perhaps?

Of much less importance is another question:

How can I make it so that the sphere can only travel up a certain distance on a steep slope before it travels down again? My gravity doesn't seem to be strong enough (the ball can easily scale anything less than a 90 degree angle), but if I make it any stronger all sorts of problems arise, and the gravity seems realistic when the ball goes off jumps.


Stevie G(Posted 2006) [#2]
Without seeing some working code it's pretty difficult to suggest something which will fit in with how you're doing things. Can you post a cut down version with no external media?


(tu) sinu(Posted 2006) [#3]
i found this demo lying around, it might help you
Its vey messy and used source from someones ball physics.


; JP ball turn by rob
;
;ball is parented to ballpos, which is a pivot.
;moving mouse moves the ball. Vx and vY are the balls speed. used to calculate turn too.
Include "keys.bb"

SeedRnd MilliSecs()
Global ball,ballpos
Global Vx#=0,vY#=0.000001,vZ#=0
;display crap
HidePointer
Graphics3D 1024,768,0,2
SetBuffer BackBuffer()


Const TYPE_BALL = 1
Const TYPE_GROUND = 2

Collisions TYPE_BALL,TYPE_GROUND,2,2


camera=CreateCamera()
CameraRange camera,1,20000
MoveEntity camera,0,600,-600
TurnEntity camera,45,0,0
light=CreateLight()
MoveEntity light,1000,1000,1000
p=CreatePlane();:EntityAlpha p,0.9
EntityType p,TYPE_GROUND
t=CreateTexture(16,16):SetBuffer TextureBuffer(t)
ClsColor 255,0,0
Cls
Color 255,255,255
Rect 0,0,8,8,1
Rect 8,8,8,8,1
ScaleTexture t,110,110;.2,.2
EntityTexture p,t
FreeTexture t
SetBuffer BackBuffer()

;EntityColor p,0,0,255

Dim cube(3)
Dim bk_cube(3)

cube(0) = CreateCube()
NameEntity cube(0),"onebox"
ScaleEntity cube(0),280,60,80
PositionEntity cube(0),-30,0,0
EntityType cube(0),TYPE_GROUND
EntityColor cube(0),Rand(0,255),Rand(0,255),Rand(0,255):EntityAlpha cube(0),0.9
RotateEntity cube(0),0,0,35
Delay Rnd(0,1000)
cube(1) = CreateSphere()
ScaleEntity cube(1),80,80,80
PositionEntity cube(1),-480,40,180
EntityType cube(1),TYPE_GROUND
EntityColor cube(1),Rand(0,255),Rand(0,255),Rand(0,255):EntityAlpha cube(1),0.9
Delay Rnd(0,1000)
cube(2) = CreateCube()
ScaleEntity cube(2),80,80,80
PositionEntity cube(2),-880,40,380
EntityType cube(2),TYPE_GROUND
EntityColor cube(2),Rand(0,255),Rand(0,255),Rand(0,255):EntityAlpha cube(2),0.9
Delay Rnd(0,1000)
cube(3) = CreateCube()
ScaleEntity cube(3),80,80,80
PositionEntity cube(3),580,40,580
EntityType cube(3),TYPE_GROUND
EntityColor cube(3),Rand(0,255),Rand(0,255),Rand(0,255):EntityAlpha cube(3),0.9


;set up your balls etc. each ball has a pivot for movement.
ballpos=CreatePivot()
ball=CreateSphere(6,ballpos)
ScaleEntity ball,20,20,20
PositionEntity ballpos,0,120,0
EntityType ballpos,TYPE_BALL
EntityRadius ballpos,20
;EntityAlpha ball,0.7
t=CreateTexture(16,16):SetBuffer TextureBuffer(t)
ClsColor 0,0,0
Cls
Color 255,255,255
Rect 0,0,8,8,1
Rect 8,8,8,8,1
ScaleTexture t,.2,.2
EntityTexture ball,t
FreeTexture t
SetBuffer BackBuffer()
;mainloop

Global Velocity#
Global Direction_X#,Direction_Y#,Direction_Z#

Global Air_Friction_Force#


Const GROUND_FRICTION_CONSTANT# = 0.0028 , AIR_FRICTION_CONSTANT# = 0.0007, GRAVITY# = 0.08;97





Global Box_Movement#,adder# = 0.8

timer60 = CreateTimer(60)
While Not KeyHit(1)
WaitTimer timer60


Box_Movement = Box_Movement + adder;0.5
If Box_Movement >=200 adder = adder*-1
If Box_Movement <=-200 adder = adder*-1

If use
For i = 0 To 3

If bk_cube(i)>0 FreeEntity bk_cube(i)

MoveEntity cube(i),adder,0,0;Box_Movement,0,0

bk_cube(i) = CreateCube()
NameEntity bk_cube(i),"onebox"
ScaleEntity bk_cube(i),80,80,80
PositionEntity bk_cube(i),EntityX(cube(i)),EntityY(cube(i)),EntityZ(cube(i))
EntityType bk_cube(i),TYPE_GROUND
EntityAlpha bk_cube(i),0.0

Next
EndIf

If KeyHit(key_u) DebugLog Vy:Vy# = Vy# + 6:DebugLog Vy

If KeyDown(key_i) vx = vx + 20.01

	Entity_Hit = EntityCollided(ballpos, TYPE_GROUND)


Velocity# = Sqr(Vx#^2 + Vy#^2 + Vz#^2)

		; If the entity is moving, adjust it's velocity:
		If Velocity# > 0 ;a


			; Calculate the direction vector.
			; The direction vector has a length of 1.
			Direction_X# = Vx# / Velocity#
			Direction_Y# = Vy# / Velocity#
			Direction_Z# = Vz# / Velocity#


			; Compute air friction.
			; Air friction is dependent on the speed of the entity, and will prevent it from accelerting forever.
			Air_Friction_Force# = AIR_FRICTION_CONSTANT# * Velocity#^2.0	
			Velocity# = Velocity# - (Air_Friction_Force#); * Time_Delta_Sec#)

			; If the entity collided with the level, apply ground friction.
			If Entity_Hit > 0 ;b

				; Compute ground friction.  Ground friction is not dependent on the speed of the entity.
				Velocity# = Velocity# - (GROUND_FRICTION_CONSTANT#); * Time_Delta_Sec#)

			EndIf;b

			; Make sure the entity's velocity doesn't go below 0.
			; It is impossible to have a negative velocity in physics and "bad things" happen if you try to.
			If (Velocity# < 0) Then Velocity# = 0
			
			
			; Convert the entity's velocity and direction back into a motion vector.
			Vx# = Direction_X# * Velocity#
			Vy# = Direction_Y# * Velocity#
			Vz# = Direction_Z# * Velocity#
			
						
			; If the entity collided with the level, make it bounce.
			If Entity_Hit > 0 ;c

				; Calculate bounce:

	    			; Get the normal of the surface which the entity collided with.    
					Nx# = CollisionNX(ballpos, 1)
					Ny# = CollisionNY(ballpos, 1)
					Nz# = CollisionNZ(ballpos, 1)
		
		;;If Nx<>0 Or Nz<>0 DebugLog EntityName(Entity_hit)
				; Compute the dot product of the entity's motion vector and the normal of the surface collided with.
					VdotN# = Vx#*Nx# + Vy#*Ny# + Vz#*Nz#
							
				; Calculate the normal force.
					NFx# = -1.8 * Nx# * VdotN#
					NFy# = -1.725 * Ny# * VdotN#
					NFz# = -1.8 * Nz# * VdotN#

				; Add the normal force to the direction vector.
					Vx# = Vx# + NFx#
					Vy# = Vy# + NFy#
					Vz# = Vz# + NFz#
	
				; Do not allow the entity to move vertically.
				;If Vy# <> 0 Vy# = Abs(Vy#);0
;If Vy#>-0.051 Vy# = 0
;;Vy# = Abs(Vy#)*.8
			;;If Vy# < 0.0 Vy# = 0.0;Abs(Vy#);0
	
			EndIf;c


	; Apply gravity:
					Vy# = Vy# - (GRAVITY#);-Yspin#); * Time_Delta_Sec#)
;Yspin# = -0.05
					;If Vy# > 0 Vy# = Abs(Vy#);0


	; Move and rotate the entity:

		; We rotate the entity by the actual distance moved and not by the velocity because if we rotate according
		; to the velocity then the entity will roll when it's up against a wall and not moving.

		OldX# = NewX#
		OldZ# = NewZ#
		OldY# = NewY#
		TFormVector Vx,Vy,Vz,0,ballpos
		;MoveEntity ballpos,TFormedX(),TFormedY(),TFormedZ()
		TranslateEntity ballpos, Vx,Vy,Vz,1;Vx#*Time_Delta_Sec#, Vy#*Time_Delta_Sec#, Vz#*Time_Delta_Sec#, True

		NewX# = EntityX#(ballpos, True)
		NewZ# = EntityZ#(ballpos, True)
		NewY# = EntityY#(ballpos, True)

		If NewX = OldX Vx = 0
		If NewZ = OldZ Vz = 0
		If NewY = Oldy Vy = 0

		Mx# = (NewX# - OldX#)
		Mz# = (NewZ# - OldZ#)		

		; Rotate the entity the right amount for it's radius and the distance it has moved along the X and Z axis.
		; This is kinda a hack and only designed for rolling on planes but you won't notice the diffrence.
		XAngleAdjust# = (Mx# / 20.0) * (180.0/Pi) 
		ZAngleAdjust# = (Mz# / 20.0) * (180.0/Pi)
	    TurnEntity ball,ZAngleAdjust#,0,-XAngleAdjust#,True


EndIf
		;DebugLog "vy "+Vy
		
	updateball()
	
	UpdateWorld
	
;Text 0,10,"ballposX "+EntityX#(ballpos)	
 ;Text 0,20,"ballposy "+EntityY#(ballpos)	
  ;Text 0,30,"ballposz "+EntityZ#(ballpos)	
	
	RenderWorld
	;Color 0,0,255
	Text 10,10,"speeds vx "+Vx+" vy "+vy+" vz "+vz+" entity hit "+Entity_hit
	Text 10,20,EntityX(ballpos)+" "+EntityY(ballpos)+" "+EntityZ(ballpos)

	;If KeyHit(key_t) DebugLog (Vx*10000)+" e "+Abs(vz)
	 
	Flip 0
Wend

End

Function updateball()
	
	accel# = 0.0251;1;055;10.1;055
	MaxSp# = 16.0
If KeyDown(key_d)
	
	Vx = Vx + accel
	
ElseIf KeyDown(key_a)
	
	Vx = Vx - accel
	
	EndIf
	
If KeyDown(key_w)
	
	vZ = vZ + accel
	
ElseIf KeyDown(key_s)
	
	vZ = vZ - accel
	
	EndIf
	
	
	If Vx<-MaxSp Vx = -MaxSp
	If Vx>MaxSp Vx = MaxSp
	If vZ<-MaxSp vZ = -MaxSp
	If vZ>MaxSp vZ = MaxSp
	
	;If Abs(Vx) <= 0.1 Vx = 0.0
	;If Abs(vZ) <= 0.1 vZ = 0.0	
	If Abs(mx) <= accel#*.9 mx = 0.0
	If Abs(mz) <= accel#*.9 mz = 0.0
	;control the ball o death
	If MouseHit(1)
	Vx#=Rand(-5,5);MouseXSpeed()*0.5
	;vY#=Rand(5,1);-MouseYSpeed()*0.5
	vZ#=Rand(-5,5)
	EndIf
	;MoveMouse GraphicsWidth()/2,GraphicsHeight()/2
	;TranslateEntity ballpos,Vx,vY,vZ
	
	;reset turnentity
	;RotateEntity ball,0,0,0
	
	;turn it how you want.
	;TurnEntity ball,vZ*2,0,-Vx*2
	
	;reset the coordinate system for the ball! (secret frosties recipe)
	;RotateMesh ball,EntityPitch(ball),EntityYaw(ball),EntityRoll(ball)
	
	;Vx = Vx*.995
	;vZ = vZ*.995
	;vY = vY*.995


If MouseHit(2) PositionEntity ballpos,0,20,0	
	;If vY>0 vY = vY -.2
	
End Function





Buggy(Posted 2006) [#4]
Sorry about the delay in posting. Just at a quick glance it seems that even that above code is over my head. Jeez.

Anyway, here's my code with some extra code added from the code reference so I could easily make hills:

Graphics3D 800, 600, 16;starts it up

SeedRnd MilliSecs();makes random slightly more so



Const upKey = 200, rightKey = 203, leftKey = 205, downKey = 208, enter = 28, aKey = 30, sKey = 31, dKey = 32, wKey = 17

Global xInertia#, yInertia#, zInertia#
Global zInertiaLimit# = 2, bounce# = .5

Global pivot = CreatePivot()
PositionEntity pivot, 0, 0, 10
EntityType pivot, 1

Global sphere = CreateSphere(16, pivot)
texture = LoadTexture("health.PNG")
EntityTexture sphere, texture
Global lastY#, thisY#, lastX#, thisX#, lastZ#, thisZ#
Global lastCollide = 1, thisCollide = 1

light = CreateLight();let there be light
camera = CreateCamera(pivot);let there be cameras, too.
PositionEntity camera, 0, 7, -15


Global terra_size=16 ; initial size of terrain, and no. of grids segments, along each side
x_scale=10 ; x scale of terrain
y_scale=10 ; y scale of terrain
z_scale=10 ; z scale of terrain
marker_x=terra_size/2 ; initial x position of marker
marker_z=terra_size/2 ; initial z position of marker

; Create terrain
Global terra = CreateTerrain(terra_size)
PositionEntity terra, -(terra_size/2), -1, -(terra_size/2)
ScaleEntity terra, x_scale, y_scale, z_scale
terraTexture = LoadTexture("grass.PNG")
EntityTexture terra, terraTexture
EntityType terra, 2


; Create marker
marker=CreateSphere()
ScaleEntity marker,1,1,1
EntityColor marker,255,0,0





Collisions 1, 2, 2, 2

While Not KeyDown(1)

	checkKeys()
	moveSphere()
	
	PointEntity camera, pivot
	
	

	; Change marker position values depending on cursor key pressed
If KeyHit(aKey)=True Then marker_x=marker_x+1
If KeyHit(dKey)=True Then marker_x=marker_x-1
If KeyHit(wKey)=True Then marker_z=marker_z-1
If KeyHit(sKey)=True Then marker_z=marker_z+1

; Get terrain height at marker position
marker_y#=TerrainHeight(terra,marker_x,marker_z)

; If X pressed then increase marker_y value and modify terrain
If KeyDown(45)=True
If marker_y#<1 Then marker_y#=marker_y#+0.005
ModifyTerrain terra,marker_x,marker_z,marker_y#
EndIf

; If Z pressed then decrease marker_y value and modify terrain
If KeyDown(44)=True
If marker_y#>0 Then marker_y#=marker_y#-0.005
ModifyTerrain terra,marker_x,marker_z,marker_y#
EndIf

; Position marker, taking into account x, y and z scales of terrain
PositionEntity marker,marker_x*x_scale,marker_y#*y_scale,marker_z*z_scale


	UpdateWorld

	RenderWorld
	
	
	
	Text 50,0,"Use cursor keys to move marker over the terrain"
	Text 50,20,"Press A or X to alter height of terrain at marker's position"
	Text 50,40,"Terrain Height: "+marker_y#
	Text 50, 60, "thisZ#=" + thisZ#
	Text 50, 80, "lastZ#=" + lastZ#

	
	Flip

	Delay 15
	
Wend

Function checkKeys()

	If KeyHit(enter)
	
		MoveEntity pivot, 0, 40, 0
			
		For x = 1 To terra_size
		
			
		
			For y = 1 To terra_size
			
				ModifyTerrain terra, x, y, Rnd(-1)
				
				
				
			Next
			
		Next
		

		
	EndIf

	If KeyDown(upKey)
	
		If zInertia# < zInertiaLimit#
		
			zInertia# = zInertia# + (zInertiaLimit# - zInertia#)/50
			
		EndIf
		
	ElseIf KeyDown(downKey)
	
		If zInertia# > -zInertiaLimit#
		
			zInertia# = zInertia# - (zInertiaLimit - zInertia#)/50
			
		EndIf
	
	ElseIf zInertia# > .1 Or zInertia# < -.1;if the player's not making it move, slow it down
	
		zInertia# = zInertia# + Sgn(-zInertia#)*((zInertiaLimit# - zInertia#)/50)
		
	Else
	
		zInertia# = 0
		
	EndIf
	
	If KeyDown(rightKey)
	
		TurnEntity pivot, 0, 5, 0
		
	EndIf
	
	If KeyDown(leftKey)
	
		TurnEntity pivot, 0, -5, 0
		
	EndIf

End Function

Function moveSphere()

	lastY# = thisY#
	thisY# = EntityY#(pivot, True)
	lastX# = thisX#
	thisX# = EntityX#(pivot, True)
	lastZ# = thisZ#
	thisZ# = EntityX#(pivot, True)
	
	yInertia# = thisY# - lastY# -.08;calculates Y inertia
	
	lastCollide = thisCollide
	
	If CountCollisions(pivot) = 0
	
		thisCollide = 0
		
	Else
		
		For collisionNumber = 1 To CountCollisions(pivot)
	
			entity = CollisionEntity(pivot, collisionNumber)
		
			collisionType = GetEntityType(entity)
		
		
			thisCollide = collisionType
		
		

		Next
	
	EndIf
	
	If thisCollide <> 0 And lastCollide = 0
	
		yInertia# = -yInertia#*bounce#
		
		If yInertia# < .2
		
			yInertia# = 0
			
		EndIf
		
	EndIf
	
	
	
	MoveEntity pivot, 0, yInertia#, zInertia#;moves the ball
	

End Function

End