Mario Collisions

BlitzMax Forums/BlitzMax Programming/Mario Collisions

I've been thinking about creating a simple platform game, and am already having trouble with the collisions. I took some .bb source code that someone posted on CodersWorkshop, ported it to blitzmax, giving me this:
SuperStrict



'********************************** screen constants

Const SCREEN_WIDTH:Int = 800
Const SCREEN_HEIGHT:Int = 600
Const FULL_SCREEN:Int = False 
Const HERTZ:Int = 60


'********************************** grid constants

Const MAP_WIDTH:Int = 20
Const MAP_HEIGHT:Int = 15
Global map:Int[MAP_WIDTH,MAP_HEIGHT]
'********************************** player state enumeration

Const ST_NORMAL:Int = 0
Const ST_FALLING:Int = 1
Const ST_JUMPING:Int = 2

'********************************** 
Const GRAVITY:Float = .2
Const TILE_SIZE:Int = 40 
Const DIR_RIGHT:Int = 2
Const DIR_LEFT:Int = 1
Graphics SCREEN_WIDTH,SCREEN_HEIGHT,FULL_SCREEN,HERTZ


Global player:Tplayer = New Tplayer
player.x = 200
player.y = 200
'player.state = ST_FALLING
player.bg_width = 40
player.bg_height = 40



fillmap()

While Not KeyDown(KEY_ESCAPE)

	drawmap()
	player.update() 
	DrawText("x: " + player.x,50,50)
	DrawText("y: " + player.y,50,70)
	DrawText("state: " + player.state,50,90)
	Flip
	Cls
	
Wend 


Function drawmap()
	For Local loop_x:Int = 0 To MAP_WIDTH - 1
		For Local loop_y:Int = 0 To MAP_HEIGHT - 1
			SetColor map[loop_x,loop_y] * 255,60,60
			DrawRect loop_x*TILE_SIZE,loop_y * TILE_SIZE,TILE_SIZE,TILE_SIZE
		Next
	Next
End Function 






Type Tplayer
	
	Field x:Float
	Field y:Float
	
	Field xv:Float
	Field yv:Float
	
	Field id:String
	
	Field state:Int
	Field direction:Int
	Field distance:Int
	
	Field bg_width:Int 'bounding box width 
	Field bg_height:Int 'bounding box height
	
	Method Update()
		checkkeys()
		draw()
	End Method 
	
	Method draw()
		SetColor 255,255,255
		DrawRect x,y,bg_width,bg_height
	End Method 
	
	Method RightCollision:Int(speed:Float)
		
		For Local loop_x:Int = 0 To MAP_WIDTH - 1
			For Local loop_y:Int = 0 To MAP_HEIGHT - 1
				
				If rectsoverlap(x + speed + 1, y, bg_width, bg_height, loop_x * TILE_SIZE, loop_y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
					Select map[loop_x,loop_y]
						Case 1
							distance = (loop_x * TILE_SIZE) - (x + bg_width)
							Return True
					End Select
				EndIf
				
			Next
		Next 
		
	End Method 
	
	
	Method LeftCollision:Int(speed:Float)
		
		For Local loop_x:Int = 0 To MAP_WIDTH - 1
			For Local loop_y:Int = 0 To MAP_HEIGHT - 1
				
				If rectsoverlap(x + speed - 1, y, bg_width, bg_height, loop_x * TILE_SIZE, loop_y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
					Select map[loop_x,loop_y]
						Case 1
							distance = x - ((loop_x + 1) * bg_width)
							Return True
					End Select
				EndIf
				
			Next
		Next 
	End Method 
	


	Method BottomCollision:Int(speed:Float)
		
		For Local loop_x:Int = 0 To MAP_WIDTH - 1
			For Local loop_y:Int = 0 To MAP_HEIGHT - 1
				
				If rectsoverlap(x, y + speed + 1, bg_width, bg_height, loop_x * TILE_SIZE, loop_y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
					Select map[loop_x,loop_y]
						Case 1
							distance = (loop_y * TILE_SIZE) - (Y + bg_width)
							Return True
					End Select
				EndIf
				
			Next
		Next 
	End Method 	
	


	Method TopCollision:Int(speed:Float)
		
		For Local loop_x:Int = 0 To MAP_WIDTH - 1
			For Local loop_y:Int = 0 To MAP_HEIGHT - 1
				
				If rectsoverlap(x, y + speed - 1, bg_width, bg_height, loop_x * TILE_SIZE, loop_y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
					Select map[loop_x,loop_y]
						Case 1
							distance = ((loop_y + 1) * TILE_SIZE) - (y)
							Return True
					End Select
				EndIf
				
			Next
		Next 
	End Method 		
	
	
	Method checkkeys()
		
		Select state
			Case ST_NORMAL
				If Not bottomCollision(yv)
					state = ST_FALLING
				EndIf
				
			Case ST_FALLING
				yv = yv + GRAVITY
				If bottomCollision(yv) And state <> ST_JUMPING 
					yv = 0
					y = y + distance
					state = ST_NORMAL
				EndIf
				
			Case ST_JUMPING
				If topCollision(yv)
					yv = 0
					y = y + distance
					state = ST_FALLING
				EndIf
				yv = yv + GRAVITY
				If yv >= 0 
					state = ST_FALLING
				EndIf
		End Select
	
		If KeyHit(KEY_UP) And (Not topCollision(yv)) And (state = ST_NORMAL)
			state = ST_JUMPING
			yv = -8
		EndIf 
		
		If yv > 10
			yv = 10
		EndIf 
		
		If xv > 5 
			xv = 5
		ElseIf xv < -5
			xv = -5
		EndIf 
		
		x = x + xv
		y = y + yv
		
		If KeyDown(KEY_RIGHT)
			direction = DIR_RIGHT
			If rightCollision(xv) 
				xv = 0
				x = x + distance
			Else
				If xv > 0
					If leftCollision(xv)
						xv = 0
						x = x - distance
					EndIf
					xv = xv + 0.5
				Else
					xv = xv + 0.2
				EndIf
			EndIf 
		EndIf 
		
		
		If KeyDown(KEY_LEFT)
			direction = DIR_LEFT
			If leftCollision(xv)
				xv = 0
				x = x - distance
			Else
				If xv < 0 
					If rightCollision(xv)
						xv = 0
						x = x + distance
					EndIf
					xv = xv - 0.5
				Else
					xv = xv - 0.2
				EndIf
			EndIf 
		EndIf 
		
		
		If (xv > 0 Or xv < 0) And ((Not KeyDown(KEY_RIGHT)) And (Not KeyDown(KEY_LEFT)))
			
			Select direction
				Case DIR_LEFT
					If leftCollision(xv)
						xv = 0
						x = x - distance
					Else
						xv = xv + GRAVITY
						If xv >= 0 
							xv = 0
						EndIf
					EndIf
					
				Case DIR_RIGHT
					If rightCollision(xv)
						xv = 0
						x = x + distance
					Else
						xv = xv - GRAVITY
						If xv <= 0 
							xv = 0
						EndIf
					EndIf
			End Select
		EndIf
	End Method 



End Type 



Function fillmap()
	Local line:String 
	For Local y:Int = 0 To MAP_HEIGHT - 1
		ReadData line 
		For Local x:Int = 0 To MAP_WIDTH - 1
		
			map[x,y] = Int(Mid(line,x+1,1))
			
		Next
	Next 
	
End Function 
			



Function RectsOverlap:Int(x0:Float, y0:Float, w0:Float, h0:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
	If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
	Return True
End Function




DefData "11111111111111111111"
DefData "11010101010101010101"
DefData "10000000000000000001"
DefData "11110000000000111111"
DefData "10000000000000000001"
DefData "10000000000000000001"
DefData "10000000111100000001"
DefData "10000000000000000001"
DefData "11110000000000000001"
DefData "10000000000000000001"
DefData "10000000001110001111"
DefData "10000000000000000001"
DefData "10000000000000000001"
DefData "10000000000000000001"
DefData "11111111111111111111"


It works, in a sense, but is far from perfect. At times, it will put me off of the screen for no apparent reason, along with a few other quirks. I was wondering, for one, can anyone tell me what I can improve/fix to make the collisions work better? Even better, does anyone have some blitzmax code already written that shows basic collision detection? All help is appreciated.

This might interest you:

http://www.codersworkshop.com/viewshowcase.php?id=897

GellyWare: Yup, I already had a look at that. To put it simply, I was lost. I followed a few source files around and tried to figure out a few unknown variable before I gave up. Thanks for the suggestion, although the code is too confusing for me to make out quite what it's doing.

Here you go, I made this. Tell me if anything is confusing.
Oh I stole your map :P

SuperStrict

Graphics 800 , 600 , 0


Const Gravity:Float = .1


Type TPlayer
	Field x:Float,y:Float
	Field width:Float , height:Float
	Field speedx:Float , speedy:Float
	
	
	Method Draw()
		DrawRect x , y , width , height
	EndMethod
	
	Method Update() 
		
		'ground friction
		speedx:* .85
		
		'x conrtols
		If KeyDown(key_left) Then speedx = - 2
		If KeyDown(key_right) Then speedx = 2
		
		'x movement
		x:+ speedx
		
		'if you ran into a wall
		If map.rectcollide(x , y , width , height) Then
			'move back out of the wall
			x:- speedx
			'stop your speed
			speedx = 0
		EndIf
		
		
		'y controls (jump)
		If KeyHit(key_up) Then
			'make sure you are on the ground
			If map.rectcollide(x , y + 1 , width , height) Then
				'jump!
				speedy = - 5
			EndIf
		EndIf
		
		'y movement
		speedy:+ gravity
		y:+ speedy
		
		'if you hit a wall
		If map.rectcollide(x , y , width , height) Then
			'move back out of the wall
			y:- speedy
			'stop your speed
			speedy = 0
		EndIf
		
	EndMethod
EndType


Type TMap
	Field width:Int , height:Int
	Field array:Int[,]
	Const tilesize:Int = 30
	
	Method Load()
		Local x:Int,y:Int
		Local line:String
		
		ReadData width
		ReadData height
		
		array=New Int[width,height]
		
		For y = 0 Until height
			ReadData line
			For x = 0 Until width
				array[x , y] = Int(Mid(line , x + 1 , 1) )
			Next
		Next
	EndMethod
	
	
	Method Draw()
		Local x:Int , y:Int
		
		For y = 0 Until height
			For x = 0 Until width
				If array[x , y] = 1 Then
					DrawRect x * tilesize , y * tilesize , tilesize , tilesize
				EndIf
			Next
		Next
	EndMethod
	
	
	'Returns True if the rectangle collides with the map.
	'This one and only collision function can be used for everything.
	Method RectCollide:Int(x:Float , y:Float , width:Float , height:Float)
		Local x1:Int , y1:Int
		Local x2:Int , y2:Int
		
		'convert pixel positions to tile positions
		x1 = x / tilesize
		y1 = y / tilesize
		x2 = (x + width) / tilesize
		y2 = (y + height) / tilesize
		
		Local tilex:Int , tiley:Int
		
		'check each tile that the rect is in
		For tiley = y1 To y2
			For tilex = x1 To x2
				If array[tilex , tiley] = 1 Then
					Return True
				EndIf
			Next
		Next
		
		Return False
	EndMethod
	
EndType



Global You:tplayer = New tplayer
you.x = 200
you.y = 100
you.width = 20
you.height = 25


Global Map:tmap = New tmap
map.load()



Repeat
	you.update()
	you.draw()
	map.draw()
	Flip
	Cls
	If KeyHit(key_escape) Then End
Forever



DefData 20,15
DefData "11111111111111111111"
DefData "11010101010101010101"
DefData "10000000000000000001"
DefData "11110000000000111111"
DefData "10000000000000000001"
DefData "10000000000000000001"
DefData "10000000111100000001"
DefData "10000000000000000001"
DefData "11110000000000000001"
DefData "10000000000000000001"
DefData "10000000001110001111"
DefData "10000000000000000001"
DefData "10000000000000000001"
DefData "10000000000000000001"
DefData "11111111111111111111"


Thanks for the help, it works like a charm. Plus, I understand what's going on, which is a rare occurence in itself. :)