2d p*p collision detecting

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

2d p*p collision detecting

Postby {Nathan} » Nov 11, 2006 19:38

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

Postby 1000101 » Nov 12, 2006 3:29

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:

Postby {Nathan} » Nov 12, 2006 19:20

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:

Postby Stormy » Nov 17, 2006 9:02

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

Postby {Nathan} » Nov 17, 2006 20:47

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:

Postby yetifoot » Nov 17, 2006 22:02

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.

Return to “Game Dev”

Who is online

Users browsing this forum: No registered users and 4 guests