EntityBox and ScaleEntity

Blitz3D Forums/Blitz3D Beginners Area/EntityBox and ScaleEntity

Apologies in advance - but I am finally embarking on my first ever attempt at a 3D game, and have fallen at virtually the first hurdle!

I am creating an early mockup of what will eventually be a train game and am using cubes scaled into rectangles as crude trains so that I can code 'train selection with mouse click' via CameraPick.

I have searched the forums and code archives relating to EntityBox and ScaleEntity and am a little confused as to whether the two commands can be used successfully with a cube which has been stretched into a rectangle? I am able to create and scale uniform cubes and their EntityBoxes without problems (after which CameraPick works accurately every time), but if I create a rectangle using:

train=CreateCube()
ScaleEntity train,1,2,1
EntityBox train,-1,-1,-1,2,4,2
EntityPickMode train,3


CameraPick no longer works properly. Am I being a muppet and doing something stupid?

The online manual entry for EntityBox has additional information which says "the space the entity resides in should be scaled uniformly. EntityBox will not work as you might expect in non-uniform scaled space". Does this mean I cannot use EntityBox for rectangular collision detection, and if so how can I achieve this?

Any help would be gratefully appreciated!

The box you entered is not centered, it should be EntityBox train,-1,-2,-1,2,4,2

You're quite right b32! However, I have corrected the code and made a modification to the cube definitions (I was extending the Y axis and then rotating them so they lay on the floor - don't ask me why!) and whilst CameraPick now works ok, Collisions do not - unless I do not scale the cubes, in which case they work perfectly.

My code is as follows:

Graphics3D 1024,768,32,2
SetBuffer BackBuffer()
Color 255,255,255
SeedRnd MilliSecs()

Local trainsel=-1
Local last_chkpt=MilliSecs()
Local framecount
Local fps
Local newtrain=0
Local newtype=0
;Local mouseimage=LoadImage("mouse.bmp")
Local type_camera=1
Local type_ground=2
Local type_train=3

Dim train(2)

;MaskImage mouseimage,192,192,255
Collisions type_camera,type_ground,2,1 
Collisions type_train,type_train,3,1 

; setup entities
light=CreateLight()
camera=CreateCamera()
PositionEntity camera, 0,6,-16
RotateEntity camera,15.0,0,0
EntityType camera,type_camera
ground=CreatePlane()
EntityColor ground, 34,53,123
EntityType ground,type_ground

For k=0 To 2
	train(k)=CreateCube()
	ScaleEntity train(k),1,1,4
	EntityBox train(k), -1,-1,-4,2,2,8
	PositionEntity train(k),k*5,1,Rand(30)
	EntityPickMode train(k),3
	EntityType train(k),type_train
Next

While Not KeyDown( 1 )
	; check for selection
	If MouseHit(1) Then
		For k=0 To 2
			EntityColor train(k),255,255,255
		Next
		trainsel=-1
		newtype=0
		newtrain=CameraPick(camera,MouseX(),MouseY())
		If newtrain<>0 Then
			newtype=GetEntityType(newtrain)
			If newtype=type_train Then
				For k=0 To 2
					If train(k)=newtrain Then
						trainsel=k
						Exit
					EndIf
				Next
				EntityColor PickedEntity(),255,255,0
			EndIf
		EndIf
	EndIf
	; check for move/rotate
	If KeyDown(42) Then ; LEFTSHIFT
		If KeyDown(200) Then
			MoveEntity camera,0,0,0.1
		ElseIf KeyDown(208) Then
			MoveEntity camera,0,0,-0.1
		EndIf
	ElseIf KeyDown(29) Then ; LEFT CTRL
		If KeyDown(200) Then
			MoveEntity camera,0,0.1,0
		ElseIf KeyDown(208) Then
			MoveEntity camera,0,-0.1,0
		EndIf
	ElseIf trainsel>-1 Then
		If KeyDown(200) Then
			If KeyDown(203) Then
				TurnEntity train(trainsel),0,1.0,0
			EndIf
			If KeyDown(205) Then
				TurnEntity train(trainsel),0,-1.0,0
			EndIf
			MoveEntity train(trainsel),0,0,-0.1
		EndIf
	EndIf
	; update screen
	UpdateWorld
	RenderWorld
	framecount=framecount+1
	If MilliSecs()-last_chkpt>=1000
		last_chkpt=MilliSecs()
		fps=framecount
		framecount=0
	EndIf
	Text 0,0,"FPS: "+fps
;	DrawImage mouseimage,MouseX(),MouseY()
	Flip
Wend

For k=0 To 2
	FreeEntity train(k)
Next
FreeImage mouseimage
End


I must be doing something wrong somewhere - because no matter what I try, a "train" will pass almost 1/3 of the way through another before Blitz3D registers a collision. Can anyone tell me why my code does not work? (Apologies for the camera - another eve or two spent in the archives til I learn how to improve this methinks...) :o)

I think the reason for that is that collisions are allways sphere to something. So, the 'emitting' entity is still represented by a sphere, and the 'receiving' entities are boxes. I changed the code a bit to make the EntityRadius visible:
Graphics3D 1024,768,32,2
SetBuffer BackBuffer()
Color 255,255,255
SeedRnd MilliSecs()

Local trainsel=-1
Local last_chkpt=MilliSecs()
Local framecount
Local fps
Local newtrain=0
Local newtype=0
;Local mouseimage=LoadImage("mouse.bmp")
Local type_camera=1
Local type_ground=2
Local type_train=3

Dim train(2)

;MaskImage mouseimage,192,192,255
Collisions type_camera,type_ground,1,2
Collisions type_train,type_train,1,2

; setup entities
light=CreateLight()
camera=CreateCamera()
PositionEntity camera, 0,6,-16
RotateEntity camera,15.0,0,0
EntityType camera,type_camera
ground=CreatePlane()
EntityColor ground, 34,53,123
EntityType ground,type_ground

For k=0 To 2
	train(k)=CreateCube()
	ScaleEntity train(k),1,4,1
	EntityBox train(k), -1,-4,-1,2,8,2
	PreviewEntityRadius train(k),2,8
	PositionEntity train(k),k*5,1,Rand(30)
	EntityPickMode train(k),3
	EntityType train(k),type_train
Next

While Not KeyDown( 1 )
	; check for selection
	If MouseHit(1) Then
		For k=0 To 2
			EntityColor train(k),255,255,255
		Next
		trainsel=-1
		newtype=0
		newtrain=CameraPick(camera,MouseX(),MouseY())
		If newtrain<>0 Then
			newtype=GetEntityType(newtrain)
			If newtype=type_train Then
				For k=0 To 2
					If train(k)=newtrain Then
						trainsel=k
						Exit
					EndIf
				Next
				EntityColor PickedEntity(),255,255,0
			EndIf
		EndIf
	EndIf
	; check for move/rotate
	If KeyDown(42) Then ; LEFTSHIFT
		If KeyDown(200) Then
			MoveEntity camera,0,0,0.1
		ElseIf KeyDown(208) Then
			MoveEntity camera,0,0,-0.1
		EndIf
	ElseIf KeyDown(29) Then ; LEFT CTRL
		If KeyDown(200) Then
			MoveEntity camera,0,0.1,0
		ElseIf KeyDown(208) Then
			MoveEntity camera,0,-0.1,0
		EndIf
	ElseIf trainsel>-1 Then
		If KeyDown(200) Then
			If KeyDown(203) Then
				TurnEntity train(trainsel),0,1.0,0
			EndIf
			If KeyDown(205) Then
				TurnEntity train(trainsel),0,-1.0,0
			EndIf
			MoveEntity train(trainsel),0,0,-0.1
		EndIf
	EndIf
	; update screen
	UpdateWorld
	RenderWorld
	framecount=framecount+1
	If MilliSecs()-last_chkpt>=1000
		last_chkpt=MilliSecs()
		fps=framecount
		framecount=0
	EndIf
	Text 0,0,"FPS: "+fps
;	DrawImage mouseimage,MouseX(),MouseY()
	Flip
Wend

For k=0 To 2
	FreeEntity train(k)
Next
FreeImage mouseimage
End


Function PreviewEntityRadius(mesh, xz_radius#, y_radius#)
	EntityRadius mesh, xz_radius#, y_radius#
	ok = FindChild(mesh, "COLLISION_SPHERE")
	If ok <> 0 Then FreeEntity ok
	sphere = CreateSphere()
	ScaleEntity sphere, xz_radius#, y_radius#, xz_radius#
	EntityAlpha sphere, 0.1
	EntityParent sphere, mesh
	RotateEntity sphere, 0, 0, 0, 1
End Function


Thanks b32 - that function was very useful. I have now managed to get further progress by stretching the cubes along a different plane before specifying both EntityBox and EntityRadius:

Graphics3D 1024,768,32,2
SetBuffer BackBuffer()
Color 255,255,255

Const OBJ_LENSE=1
Const OBJ_FLOOR=2
Const OBJ_Train=3

Local BallastTexture=0
Local FPS
Local FPS_Chkpt=MilliSecs()
Local FPS_Count
Local ObjHandle=0
Local ObjType=0
Local TrainSel=-1

Dim Train(2)

Collisions OBJ_LENSE,OBJ_FLOOR,2,1
Collisions OBJ_Train,OBJ_Train,3,2

; setup entities
light=CreateLight()
Camera=CreateCamera()
PositionEntity Camera,0,6,-16
RotateEntity Camera,15.0,0,0
EntityType Camera,OBJ_LENSE
ground=CreatePlane()
EntityColor ground, 34,53,123
EntityType ground,OBJ_FLOOR

For k=0 To 2
	Train(k)=CreateCube()
	ScaleEntity Train(k),4,0.5,0.5
	EntityBox Train(k),-4,-0.5,-0.5,8,1,1
	EntityRadius Train(k),4,0.5
	PositionEntity Train(k),10,1,k*3
	EntityPickMode Train(k),3
	EntityType Train(k),OBJ_Train
Next

While Not KeyDown(1)
	; check for selection
	If MouseHit(1) Then
		For k=0 To 2
			EntityColor Train(k),255,255,255
		Next
		TrainSel=-1
		ObjType=0
		ObjHandle=CameraPick(Camera,MouseX(),MouseY())
		If ObjHandle<>0 Then
			ObjType=GetEntityType(ObjHandle)
			If ObjType=OBJ_Train Then
				For k=0 To 2
					If Train(k)=ObjHandle Then
						TrainSel=k
						Exit
					EndIf
				Next
				EntityColor PickedEntity(),255,255,0
			EndIf
		EndIf
	EndIf
	; check for move/rotate
	If KeyDown(42) Then ; LEFTSHIFT
		If KeyDown(200) Then
			MoveEntity Camera,0,0,0.1
		ElseIf KeyDown(208) Then
			MoveEntity Camera,0,0,-0.1
		ElseIf KeyDown(203) Then
			MoveEntity Camera,-0.1,0,0
		ElseIf KeyDown(205) Then
			MoveEntity Camera,0.1,0,0
		EndIf
	ElseIf KeyDown(29) Then ; LEFT CTRL
		If KeyDown(200) Then
			MoveEntity Camera,0,0.1,0
		ElseIf KeyDown(208) Then
			MoveEntity Camera,0,-0.1,0
		ElseIf KeyDown(203) Then
			MoveEntity Camera,-0.1,0,0
		ElseIf KeyDown(205) Then
			MoveEntity Camera,0.1,0,0
		EndIf
	ElseIf TrainSel>-1 Then
		If KeyDown(200) Then
			If KeyDown(203) Then
				TurnEntity Train(TrainSel),0,1.0,0
			EndIf
			If KeyDown(205) Then
				TurnEntity Train(TrainSel),0,-1.0,0
			EndIf
			MoveEntity Train(TrainSel),-0.1,0,0
		EndIf
	EndIf
	; update screen
	UpdateWorld
	RenderWorld
	FPS_Count=FPS_Count+1
	If MilliSecs()-FPS_Chkpt>=1000
		FPS_Chkpt=MilliSecs()
		FPS=FPS_Count
		FPS_Count=0
	EndIf
	Text 0,0,"FPS: "+FPS
	If TrainSel>-1 Then
		Text 0,12,"Entity Handle:"+Train(TrainSel)
		Text 0,24,"Entity Collision:"+EntityCollided(Train(TrainSel),OBJ_TRAIN)
	EndIf
	Flip
Wend

For k=0 To 2
	FreeEntity Train(k)
Next
End


However - EntityRadius seems to ignore the optional y_radius parameter and use the x_radius value for both instead. If you run the above code and move the middle "train" clear of the others, then move the others the same distance, both glide outwards as they slide round the middle train's y_radius boundary. This is no good to me since I need trains to run on adjacent tracks - which Blitz3D will currently prevent me doing since its collision detection will halt the trains if the tracks are close together.

I see in the forums that this y_radius behaviour was once thought to be a possible bug. I am now using v1.98 and can find nothing in the versions.txt to indicate this was fixed.

Does anyone know if this has ever been confirmed as a bug and if so whether a fix was issued?

I thought it is like this: blitz uses the X-radius for X/Z and the Y for Y, and you can't rotate the EntityRadius, but maybe I'm wrong.

Yeah I think your right - particularly as I have finally realised that it's the z-plane the entities are sliding in! Looks like I need to use the y-plane as the width and rotate it so that the z-plane becomes height - so I hope you're wrong about EntityRadius rotation (though I'm sure I've seen that mentioned somewhere in these forums too).

Seems odd that there's no z_radius parameter since entities are 3D and designed to be used in 3-planes. Mind you I notice that the y_radius was only introduced fairly recently, so really mustn't grumble...

Ok - I admit defeat. RotateEntity evidently does not rotate the EntityRadius - and no matter what I do, I cannot get the collisions to work the way I need them to because of the fact that the x/z axis share the same radius, and so the moving "train" will always have a horizontal circular collision boundary:

1) If one cube runs into the back of another it stops (just what I need)
2) If one cube runs very close and parallel to another, it will stop as soon as its ellipsoid hits the other's box (not what I need - depending on the angle [90 degrees on is fine!])

Many thanks to b32 for the PreviewEntityRadius function, which helps illustrate my problem perfectly:

Graphics3D 1024,768,32,2
SetBuffer BackBuffer()
Color 255,255,255

Const OBJ_LENSE=1
Const OBJ_FLOOR=2
Const OBJ_Train=3

Local BallastTexture=0
Local FPS
Local FPS_Chkpt=MilliSecs()
Local FPS_Count
Local ObjHandle=0
Local ObjType=0
Local TrainSel=-1

Dim Train(2)

Collisions OBJ_LENSE,OBJ_FLOOR,2,1
Collisions OBJ_Train,OBJ_Train,3,1

; setup entities
light=CreateLight()
Camera=CreateCamera()
CameraClsColor Camera,0,128,255
PositionEntity Camera,0,6,-16
RotateEntity Camera,15.0,0,0
EntityType Camera,OBJ_LENSE
ground=CreatePlane()
EntityColor ground, 34,255,123
EntityType ground,OBJ_FLOOR

For k=0 To 2
	Train(k)=CreateCube()
	ScaleEntity Train(k),4,1,0.5
	EntityBox Train(k),-4,-1,-0.5,8,2,1
	EntityRadius Train(k),4,1
	PreviewEntityRadius train(k),4,1
	PositionEntity Train(k),5,1,k*3
	EntityPickMode Train(k),3
	EntityType Train(k),OBJ_Train
Next

While Not KeyDown(1)
	; check for selection
	If MouseHit(1) Then
		For k=0 To 2
			EntityColor Train(k),255,255,255
		Next
		TrainSel=-1
		ObjType=0
		ObjHandle=CameraPick(Camera,MouseX(),MouseY())
		If ObjHandle<>0 Then
			ObjType=GetEntityType(ObjHandle)
			If ObjType=OBJ_Train Then
				For k=0 To 2
					If Train(k)=ObjHandle Then
						TrainSel=k
						Exit
					EndIf
				Next
				EntityColor PickedEntity(),255,255,0
			EndIf
		EndIf
	EndIf
	; check for move/rotate
	If KeyDown(42) Then ; LEFTSHIFT
		If KeyDown(200) Then
			MoveEntity Camera,0,0,0.1
		ElseIf KeyDown(208) Then
			MoveEntity Camera,0,0,-0.1
		ElseIf KeyDown(203) Then
			MoveEntity Camera,-0.1,0,0
		ElseIf KeyDown(205) Then
			MoveEntity Camera,0.1,0,0
		EndIf
	ElseIf KeyDown(29) Then ; LEFT CTRL
		If KeyDown(200) Then
			MoveEntity Camera,0,0.1,0
		ElseIf KeyDown(208) Then
			MoveEntity Camera,0,-0.1,0
		ElseIf KeyDown(203) Then
			MoveEntity Camera,-0.1,0,0
		ElseIf KeyDown(205) Then
			MoveEntity Camera,0.1,0,0
		EndIf
	ElseIf TrainSel>-1 Then
		If KeyDown(200) Then
			If KeyDown(203) Then
				TurnEntity Train(TrainSel),0,1.0,0
			EndIf
			If KeyDown(205) Then
				TurnEntity Train(TrainSel),0,-1.0,0
			EndIf
			MoveEntity Train(TrainSel),-0.1,0,0
		EndIf
		If KeyDown(208) Then
			MoveEntity Train(TrainSel),0.1,0,0
		EndIf
	EndIf
	; update screen
	UpdateWorld
	RenderWorld
	FPS_Count=FPS_Count+1
	If MilliSecs()-FPS_Chkpt>=1000
		FPS_Chkpt=MilliSecs()
		FPS=FPS_Count
		FPS_Count=0
	EndIf
	Text 0,0,"FPS: "+FPS
	If TrainSel>-1 Then
		Text 0,12,"Entity Handle:"+Train(TrainSel)
		Text 0,24,"Entity Collision:"+EntityCollided(Train(TrainSel),OBJ_TRAIN)
	EndIf
	Flip
Wend

For k=0 To 2
	FreeEntity Train(k)
Next
End

Function PreviewEntityRadius(mesh,xz_radius#,y_radius#)
	EntityRadius mesh,xz_radius#,y_radius#
	ok=FindChild(mesh, "COLLISION_SPHERE")
	If ok<>0 Then FreeEntity ok
	sphere=CreateSphere()
	ScaleEntity sphere,xz_radius#,y_radius#,xz_radius#
	EntityAlpha sphere,0.5
	EntityParent sphere,mesh
	RotateEntity sphere,0,0,0,1
End Function


The sort of collision detection I am trying to achieve must be possible - but is unfortunately beyond my noob and limited understanding of 3D programming. Maybe I am trying to run before I can walk? :o/

If anyone can give me any pointers as to how else I can achieve this I would be extremely grateful.

PS: EntityRadius entity,x_radius,y_radius,z_radius please!

I think you could use a physics library for this. I've never used one, but they were mentioned off and on here. Another solution might be turning your world 90 degrees, so the X is still X and the Y is the Z. That way the entityradius can remain upright, and still be adjusted to (sort of) fit the train.

Hiya b32. I did try turning the objects 90 degrees but that didn't work. Mind you, I didn't turn the floor and camera as well (if that's what you mean?). Hmmm - I wonder...

[edit] Well - a couple of hours later, it doesn't work if I tilt the world either. :o( I give up. Think I might fire up dear old DBPro... ;o)

DB Pro? Have a bit of perseverance and you'll be rewarded!!

Parent 2 x collision pivots to your cube one at the front of the train and the other at the rear. Set sphere collisions to them, rather than the cube. For the cube, continue to set the entitypickmode but to polygon (2 I think ). This may work for you.

As far as I know box collisions do not rotate with the object it's applied to so it's not very useful imo.

I think you also need to get used to the blitz coords system otherwise you'll be in a world of pain later.

Stevie