Help > Collision problems with rotating objects

Blitz3D Forums/Blitz3D Programming/Help > Collision problems with rotating objects

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

Here is the nasty workaround mentioned above. Maybe someone can think of better methods to get around some of the limitations, it all gets a bit messy though.

; colliding with rotating block

; Following rotation of block stops ball as expected
; Going against rotation allows ball to pass through *
; * How do I prevent this and get the block to 'push' the ball?
; (Syntax Error)

; One possible approach.  Not very satisfactory.  Lots of issues.
; ( must separate blocks from other blocks and so on )
; Credit Shawn Swift for the ball bouncing/rotating code.

; Note: Transfer of block momentum is incorrectly implemented, but thats the
; least of your worries when using this method so hey... :-) 
; (D. Woodgate)

; display
Graphics3D 640,480,0,2
HidePointer
SeedRnd MilliSecs()

; entitys
campiv=CreatePivot()
camera=CreateCamera(campiv)
PositionEntity camera,0,50,0
PointEntity camera,campiv

light=CreateLight(1,campiv)
TurnEntity light,60,-60,0

texture=CreateTexture(128,128)
SetBuffer TextureBuffer(texture)
Color 128,128,128
Rect 0,0,128,128
Color 128,0,0
Rect 0,0,64,64 Rect 64,64,64,64
SetBuffer BackBuffer()
ScaleTexture texture,0.25,0.25

Color 255,255,255
plane=CreatePlane()
EntityColor plane,50,100,50
PositionEntity plane,0,-1,0

; collisions
Const cBALL=1 , cBLOCK=2, cWALL=3

Collisions cBALL,cBLOCK,2,2
Collisions cBALL,cWALL,2,2

Dim block(50)
Dim rotspd#(50)


; create arena
Nwall=CreateCube()
EntityColor Nwall,200,0,0
EntityType Nwall,cWALL
FitMesh Nwall,-35,-1,-0.5, 70,2,0.5
PositionEntity nwall,0,0,35
Swall=CopyEntity(Nwall)
PositionEntity Swall,0,0,-35
Ewall=CopyEntity(Nwall)
RotateEntity EWall,0,90,0
PositionEntity Ewall,-35,0,0
Wwall=CopyEntity(Nwall)
RotateEntity Wwall,0,-90,0
PositionEntity Wwall,35,0,0

; The blocks
accel#=1
For x=-24 To 24 Step 15
	For z=-24 To 24 Step 15
		numblock=numblock+1
		block(numblock)=CreateCube()
		PositionEntity block(numblock),x,0,z
		EntityColor block(numblock),200,100,100
		ScaleMesh block(numblock),0.4,1,3.5
		EntityType block(numblock),cBLOCK
		rotspd(numblock)=Rnd(2,15)
		If Rand(1,2)=1 Then rotspd(numblock)=-rotspd(numblock)
	Next	
Next

; the ball
ballrad# = 1.5
maxballspd# = 3
Drag#=0.98

ball=CreatePivot() ; Collision proxy
PositionEntity ball,0,0,0
EntityRadius ball,ballrad
EntityType ball,cBALL

orb=CreateSphere(12) ; The visible ball
ScaleEntity orb,ballrad,ballrad,ballrad
EntityTexture orb,texture

; init
MoveMouse 320,240 : FlushMouse
vx#=0 : vz#=0

; MAINLOOP
Repeat
	; mouse control
	MXSpd# = Float(MouseXSpeed())/255
	MySpd# = Float(MouseYSpeed())/255
	MoveMouse 320,240
	
	; parent ball collision proxy to block collision candidate
	; rotate all blocks	and calculate ball translation due to block rotation
	Gosub updateblocks
	
	; Reset block state
	UpdateWorld

	; unparent ball from block
	EntityParent ball,0
	
	; update ball motion with drag and user input.  Limit ball speed
	Gosub updateball

	; translate ball taking into account relative movement
	; get ball position before applying collisions.
	Gosub moveball

	; Second update world to capture ball collisions
	UpdateWorld 
	 
	Gosub checkcollisions
	
	; rotate ball according to motion vector
	Gosub rotateball

	Gosub movecamera

	RenderWorld

	Text 320,450,"MOVE BALL WITH MOUSE",1
	Flip

Until KeyHit(1)

End

.checkcollisions
	If CountCollisions(ball)>0 Then
	; Swifty bounce code:
	; Get the normal of the surface which the entity collided with. 
		Nx# = CollisionNX(ball, 1)
		Nz# = CollisionNZ(ball, 1) 
	; Compute the dot product of the entity's motion vector and the normal of the surface collided with. 
	; Adding Rx and Rz is my lame attempt to factor in block rotational momentum
		VdotN# = (Vx# + Rx#) * Nx# + (Vz# + Rz# ) * Nz# 
	; Calculate the normal force. 
		NFx# = -2.0 * Nx# * VdotN#
		NFz# = -2.0 * Nz# * VdotN#
	; Add the normal force to the direction vector. 
		Vx# = Vx# + NFx# 
		Vz# = Vz# + NFz# 
	EndIf
Return

.updateball
	; apply drag
	Vx# = Vx# * Drag# : Vz# = Vz# * Drag#
	
	; transform mouse input so we move the ball with respect to the camera pivot	
	TFormVector MxSpd,0,-Myspd,campiv,0
	Vx# = Vx# + TFormedX()
	Vz# = Vz# + TFormedZ()
	
	; limit ball speed, things break down if its going too fast
	ballspd# = Sqr(Vx# * Vx# + Vz# * Vz#)
	If ballspd# > maxballspd# Then
		Vx# = Vx# / ballspd# * maxballspd#
		Vz# = Vz# / ballspd# * maxballspd#
	; keep the ball rolling if it stops
	ElseIf ballspd<0.01 Then
		vx#=Rnd(-1,1) vz#=Rnd(-1,1)
	EndIf
Return

.updateblocks
	; get global ball position
	Gx#=EntityX(ball,1)
	Gz#=EntityZ(ball,1)
	
	accel# = 1+(KeyDown(52)-KeyDown(51))*0.01
	
	; parent ball to nearest rotating block.  This means of course that blocks
	; cannot be too close together or too close to any other colliding object.

	For i=1 To numblock
		If EntityDistance(ball,block(i))<6.5 Then 
			EntityParent ball,block(i)
			EntityColor block(i),200,200,100
		Else
			EntityColor block(i),200,100,100
		EndIf
		rotspd(i) = rotspd(i) * accel#
		TurnEntity block(i),0,rotspd(i),0		
	Next
	
	; the distance the ball has moved due to block rotation
	; if there is no collision candidate then this will be zero
	Rx# = Gx# - EntityX(ball,1)
	Rz# = Gz# - EntityZ(ball,1)
Return

.moveball
	; correct for relative movement
	TranslateEntity ball,Rx,0,Rz,True

	; get ball position before movement
	OldX# = EntityX(ball,1) 
	OldZ# = EntityZ(ball,1)

	; move ball globally 
	TranslateEntity ball,Vx#,0,Vz#,True
Return

.rotateball
	; get distance ball has moved
	NewX# = EntityX(ball,1) 
	NewZ# = EntityZ(ball,1) 
	Mx# = (NewX# - OldX#) 
	Mz# = (NewZ# - OldZ#)
	 
	; Swifty rotation code:
	; 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 difference.
	XAngleAdjust# = (Mx# / ballrad) * (180.0/Pi) 
	ZAngleAdjust# = (Mz# / ballrad) * (180.0/Pi)
	PositionEntity orb,EntityX(ball,1),EntityY(ball,1),EntityZ(ball,1),True
	TurnEntity orb,ZAngleAdjust#,0,-XAngleAdjust#,True 
Return

.movecamera
	; move the camera
	TurnEntity campiv,0,KeyDown(203)-KeyDown(205),0
	TurnEntity camera,KeyDown(200)-KeyDown(208),0,0
	MoveEntity camera,KeyDown(32)-KeyDown(46),KeyDown(31)-KeyDown(45),KeyDown(30)-KeyDown(44)	
Return


[Edit] Changed the bounce routine a bit. Seems to give better results as does using sliding collisions, though I am not quite sure why (I suspect it has to do with the fact I am doing two translations ) I am now wondering if more flexibility might be gained by using multiple collision proxies, though I am not sure yet how that would pan out in practice.