2d p*p collision detecting

Game development specific discussions.
Post Reply
{Nathan}
Posts: 301
Joined: Jun 04, 2005 15:18
Location: Ohio
Contact:

2d p*p collision detecting

Post by {Nathan} »

I am having *major* issues with this. I am no master at p*p scrolling, and this is the second p*p engine I have ever made. This is the first one to attempt to incorperate collision detection in it. I don't have any idea how to do it. My source code, below, has 4 different movexxxx subs, but the MoveDown sub was the only one I really tested. If some p*p guru could help me, I would be forever greatful.

Code: Select all

type EngineType
	MaxX		as integer
	MaxY		as integer
	ScreenWidth	as integer
	ScreenHeight	as integer
	Action 		as integer							'Action tracker
	MaxNPCs		as integer							'Maximum amount of NPCs
	PlayerGFX	as integer
end type

type MapType
	tile 			as integer
	Collision		as integer
end type

type TankType
	Sprites(1 to 4) as any ptr
	Direction		as integer
	x			as integer
	y			as integer
end type

declare sub DrawMap ()				'Draws our map
declare sub MoveUp (byref Tank as TankType) 				'These next subs will handle moving up, down, left, and right
declare sub MoveDown (byref Tank as TankType)
declare sub MoveLeft (byref Tank as TankType)
declare sub MoveRight (byref Tank as TankType)
declare sub LoadTiles (TileFile as string)
declare sub LoadMap (MapFile as string)
declare function CheckFileLines (File as string) as integer
declare function IsCollision(X1 as integer, Y1 as integer, X2 as integer, Y2 as integer) as integer

#define KeyLeft &h4B
#define KeyRight &h4D
#define KeyUp &h48
#define KeyDown &h50
#define KeyEsc &h01
#define CloseApp chr(255) + "k"

const South = 1, North = 2, West = 3, East = 4				'North, South, East, and West
const False = 0, True = NOT False							'True and False
const Wall = 1, Grass = 0

dim shared as EngineType Engine						'The game's engine data
dim shared as MapType Map(19, 19)						'The map!
dim shared as any ptr Tiles() 							'The tileset array
dim shared as any ptr Explosion1, Explosion2, Explosion3
dim shared as TankType BlueTank
dim shared as TankType GreenTank

dim as integer xCount, yCount

screenres 640, 480, 32, 2											'512 by 384 at 32 bits
screenset 1, 0										'Sets screen 1to the working screen and screen 0 to display

screeninfo Engine.ScreenWidth, Engine.ScreenHeight			'Stores 512 and 384 into the Engine type
Engine.MaxX = 32 * 20									'Sets the max X value in our Engine type
Engine.MaxY = 32 * 20									'Sets the max Y value in our Engine type

LoadTiles("sprites/tiles.dat")								'Loads the tiles into the Tiles() array
LoadMap("easymap.map")								'Loads the 20 by 20 map into the Map array

do

	if multikey(KeyUp) then
		MoveUp(GreenTank)
		Engine.Action = True
	elseif multikey(KeyDown) then
		MoveDown(GreenTank)
		Engine.Action = True
	elseif multikey(KeyLeft) then
		MoveLeft(GreenTank)
		Engine.Action = True
	elseif multikey(KeyRight) then
		MoveRight(GreenTank)
		Engine.Action = True
	end if
	
	if Engine.Action = True then
		DrawMap()
		Engine.Action = False
	end if
	
loop until inkey = chr(27)




'###############################################################
'S U B   P R O G R A M S
'###############################################################
sub DrawMap()
	dim as integer CameraX, CameraY, xTile, yTile
	dim as integer yCount, xCount, Tile, xOffset, yOffset
	
	cls
	
	'GreenTank Half
	CameraX = GreenTank.X - Engine.ScreenWidth \ 2
	CameraY = GreenTank.Y - Engine.ScreenHeight \ 2
	if CameraX < 0 then CameraX = 0
	if CameraY < 0 then CameraY = 0
	if CameraX > Engine.MaxX - Engine.ScreenWidth then CameraX = Engine.MaxX - Engine.ScreenWidth
	if CameraY > Engine.MaxY - Engine.ScreenHeight then CameraY = Engine.MaxY - Engine.ScreenHeight
	
	xTile = int(CameraX \ 32)
	yTile = int(CameraY \ 32)
	xOffset = CameraX mod 32
	yOffset = CameraY mod 32
	
	for xCount = 0 to Engine.ScreenWidth \ 32 - 1
		for yCount = 0 to Engine.ScreenHeight \ 32
			Tile = Map( xCount + xTile, yCount + ytile ).Tile
			put (xCount * 32 - xOffset, yCount * 32 - yOffset), Tiles(Tile)
		next
	next
	
	put (GreenTank.X - CameraX, GreenTank.Y - CameraY), GreenTank.Sprites(GreenTank.Direction), trans

	
	sleep 4
	screencopy 1, 0											'Copies the working page to the page that is displayed
end sub


sub LoadTiles(TileFile as string)
	dim as integer FIleLines, i, FileNum
	dim as string FileToLoad
	
	FileLines = CheckFileLines(TileFile)
	FileNum = FreeFile
	redim Tiles(FileLines - 1) as any ptr
	
	for i = 0 to FileLines - 1
		Tiles(i) = ImageCreate(32, 32)
	next
	
	open TileFile for input as #FileNum
		for i = 0 to FileLines - 1
			input #FileNum, FileToLoad
			bload "sprites/" & FileToLoad, Tiles(i)
		next
	close #FileNum
	
	bload "explode1.bmp", Explosion1
	bload "explode2.bmp", Explosion2
	bload "explode3.bmp", Explosion3
	
	with GreenTank
		for i = 1 to 4
			.Sprites(i) = ImageCreate(32, 32)
		next
		bload "sprites/greentankright.bmp", .Sprites(East)
		bload "sprites/greentankleft.bmp", .Sprites(West)
		bload "sprites/greentankup.bmp", .Sprites(North)
		bload "sprites/greentankdown.bmp", .Sprites(South)
	end with
	
	with BlueTank
		for i = 1 to 4
			.Sprites(i) = ImageCreate(32, 32)
		next
		
		bload "sprites/bluetankright.bmp", .Sprites(East)
		bload "sprites/bluetankleft.bmp", .Sprites(West)
		bload "sprites/bluetankup.bmp", .Sprites(North)
		bload "sprites/bluetankdown.bmp", .Sprites(South)
	end with
end sub

sub LoadMap (MapFile as string)
	dim as integer FileInteger, FileNum, yCount, xCount
	
	FileNum = FreeFile
	
	open MapFile for input as #FileNum
		for yCount = 0 to 19
			for xCount = 0 to 19
				input #FileNum, FileInteger
				Map(xCount, yCount).Tile = FileInteger
			next
		next
	close #1
	
	for yCount = 0 to 19
		for xCount = 0 to 19
			if Map(xCount, yCount).Tile = Wall then
				Map(xCount, yCount).Collision = True
			elseif Map(xCount, yCount).Tile = Grass then
				Map(xCount, yCount).Collision = False
			end if
		next
	next
end sub

function CheckFileLines(File as String) as integer
	dim as integer FileNum, FileLines
	dim as string GarbageString
	
	FileNum = FreeFile
	
	open File for input as #FileNum
		do
			input #1, GarbageString
			FileLines = FileLines + 1
		loop until eof(FileNum)
	close #FileNum
	
	return FileLines
end function
	
	
	
	
	
	
	
	
	
sub MoveUp(byref Tank as TankType)
	dim as integer Collision, xTile, yTile
	
	yTile = int(Tank.Y/ 32)
	xTile = int(Tank.X / 32)
	
	Tank.Direction =  North
	Collision = Map(xTile, YTile ).Collision
	
	if Collision = False and Tank.Y - 1 > 0 then
		if Map(int((Tank.X + 30) / 32), yTile).Collision = False then
			Tank.Y = Tank.Y - 1
		end if
	end if
end sub
sub MoveDown(byref Tank as TankType)
	dim as integer Collision, xTile, yTile	
	
	Tank.Direction =  South	
	yTile = int(Tank.Y / 32)
	xTile = int(Tank.X / 32)
	Collision = Map(xTile, yTile + 1).Collision

	if IsCollision(Tank.X, Tank.Y, xTile * 32, (yTile + 1) * 32) <> True and Collision <> True then
		Tank.Y = Tank.Y + 1
	end if
end sub
sub MoveLeft(byref Tank as TankType)
	dim as integer Collision, xTile, yTile	
	yTile = int(Tank.Y/ 32)
	xTile = int(Tank.X / 32)
	Tank.Direction =  West
	Collision = Map(xTile, YTile ).Collision
	
	if Collision = False and Tank.X - 1 > 0 then
		Tank.X = Tank.X - 1
	end if
end sub
sub MoveRight(byref Tank as TankType)
	dim as integer Collision, xTile, yTile	
	yTile = int(Tank.Y/ 32)
	xTile = int(Tank.X / 32)
	Tank.Direction =  East
	Collision = Map(xTile, YTile ).Collision
	
	if Collision = False and Tank.X + 1 < Engine.MaxX - 32 then
		Tank.X = Tank.X + 1
	end if
end sub

 function IsCollision(X1 as integer, Y1 as integer, X2 as integer, Y2 as integer) as integer
	IsCollision = -1
	if (((X1 + 32) < X2) OR (X1 > (X2 + 32)) or _
		((Y1 + 32) < Y2) OR (Y1 > (Y2 + 32))) then
		IsCollision = 0
	end if
end function
Note that you can't compile it without a map file and .bmp files. Download my entire directory (recommended) here -> http://fileanchor.com/78490-d
1000101
Posts: 2556
Joined: Jun 13, 2005 23:14
Location: SK, Canada

Post by 1000101 »

Well, what is the problem? Saying, "I need help," but not mentioning with what isn't very helpful.
{Nathan}
Posts: 301
Joined: Jun 04, 2005 15:18
Location: Ohio
Contact:

Post by {Nathan} »

Sorry for not posting this, but Yetifoot helped me with it on the IRC channel. I got it working.
Stormy
Posts: 198
Joined: May 28, 2005 17:57
Location: Germany
Contact:

Post by Stormy »

Could you share your solution? Would be great for the others.
{Nathan}
Posts: 301
Joined: Jun 04, 2005 15:18
Location: Ohio
Contact:

Post by {Nathan} »

Sure! This is yetifoot's code, with the Engine.TileWidth and Engine.TileHeight variables edited in by me. I hope this can be useful to someone.

Code: Select all

sub MoveUp(byref Tank as TankType)
 
  Dim As Integer tile1_col, tile2_col, tile1_X, tile1_Y, tile2_X, tile2_Y
 
    Tank.Direction = North
 
    tile1_X = Tank.X
    tile1_Y = Tank.Y - 1
    tile2_X = Tank.X + Engine.TileWidth - 1
    tile2_Y = Tank.Y - 1
 
    If (tile1_X >= 0) AND (tile1_Y >= 0) AND (tile2_X >= 0) AND (tile2_Y >= 0) Then
      tile1_col = Map(tile1_X \ Engine.TileWidth, tile1_Y \ Engine.TileHeight).Collision
      tile2_col = Map(tile2_X \ Engine.TileWidth, tile2_Y \ Engine.TileHeight).Collision
      If (tile1_col = FALSE) AND (tile2_col = FALSE) Then
        Tank.Y -= 1
      End If
    End If
 
end sub
 
sub MoveDown(byref Tank as TankType)
 
  Dim As Integer tile1_col, tile2_col, tile1_X, tile1_Y, tile2_X, tile2_Y
 
    Tank.Direction = South
 
    tile1_X = Tank.X
    tile1_Y = Tank.Y + Engine.TileHeight
    tile2_X = Tank.X + Engine.TileWidth - 1
    tile2_Y = Tank.Y + Engine.TileHeight
 
    If (tile1_X >= 0) AND (tile1_Y >= 0) AND (tile2_X >= 0) AND (tile2_Y >= 0) Then
      tile1_col = Map(tile1_X \ Engine.TileWidth, tile1_Y \ Engine.TileHeight).Collision
      tile2_col = Map(tile2_X \ Engine.TileWidth, tile2_Y \ Engine.TileHeight).Collision
      If (tile1_col = FALSE) AND (tile2_col = FALSE) and Tank.Y < Engine.MaxY - Engine.TileHeight Then
        Tank.Y += 1
      End If
    End If
 
end sub
 
sub MoveLeft(byref Tank as TankType)
 
  Dim As Integer tile1_col, tile2_col, tile1_X, tile1_Y, tile2_X, tile2_Y
 
    Tank.Direction = West
 
    tile1_X = Tank.X - 1
    tile1_Y = Tank.Y
    tile2_X = Tank.X - 1
    tile2_Y = Tank.Y + Engine.TileHeight - 1
 
    If (tile1_X >= 0) AND (tile1_Y >= 0) AND (tile2_X >= 0) AND (tile2_Y >= 0) Then
      tile1_col = Map(tile1_X \ Engine.TileWidth, tile1_Y \ Engine.TileHeight).Collision
      tile2_col = Map(tile2_X \ Engine.TileWidth, tile2_Y \ Engine.TileHeight).Collision
      If (tile1_col = FALSE) AND (tile2_col = FALSE) Then
        Tank.X -= 1
      End If
    End If
 
end sub
 
sub MoveRight(byref Tank as TankType)
 
  Dim As Integer tile1_col, tile2_col, tile1_X, tile1_Y, tile2_X, tile2_Y
 
    Tank.Direction = East
 
    tile1_X = Tank.X + Engine.TileWidth
    tile1_Y = Tank.Y
    tile2_X = Tank.X + Engine.TileWidth
    tile2_Y = Tank.Y + Engine.TileHeight - 1
 
    If (tile1_X >= 0) AND (tile1_Y >= 0) AND (tile2_X >= 0) AND (tile2_Y >= 0) Then
      tile1_col = Map(tile1_X \ Engine.TileWidth, tile1_Y \ Engine.TileHeight).Collision
      tile2_col = Map(tile2_X \ Engine.TileWidth, tile2_Y \ Engine.TileHeight).Collision
      If (tile1_col = FALSE) AND (tile2_col = FALSE) and Tank.X < Engine.MaxX - Engine.TileWidth Then
        Tank.X += 1
      End If
    End If
 
end sub 
TankType is as follows:

Code: Select all

type TankType
	Sprites(1 to 4) as any ptr							'The North, South, East, and West sprites. The actual value is a pointer
	hp 			as integer							'The tank's hit points. Starts at 9.
	Direction		as integer							'The direction of the tank. Actual values are 1, 2, 3, 4 but accessed with the North, South, East, and West constants.
	x			as integer							'Where the tank is in the world
	y			as integer							'Where the tank is in the world
	xScreenCords 	as integer							'Where the tank actually is on the screen
	yScreenCords 	as integer							'Where the tank actually is on the screen
	StartTime		as integer							'The last time the tank fired. There is a five reload time.
	CanFire		as integer							'Can the tank fire? Defined by True and False constants.
end type
yetifoot
Posts: 1710
Joined: Sep 11, 2005 7:08
Location: England
Contact:

Post by yetifoot »

Here are my thoughts as to how I solved the problem, that may help more than just reading the raw code.

The map is set up in 32x32 tiles,
The tank itself is a 32x32 tile, but can move in pixel steps.

So for any direction the tank can move in, there are two possible tiles it can collide with, the raw pixel positions of these collisions are tile1, and tile2 in the code.

These are first checked to make sure they fit within the bounds of the map, in nathans code these are the checks for >=0, there should also be checks for < map_width * tile_width for the X, and < map_height * tile_height for the Y

This is to stop movement being allowed off the map, and to make sure also that when they are divided by tile_width to get the position in the map, no out of bounds checking of the map() array will occur.

Each map array element has a collision element, that is 1 if that map tile is a wall or other collidable tile.

Only once it has been satisfied that the potential movement will not go out of map bounds, and will not collide with any wall type tiles, will the movement occur.
Post Reply