Tile Collision help

BlitzMax Forums/BlitzMax Programming/Tile Collision help

Hi all, I need some help with the code below, specifically detecting collisions correctly

if I move with increments of 32(tile size) collision is perfect, but obviously this is not ideal, so if I move say 3 unit increments, collision gets screwy, as in the example below. Can anyone help me this ?




'tile map example by Deux


Strict
Graphics 640,480,0

Const TILE_TYPE_WALL=1
Const TILE_TYPE_ROOF=2
Const TILE_TYPE_GROUND=3

Const MAP_WIDTH = 10
Const MAP_HEIGHT = 10



'SetClsColor 155,155,155
Type TTile
	Field x#
	Field y#
	Field width#
	Field height#
	Field red#
	Field green#
	Field blue#
	Field TileType%
	Field image:TImage
End Type


Global map[] = [2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,3,3,3,3,3,3,3,3,3,3]

Global TileList:TList=CreateList()

Global playery#=64
Global playerx#=64
Global oldx#
Global oldy#

Global x#
Global y#



Function LoadMap()

	Local tile_countx%=0
	Local tile_county%=0
	
	For Local x%=EachIn map
		Local t:TTile = New TTile
		If(tile_countx = MAP_WIDTH) Then
			tile_county :+1
			tile_countx = 0
		End If	
		
		
		Select x
		
			Case 1
				t.red# = tile_countx*10
				t.green# = tile_county*10
				t.blue# = 125
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_WALL
				TileList.AddLast t
			Case 2
				t.red# = tile_countx*10
				t.green# = tile_county*10
				t.blue# = 125
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_ROOF
				TileList.AddLast t
			Case 3
				t.red# = tile_countx*10
				t.green# = tile_county*10
				t.blue# = 125
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_GROUND
				TileList.AddLast t
			
		End Select
			
	tile_countx :+ 1


	Next

End Function

Function RenderMap()
	For Local t:TTile=EachIn TileList
		SetColor t.red#,t.green#,t.blue#
		DrawRect t.x#,t.y#,32,32
		DrawText t.tiletype,t.x#,t.y#
	Next
End Function


LoadMap()

HideMouse

While Not KeyDown(KEY_ESCAPE)

	RenderMap()
	oldx# = playerx#
	oldy# = playery#

	If KeyDown(KEY_LEFT) playerx# :-3
	If KeyDown(KEY_RIGHT) playerx# :+3
	If KeyDown(KEY_UP) playery# :-3
	If KeyDown(KEY_DOWN) playery# :+3
	
	Local index:Int = playerx#/32+(playery#/32*MAP_HEIGHT)
	DrawText map[index],10,50
	
	If map[index] = 0 Then
	Else
		playerx# = oldx#
		playery# = oldy#
		
	End If
	
	DrawRect  playerx#,playery#,32,32
	
	Flip
	FlushMem
	Cls
	
	
Wend

 




this is a bit messy but it works - I just noticed some situations where this does quite cut it, working it out...

'tile map example by Deux


Strict
Graphics 640,480,0

Const TILE_TYPE_WALL=1
Const TILE_TYPE_ROOF=2
Const TILE_TYPE_GROUND=3

Const MAP_WIDTH = 10
Const MAP_HEIGHT = 10



'SetClsColor 155,155,155
Type TTile
	Field x#
	Field y#
	Field width#
	Field height#
	Field red#
	Field green#
	Field blue#
	Field TileType%
	Field image:TImage
End Type


Global map[] = [2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,3,3,3,3,3,3,3,3,3,3]

Global TileList:TList=CreateList()

Global playery#=64
Global playerx#=64
Global oldx#
Global oldy#

Global x#
Global y#



Function LoadMap()

	Local tile_countx%=0
	Local tile_county%=0
	
	For Local x%=EachIn map
		Local t:TTile = New TTile
		If(tile_countx = MAP_WIDTH) Then
			tile_county :+1
			tile_countx = 0
		End If	
		
		
		Select x
		
			Case 1
				t.red# = tile_countx*10
				t.green# = tile_county*10
				t.blue# = 125
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_WALL
				TileList.AddLast t
			Case 2
				t.red# = tile_countx*10
				t.green# = tile_county*10
				t.blue# = 125
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_ROOF
				TileList.AddLast t
			Case 3
				t.red# = tile_countx*10
				t.green# = tile_county*10
				t.blue# = 125
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_GROUND
				TileList.AddLast t
			
		End Select
			
	tile_countx :+ 1


	Next

End Function

Function RenderMap()
	For Local t:TTile=EachIn TileList
		SetColor t.red#,t.green#,t.blue#
		DrawRect t.x#,t.y#,32,32
		DrawText t.tiletype,t.x#,t.y#
	Next
End Function


LoadMap()

HideMouse

While Not KeyDown(KEY_ESCAPE)

	RenderMap()
	oldx# = playerx#
	oldy# = playery#
	Local index:Int = 0
	Local temp:Int=0
	
	If KeyDown(KEY_A) Or KeyDown(KEY_LEFT) Then 
		If playerx# > 0 Then playerx#:-3
		If playerx# < 0 Then 
			playerx#=0
		Else
			' check the characters upper left corner
			index=(playerx#/32)
			temp:Int=(playery#/32)
			temp:*MAP_HEIGHT
			index:+temp
			If map[index]<>0 Then 
				playerx#:+3
			Else ' check the characters lower left corner
				index=(playerx#/32)
				temp:Int=((playery#+32)/32)
				temp:*MAP_HEIGHT
				index:+temp
				If map[index]<>0 Then playerx#:+3
			EndIf
		EndIf
	EndIf
	
	If KeyDown(KEY_D) Or KeyDown(KEY_RIGHT) Then 
		If playerx# < (MAP_WIDTH*32)-32 Then playerx#:+3
		If playerx#+32 > (MAP_WIDTH*32)-32 Then 
			playerx#=(MAP_WIDTH*32)-32
		Else
			' check the characters upper right corner
			index=((playerx#+32)/32)
			temp:Int=(playery#/32)
			temp:*MAP_HEIGHT
			index:+temp
			If map[index]<>0 Then 
				playerx#:-3
			Else ' lower right corner
				index=((playerx#+32)/32)
				temp:Int=((playery#+32)/32)
				temp:*MAP_HEIGHT
				index:+temp
				If map[index]<>0 Then playerx#:-3
			EndIf
		EndIf
	EndIf
	If KeyDown(KEY_W) Or KeyDown(KEY_UP) Then 
		If playery# > 0 Then playery#:-3
		If playery# < 0 Then 
			playery#=0
		Else
			' check the characters upper left corner
			index=(playerx#/32)
			temp:Int=(playery#/32)
			temp:*MAP_HEIGHT
			index:+temp
			If map[index]<>0 Then 
				playery#:+3
			Else
				' check the characters upper right corner
				index=((playerx#+32)/32)
				temp:Int=(playery#/32)
				temp:*MAP_HEIGHT
				index:+temp
				If map[index]<>0 Then playery#:+3
			EndIf
		EndIf
	EndIf
	If KeyDown(KEY_S) Or KeyDown(KEY_DOWN) Then 
		If playery# < (MAP_HEIGHT*32)-32 Then playery#:+3
		If playery# > (MAP_HEIGHT*32)-32 Then 
			playery#=(MAP_HEIGHT*32)-32
		Else
			' check the characters lower left corner
			index=(playerx#/32)
			temp:Int=((playery#+32)/32)
			temp:*MAP_HEIGHT
			index:+temp
			If map[index]<>0 Then 
				playery#:-3
			Else
				' check the characters lower right corner
				index=((playerx#+32)/32)
				temp:Int=((playery#+32)/32)
				temp:*MAP_HEIGHT
				index:+temp
				If map[index]<>0 Then playery#:+3
			EndIf
		EndIf
	EndIf
	
	'If KeyDown(KEY_LEFT) playerx# :-3
	'If KeyDown(KEY_RIGHT) playerx# :+3
	'If KeyDown(KEY_UP) playery# :-3
	'If KeyDown(KEY_DOWN) playery# :+3
	
	'Local index:Int = playerx#/32+(playery#/32*MAP_HEIGHT)
	'DrawText map[index],10,50
	
	'If map[index] = 0 Then
	'Else
	'	playerx# = oldx#
	'	playery# = oldy#
		
	'End If
	
	DrawRect  playerx#,playery#,32,32
	
	Flip
	FlushMem
	Cls
	
	
Wend

 





Ah thanks scott !

What problems were you experiencing ?

I now understand the above code, it works well, but how would one handle collision on slopes ?

Also another silly question, I am using a linear array, and pushing each element tile into a list, is there any advantage in rather using a multi-dimensional array instead of a linear array ? ala map[x][y] and looping through it ? Would it make life easier ?

I am just trying to gauge how this tiling thing is done.

'tile map example by Deux


Strict
Graphics 640,480,0

Const TILE_TYPE_WALL=1
Const TILE_TYPE_ROOF=2
Const TILE_TYPE_GROUND=3

Const MAP_WIDTH = 10
Const MAP_HEIGHT = 10

Type TTile
	Field x#
	Field y#
	Field width#
	Field height#
	Field red#
	Field green#
	Field blue#
	Field TileType%
	Field image:TImage
End Type


Global map[] = [2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,3,3,3,3,3,3,3,3,3,3]

Global TileList:TList=CreateList()



Type TPlayer
	Global px:Float
	Global py:Float
	
	
	Function Update()
		Local index:Int=0
		Local y_comp:Int=0
		
		If KeyDown(KEY_LEFT) Then
				If px > 0 Then px:-3
				If px < 0 Then 
				px=0 
		Else
			' upper left
			index = px/32
			y_comp = py/32
			y_comp:*MAP_WIDTH
			index:+y_comp
		
			If map[index] <> 0 Then
				px:+ 3
			Else
				' lower left
				index=px/32
				y_comp = (py+32)/32
				y_comp:*MAP_WIDTH
				index :+ y_comp%
				If map[index] <> 0 Then px :+3
			End If
		
		
		End If
		End If

	   If KeyDown(KEY_RIGHT) Then 
				If px < (MAP_WIDTH*32)-32 Then px:+3
				If px+32 > (MAP_WIDTH*32)-32 Then 
				px=(MAP_WIDTH*32)-32
	   Else
			' upper right
			index=((px+32)/32)
			y_comp=(py/32)
			y_comp:*MAP_WIDTH
			index:+y_comp

			If map[index]<>0 Then 
				px:-3
			Else ' lower right corner
				index=((px#+32)/32)
				y_comp=((py#+32)/32)
				y_comp:*MAP_WIDTH
				index:+y_comp
				If map[index]<>0 Then px:-3
			EndIf
       EndIf
	   End If	

		If KeyDown(KEY_UP) Then 
			If py > 0 Then py:-3
			If py < 0 Then 
			py=0
		Else
			' check the characters upper left corner
				index=(px/32)
				y_comp=(py/32)
				y_comp:*MAP_HEIGHT
				index:+y_comp
				If map[index]<>0 Then 
					py:+3
 			    Else
		' check the characters upper right corner
					index=((px+32)/32)
					y_comp=(py/32)
					y_comp:*MAP_HEIGHT
					index:+y_comp
					If map[index]<>0 Then py:+3
				EndIf
		EndIf
	   End If

	   If KeyDown(KEY_DOWN) Then 
			If py < (MAP_HEIGHT*32)-32 Then py:+3
			If py > (MAP_HEIGHT*32)-32 Then 
			py=(MAP_HEIGHT*32)-32
	   Else
		' check the characters lower left corner
			index=(px/32)
			y_comp=((py+32)/32)
			y_comp:*MAP_HEIGHT
			index:+y_comp

			If map[index]<>0 Then 
				py#:-3
	   		Else
		' check the characters lower right corner
				index=((px+32)/32)
				y_comp:Int=((py+32)/32)
				y_comp:*MAP_HEIGHT
				index:+y_comp
				If map[index]<>0 Then py:-3
			End If	
	    EndIf
	End If	

	DrawRect  px,py,32,32

		
	End Function
	
	Function Draw()
		DrawRect  self.px#,self.py#,32,32
	End Function
	
End Type




Function LoadMap()

	Local tile_countx%=0
	Local tile_county%=0
	
	For Local x%=EachIn map
		Local t:TTile = New TTile
		If(tile_countx = MAP_WIDTH) Then
			tile_county :+1
			tile_countx = 0
		End If	
		
		
		Select x
		
			Case 1
				t.red# = 255
				t.green# = 0
				t.blue# = 0
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_WALL
				TileList.AddLast t
			Case 2
				t.red# = 0
				t.green# = 255
				t.blue# = 0
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_ROOF
				TileList.AddLast t
			Case 3
				t.red# = 0
				t.green# = 0
				t.blue# = 255
				t.width#=32
				t.height#=32
				t.x# = (tile_countx)*t.width#
				t.y#=(tile_county)*t.height#
				t.TileType = TILE_TYPE_GROUND
				TileList.AddLast t
			
		End Select
			
	tile_countx :+ 1


	Next

End Function

Function RenderMap()
	For Local t:TTile=EachIn TileList
		SetColor t.red#,t.green#,t.blue#
		DrawRect t.x#,t.y#,32,32
		DrawText t.tiletype,t.x#,t.y#
	Next
End Function


LoadMap()

HideMouse

TPlayer.px=64
TPlayer.py=64


While Not KeyDown(KEY_ESCAPE)

	RenderMap()
	
	TPlayer.Update()
	
	Flip
	FlushMem
	Cls
	
	
Wend

 




ok, was playing around a bit with this, i like muti-dim arrays better :)

Strict
Graphics 640,480,0


Global map%[5,5]
Global px#=128
Global py#=128

map[0,0]=1
map[0,1]=1
map[2,0]=1

Function rendermap()

	For Local x:Int = 0 To 4
		For Local y:Int=0 To 4
			Local tile = map[x,y]
			If(tile=1) Then
				SetColor 255,0,0
			Else
				SetColor 0,255,0			
			End If
  		    DrawRect x*32,y*32,32,32

		Next
	Next 
End Function

While Not KeyDown(key_escape)

	If KeyHit(KEY_LEFT) px:-32
	If KeyHit(KEY_RIGHT) px:+32
	If KeyHit(KEY_UP) py:-32
	If KeyHit(KEY_DOWN) py:+32
	
	SetColor 255,255,255
	
	
	RenderMap

	SetColor 0,0,255
	DrawRect px,py,32,32
	If map[px/32,py/32]=1 DrawText "collision",10,10
	Flip
	Cls
Wend







ok, I am ready to pull all my hair out :(

for some reason, the collisions are screwy, misaligned if you will.

Could someone please point out the error of my ways here, I cant spot why.

You will notice than in some occations the rects are flush. I think this is what scott was talking about.

Anyway, I built in a little level editor, so you can place tiles down, press [space] to activate the editor, place your tiles, and move the player around.

Any help would be greatly appreciated.

' tile engine and map - Deux

Strict
Graphics 640,480,0

Const MAP_HEIGHT = 100
Const MAP_WIDTH  = 100
Const PLAYER_INC = 3

Global map%[MAP_WIDTH,MAP_HEIGHT]
Global px#=64
Global py#=64
Global oldx#=0
Global oldy#=0
Global TileList:TList = CreateList()
Global EditMode:Byte = 0


Type Tile
	Field red
	Field green
	Field blue
End Type


Function DoGame()
	Local oldx:Int = px
	Local oldy:Int = py

	If KeyDown(KEY_LEFT) Then
		If px > 0 Then px:-PLAYER_INC
		If px < 0 Then 
			px=0 
		Else
			If Collision(px,py) Then ' upper left
				px:+PLAYER_INC				
				'px = oldx
			Else If Collision(px,py+32) Then 'lower left
				px:+PLAYER_INC
				'px = oldx
			End If
		End If
	End If

	If KeyDown(KEY_RIGHT) Then
		If px < (MAP_WIDTH*32)-32 Then px:+PLAYER_INC
		If px > (MAP_WIDTH*32)-32 Then 
			px=(MAP_WIDTH*32)-32 
		Else
			If Collision(px+32,py) Then ' upper right
				px:-PLAYER_INC
				'px = oldx
			Else If Collision(px+32,py+32) Then ' lower right
				px:-PLAYER_INC
				'px = oldx
			End If
		End If
	End If

	If KeyDown(KEY_UP) Then
		If py > 0 Then py:-PLAYER_INC
		If py < 0 Then 
			py=0
		Else
			If Collision(px,py) Then ' upper left
				py:+PLAYER_INC
				'py=oldy
			Else If Collision(px+32,py) Then ' upper right
				py:+PLAYER_INC
				'py=oldy
			End If
		End If
	End If
	
	If KeyDown(KEY_DOWN) Then
		If py < (MAP_HEIGHT*32)-32  Then py:+PLAYER_INC
		If py > (MAP_HEIGHT*32)-32 Then 
			py=(MAP_HEIGHT*32)-32
		Else
			If Collision(px,py+32) Then ' upper left
				py:-PLAYER_INC
				'py=oldy
			Else If Collision(px+32,py+32) Then ' upper right
				py:-PLAYER_INC
				'py=oldy
			End If
		End If
	End If
	
	RenderMap

	SetColor 0,0,255

	DrawRect px,py,32,32
	
	SetColor 55,55,55
	
End Function

While Not KeyDown(key_escape)

	If KeyHit(KEY_SPACE) EditMode = Not EditMode

	If EditMode=0 Then
		DoGame()
	Else
		DoEditor()	
	End If

	Flip
	Cls
Wend

Function DoEditor()
	Local mx:Int = 0
	Local my:Int = 0
	Local tx:Int = 0
	Local ty:Int = 0

	RenderMap()

	SetColor 0,125,0

	For Local x:Int = 0 To GraphicsWidth() Step 32
		DrawLine x,0,x,GraphicsHeight()
	Next
	
	For Local y:Int = 0 To GraphicsHeight() Step 32
		DrawLine 0,y,GraphicsWidth(),y
	Next
	
	SetColor 0,255,0
	DrawText "X: " + MouseX() + " Y: " + MouseY(),10,10
	DrawText "X2: " + MouseX()/32 + " Y2: " + MouseY()/32,10,30
	SetColor 255,255,255

	mx = MouseX()/32
	my = MouseY()/32
	
	tx = mx*32
	ty = my*32

	DrawRect tx,ty,32,32	
	
	If MouseDown(1) Then
		map[mx,my]=1
	Else If MouseDown(2) Then
		map[mx,my]=0	
	End If
	
	
End Function

Function Collision:Byte(x:Int,y:Int)
	Return map[x/32,y/32]
End Function

Function rendermap()

	For Local x:Int = 0 To MAP_WIDTH-1
		For Local y:Int=0 To MAP_HEIGHT-1
			Local tile = map[x,y]
			If(tile=1) Then
				SetColor 255,0,0
			Else
				SetColor 55,55,55			
			End If
  		    DrawRect x*32,y*32,32,32
		
		Next
	Next 
	
End Function


Function loadMap()

	
End Function





Is there nobody that can point me in the right direction ?

Looks like I have to use AABB :(

I seem to have fixed it, w00t !

Sorry I didn't get back to you sooner. Yeah the flush issues was part of the problem in my code above. You have it fixed though, it sounds like. I was going to use Mod to determine the number of pixels the player could move if they couldn'e move an entire 3 pixels.

yeah, change the coordinates for getting 4 points from 32 to 31.