advice on bounds checking

Blitz3D Forums/Blitz3D Beginners Area/advice on bounds checking

Uhfgood(Posted 2005) [#1]
Well i'm making a game. Essentially right now I have a 'table' made of cubes, some blocks for walls, and a sphere, and you're able to move the sphere around the table as it doesn't fall through the table nor does it fall off the edges as i'm using blitz collisions. Sphere-to-poly with sliding collisions so i can move the ball without it stopping dead.

The table of blocks is simply a 2d array for entity id's right now, later they will become "tiles" in my game. in order to access each tile or block individually I need to determine where the ball is sitting on what block(s). I've already done this in the code. One thing I notice though is that sometimes the ball, that although blitz is taking care of the collisions, will move off the edge by however far I move it (currently it moves .2 units each time it moves). When it does this it will be out of range for the array when it comes time for me to index the array.

I move my game piece before calling update world.

I'm afraid i'll be forced to post my program again, so i'll do that here. Notice my main program loop is at the bottom of the program listing. The main functions to be concerned with are fn_MoveGameToken, and fn_ManageCollisions -- note also that fn_ManageCollisions does nothing right now other than determining what tile(s) the ball is sitting on, and displays it on the screen.

This will run if you put it in directly, and you can notice the numbers. The top numbers are the x and y which of course are the middle of the entity (.49 or some such) when you hit a wall and continue to press the arrow key down for whichever wall you're touching, it will jump to x.249 - because i'm moving .2

I would like to know the best way to handle bounds checking like this... Thanks to anyone who may reply...

; m = member, b = boolean

Const b_DebugMode = False

; This won't have positional information
; as blitz will do that for me.  It will
; have velocity, speed, and gravity parameters
; probably some blitz collision information as well
Type TGameObject

	Field m_entity
	Field m_xVelocity#
	Field m_yVelocity#
	Field m_zVelocity#
	Field m_xSpeed#
	Field m_ySpeed#
	Field m_zSpeed#	
	Field mb_useGravity
	Field m_gravity#
	
End Type

Global debugtxt$ = ""

; most of the game entities
; to copy all the playfield 'cells'
; from; Gametoken is the piece that 
; moves around the board.

Global camera, light, gameToken.TGameObject
Dim Playfield( 12, 12 )

Global logfile

;;; General Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function fn_AddDebugTxt( txt$ )

	debugTxt = debugTxt + txt$ + "|"
 
End Function

Function fn_ShowDebugTxt( txt$ )

	If( Right( txt, 1 ) <> "|" ) Then txt = txt + "|"

	ty = 0
	Repeat
		tx = Instr( txt, "|" )
		If( tx > 0 )
			Text( 0, ty, Left( txt, tx - 1 ) )
			txt = Right( txt, Len( txt ) - tx )
			ty = ty + 10
		End If
	Until tx = 0
	
	debugTxt$ = ""
	
End Function

Function fn_OpenLogFile()

	If( b_DebugMode = True )
	
		Cdate$ = CurrentDate$()
		Ctime$ = CurrentTime$()
		Cdir$ = CurrentDir$()
		logdir$ = Cdir$ + "log"
		
		If( FileType( logdir$ ) = 0 )
			CreateDir( logdir$ )
		End If
		
		LogFileName$ = logdir$ + "\" + Cdate$ + "log.txt"
		
		If( FileType( logdir$ ) = 2 )
			
			If( FileType( LogFileName$ ) = 1 )
				logfile = OpenFile( LogFileName$ )
				Repeat
					SeekFile( logfile, i )
					i = i + 1
				Until Eof( logfile )	
			Else
				logfile = WriteFile( LogFileName$ )
		
			End If
			
			If( logfile <> 0 )
			
				WriteLine( logfile, "" )
				WriteLine( logfile, LogFileName$ + " opened successfully." )
				WriteLine( logfile, "" )
				WriteLine( logfile, "log file opened on " + Cdate$ + " at " + Ctime$ + "." )
		
			Else
				DebugLog( "Could not create or open log file." )	
			End If
			
		End If
	
	End If
	
End Function ; fn_OpenLogFile()

;--------------------------------------;

Function fn_CloseLogFile()

	If( b_DebugMode = True )
	
		If( logfile <> 0 )
		
			WriteLine( logfile, "Logging successful, closing log file." )
			WriteLine( logfile, "" )
			WriteLine( logfile, "---" )
			CloseFile( logfile )
		
		End If
	
	End If

End Function ; fn_CloseLogFile()

;--------------------------------------;

; This function generally creates everything in 
; the game.  First the camera, then the light, then
; playfield and finally the game token.
Function fn_CreateWorld()

	; camera 
	camera=CreateCamera() 
	PositionEntity( camera, 6, 8, -11.5 )
	temppivot = CreatePivot()
	PositionEntity( temppivot, 6, 0, -6 )
	PointEntity( camera, temppivot )
	FreeEntity( temppivot )
	
	; light
	AmbientLight 32, 32, 32
	light=CreateLight() 
	RotateEntity light, 60, 0, 0

	; playfield boundry walls
	wallone = CreateCube()
	ScaleEntity( wallone, .5, 1, 7 )
	PositionEntity( wallone, -1, .5, -5.5 )
	EntityAlpha( wallone, .25 )
	EntityType( wallone, 3 )
		
	walltwo = CreateCube()
	ScaleEntity( walltwo, .5, 1, 7 )
	PositionEntity( walltwo, 12, .5, -5.5 )
	EntityAlpha( walltwo, .25 )
	EntityType( walltwo, 3 )
		
	wallthree = CreateCube()
	ScaleEntity( wallthree, 6, 1, .5 )
	PositionEntity( wallthree, 5.5, .5, -12 )
	EntityAlpha( wallthree, .25 )
	EntityType( wallthree, 3 )
		
	wallfour = CreateCube()
	ScaleEntity( wallfour, 6, 1, .5 )
	PositionEntity( wallfour, 5.5, .5, 1 )
	EntityAlpha( wallfour, .25 )	
	EntityType( wallfour, 3 )
	
	fn_CreatePlayfield()
	fn_CreateGameToken()

	; 2 = playfield (cube array array)
	Collisions( 1, 2, 2, 3 )
	Collisions( 1, 3, 2, 3 )
	
End Function ; fn_CreateWorld()

;--------------------------------------;

; free everything
Function fn_DestroyWorld()

	fn_DestroyGameToken()
	fn_DestroyPlayfield()

	FreeEntity( light )	
	FreeEntity( camera )
	
End Function ; fn_DestroyWorld

Function fn_ManageCollisions()

	; cell y is as you're looking from the top down, essentially the 2d
	; version used the y coordinates, but as y literally goes up and down
	; i have to worry about the z position (in and out of the screen)
	
	readjusted_z# = -EntityZ( gameToken\m_entity )
	
	fn_AddDebugTxt( "x = " + EntityX( gameToken\m_entity ) + " y = " + EntityZ( gameToken\m_entity ) )
	ul_x = ( EntityX( gameToken\m_entity ) - 0.45 )
	ul_y = ( readjusted_z# - 0.45 )
	fn_AddDebugTxt( "upperleft x = " + ul_x + " upperleft y = " + ul_y )
	ur_x = ( EntityX( gameToken\m_entity ) + 0.45 )
	ur_y = ( readjusted_z# - 0.45 )
	fn_AddDebugTxt( "upperright x = " + ur_x + " upperright y = " + ur_y )
	ll_x = ( EntityX( gameToken\m_entity ) - 0.45 )
	ll_y = ( readjusted_z# + 0.45 )
	fn_AddDebugTxt( "lowerleft x = " + ll_x + " lowerleft y = " + ll_y )
	lr_x = ( EntityX( gameToken\m_entity ) + 0.45 )
	lr_y = ( readjusted_z# + 0.45 )
	fn_AddDebugTxt( "lowerright x = " + lr_x + " lowerright y = " + lr_y )
	
End Function

;;; Playfield related functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Create the playfield, which at the moment
; is nothing more than an array of cubes.
Function fn_CreatePlayfield()

	; initial cube as a starting point
	Local cube = CreateCube() 
	ScaleEntity( cube, 0.5, 0.5, 0.5 )
	; 12 x 12 game board ( 0 - 11 = 12 positions )
	; note using y for z, swapping axes as the 2d
	; gameboard is being layed out flat like a table
	For y = 0 To 11
		For x = 0 To 11
			If( y > 0 ) Then adj_y = -y
			; create each "cell"
			Playfield( x, y ) = CopyEntity( cube )
			; position it relative to the other cells
			PositionEntity( Playfield( x, y ), x, 0, adj_y )
			EntityType( Playfield( x, y ), 2 )
			; give it a random color for indentity
			EntityColor( Playfield( x, y ), Rand( 63, 192 ), Rand( 63, 192 ), Rand( 63, 192 ) )
		Next
	Next
	
	; no more need for the cube
	FreeEntity( cube )
	
End Function ; fn_CreatePlayfield

;--------------------------------------;

; free up all the cells essentially
Function fn_DestroyPlayfield()

	For y = 0 To 11
		For x = 0 To 11
			FreeEntity( Playfield( x, y ) )
		Next
	Next

End Function ; fn_DestroyPlayfield

;;; Game Token Related Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; The piece the player directs
Function fn_CreateGameToken()

	gameToken = fn_CreateGameObject()
	
	; Make sure it created correctly
	If( gameToken <> Null )

		; now create the mesh/model etc
		gameToken\m_entity = CreateSphere()
		ScaleEntity( gameToken\m_entity, 0.45, 0.45, 0.45 )
		EntityType( gameToken\m_entity, 1 )
		PositionEntity( gameToken\m_entity, 5.5, .95, -5.5 )
		EntityRadius( gameToken\m_entity, .45 )
		ResetEntity( gameToken\m_entity )
		gameToken\m_xSpeed# = 0.2
		gameToken\m_ySpeed# = 0.0
		gameToken\m_zSpeed# = 0.2
		gameToken\m_xVelocity# = 0.0
		gameToken\m_yVelocity# = 0.0
		gameToken\m_zVelocity# = 0.0
		gameToken\mb_useGravity = True
		gameToken\m_gravity# = -.01
	Else
		RuntimeError( "Could not create game token." )
	End If
		
End Function ; fn_CreateGameToken()

;--------------------------------------;

; get rid of the game piece
Function fn_DestroyGameToken()

	fn_DestroyGameObject( gameToken )
	
End Function ; fn_DestroyGameToken()

;--------------------------------------;

Function fn_MoveGameToken()

	If( gameToken\mb_useGravity = True )
		gameToken\m_yVelocity# = gameToken\m_yVelocity# + gameToken\m_gravity#
	End If
	
	If( KeyDown( 200 ) )
		gameToken\m_zVelocity# = gameToken\m_zSpeed#
	Else If( KeyDown( 208 ) )
		gameToken\m_zVelocity# = -gameToken\m_zSpeed#
	Else
		gameToken\m_zVelocity# = 0
	End If

	If( KeyDown( 203 ) )
		gameToken\m_xVelocity# = -gameToken\m_xSpeed#
	Else If( KeyDown( 205 ) )
		gameToken\m_xVelocity# = gameToken\m_xSpeed#
	Else
		gameToken\m_xVelocity# = 0
	End If
	
	dx# = gameToken\m_xVelocity#
	dy# = gameToken\m_yVelocity#
	dz# = gameToken\m_zVelocity#
	
	TranslateEntity( gameToken\m_entity, dx#, dy#, dz# )
	
End Function

;;; TGameObject Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; A quick note about game object functions.  It's not always
; good to create super long function names, so most of the
; game object functions shortend TGameObject as TGOb For instance
; fn_TGObXXX() - where XXX is the rest of the function name.

;--------------------------------------;

; create and return a game object (game entity,
; but since this is blitz i called it an object)
Function fn_CreateGameObject.TGameObject()

	; 'o' is temporary
	Local o.TGameObject
	
	; create it make sure everything went well
	o.TGameObject = New TGameObject
	If( o <> Null )
		Return( o )
	Else
		RuntimeError( "Could not create game object." )
	End If
	
End Function ; fn_CreateGameObject.TGameObject()

;--------------------------------------;

; Get rid of it
Function fn_DestroyGameObject( o.TGameObject )

	; if there's an entity created free it first
	If( o\m_entity <> 0 )
		FreeEntity( o\m_entity )
	End If
	
	Delete( o )
	
End Function ; fn_DestroyGameObject( o.TGameObject )

;--------------------------------------;

Function fn_TGObSetXVelocity( o.TGameObject, value# )

	o\m_xVelocity# = value#
	
End Function

;--------------------------------------;

Function fn_TGObGetXVelocity#( o.TGameObject )

	Return( o\m_xVelocity# )
	
End Function

;--------------------------------------;

Function fn_TGObSetYVelocity( o.TGameObject, value# )

	o\m_yVelocity# = value#
	
End Function

;--------------------------------------;

Function fn_TGObGetYVelocity#( o.TGameObject )

	Return( o\m_yVelocity# )
	
End Function

;--------------------------------------;

Function fn_TGObSetZVelocity( o.TGameObject, value# )

	o\m_zVelocity# = value#
	
End Function

;--------------------------------------;

Function fn_TGObGetZVelocity#( o.TGameObject )

	Return( o\m_zVelocity# )
	
End Function

;--------------------------------------;

Function fn_TGObSetXSpeed( o.TGameObject, value# )

	o\m_xSpeed# = value#
	
End Function

;--------------------------------------;

Function fn_TGObGetXSpeed#( o.TGameObject )

	Return( o\m_xSpeed# )
	
End Function

;--------------------------------------;

Function fn_TGObSetYSpeed( o.TGameObject, value# )

	o\m_ySpeed# = value#
	
End Function

;--------------------------------------;

Function fn_TGObGetYSpeed#( o.TGameObject )

	Return( o\m_ySpeed# )
	
End Function

;--------------------------------------;

Function fn_TGObSetZSpeed( o.TGameObject, value# )

	o\m_zSpeed# = value#
	
End Function

;--------------------------------------;

Function fn_TGObGetZSpeed#( o.TGameObject )

	Return( o\m_zSpeed# )
	
End Function

;--------------------------------------;

Function fn_TGObSetGravityFlag( o.TGameObject, b_flag )

	o\mb_useGravity = b_flag

End Function

;--------------------------------------;

Function fn_TGObGetGravityFlag( o.TGameObject )

	Return( o\mb_useGravity )
	
End Function

;--------------------------------------;

Function fn_TGObSetGravity( o.TGameObject, value# )

	o\m_gravity = value

End Function

;--------------------------------------;

Function fn_TGObGetGravity#( o.TGameObject )

	Return( o\m_gravity# )
	
End Function

;--------------------------------------;

.main

Graphics3D 640,480 
SetBuffer BackBuffer() 

fn_OpenLogFile()

fn_CreateWorld()

While Not KeyDown( 1 ) 
	fn_MoveGameToken()
	fn_ManageCollisions()
	fn_AddDebugTxt( "Count collisions = " + CountCollisions( gameToken\m_entity ) )
	UpdateWorld
	RenderWorld 
	fn_ShowDebugTxt( debugTxt )
	Flip 
Wend 

fn_DestroyWorld()

fn_CloseLogFile()

End 



Stevie G(Posted 2005) [#2]
Set up a cube , scale it to the size of your grid , flipmesh and entityalpha 0 to hide it. Set it up with polygon collision type and you'll find that the ball can't move beyond the main grid.

Not sure if this is what you're after?

Stevie


Uhfgood(Posted 2005) [#3]
I want to be able to detect when a ball is sitting on one of the cubes in the grid. Those walls around there are for collision, but even though the ball doesn't actually move the position still gets changed. I'm assuming that it gets corrected when updateworld is called, as it's thus called before flip you don't see it move, but the numbers say it moved.

I suppose I could call ManageCollisions after updateworld? (this is not blitz collisions but ones that will deal specifically with the game)

I've done just that and appearently that works... so fn_ManageCollisions (user defined collision system for game) is now being called after updateworld

Thanks!