Tile collision help

Tile collision help

Postby Pix3l » 19 Oct 2010, 21:54

Hello ppl :]

I'm in a little trouble with a tile collision engine. It seem something in my logic are wrong but i can't figure it out...

This is the (horrible) code i wrote in C
{l Code}: {l Select All Code}
//gcc -o maze maze.c -lSDL

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <time.h>

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 320

#define MAX_MAP_X 10
#define MAX_MAP_Y 10

#define BLANK_TILE 0
#define TILE_SIZE 32
#define PLAYER_SPEED 4
#define TIME_STOP 2000

    //Global variables
    int t, tl = 0, frequency = 1000 / 100, temp, t2; //StopBall variables
    int repeat, i, quit=0, paused=0, shoot=0;
    SDL_Event event;
    SDL_Surface *screen, *textSurface;
    SDL_Rect rect; //Main drawing surface
    Uint8 *keystate; // keyboard state
   
int matrice[10][10] =
{
  0,0,1,1,1,1,1,1,1,1,
  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,
  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,
  1,0,0,0,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1,
}; 

struct _Player
{
   int w, h, onGround;
   int thinkTime;
   int x, y, dirX, dirY;
} Player;

struct _Character {
   int x,y;
   int dir;
   int delay;   
} Character;

struct _Game
{
   int player_score, cpu_score, status, players, difficult;
} Game;

void restartTimer()
{
   //Reset frame per screen counter
   t = tl = temp = SDL_GetTicks();
}

int Reset()
{   
   ////Player start position
    //Player.x = SCREEN_WIDTH/2;
    //Player.y = SCREEN_HEIGHT/2;
}

void checkToMap(struct _Player *e);

void DrawRect(int x, int y, int width, int height, int color)
 {
       rect.x = x;
      rect.y = y;
       rect.w = width;
       rect.h = height;
       SDL_FillRect(screen, &rect, color);
 }

void drawbackground()
 {
  int i, j;
  for (i = 0; i <= 10; i++)
  {
    for (j = 0; j <= 10; j++)
    {
      if(matrice[i][j]==0) DrawRect(j * TILE_SIZE, i * TILE_SIZE, TILE_SIZE, TILE_SIZE, 0xffffff);
    }
  }   
 }

void Draw()
{
   //Draw Maze
   drawbackground();
   //Draw Player
   DrawRect(Player.x, Player.y, TILE_SIZE, TILE_SIZE, 0xff0000);
}

int getInput()
{
     int x, y;
   
    x = Player.x;
    y = Player.y;
   
    x/=TILE_SIZE;
    y/=TILE_SIZE;
   
 // Grab a keystate snapshot
 keystate = SDL_GetKeyState( NULL );
 /* Handle key presses */
 //The paddles can move only if there's nothing on their way
 if (keystate[SDLK_LEFT])
  {
   if(matrice[x-1][y]!=1)
    Player.x--;
   printf("x %d y %d\n", x, y);
   printf("%d \n", matrice[x-1][y]);
  }
 if (keystate[SDLK_RIGHT])
  {
     
     if(matrice[x+1][y]!=1)
     {
     Player.x++;
     printf("x %d y %d\n", x, y);
     }
  }
 if (keystate[SDLK_UP])
  {
     if(matrice[x][y-1]==0)
     Player.y--;
     printf("x %d y %d\n", x, y);
     printf("%d \n", matrice[x-1][y]);
  }
 if (keystate[SDLK_DOWN])
  {
     if(matrice[x][y+1]==0)
     Player.y++;
     printf("x %d y %d\n", x, y);
     printf("%d \n", matrice[x-1][y]);
  }
}

int fps_sync ()
{
   t = SDL_GetTicks ();
   //printf("tl: %d t: %d\n", tl, t);
   if (t - tl >= frequency)
   {
      temp = (t - tl) / frequency; //delta time
      tl += temp * frequency;
      //printf("temp: %d tl: %d \n", t, tl);
      return temp;
   }
   else
   {
      SDL_Delay (frequency - (t - tl));
      tl += frequency;
      return 1;
   }
}

void logicLoop()
 {
  if(!paused)
   getInput();
 }

int mainLoop()
 {
  //main game loop
  while(!quit)
   {
      SDL_PollEvent(&event);
   repeat = fps_sync ();
      for (i = 0; i < repeat; i ++)
       {
         if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))
          quit = 1; /* Window closed */
           
            switch(Game.status)
             {
                case 0: //Menu
                 logicLoop();
                 break;
             }
       }
      
      //clear the screen
      SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
      //draw to the screen
      Draw();
      //update the screen
      SDL_Flip(screen);
   }
 }

int main()
{
  // init video stuff
   // init screen
   screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_SWSURFACE);
   if(screen == NULL)
   {
      fprintf(stderr, "Can't initialize SDL: %s\n", SDL_GetError());
      exit(-1);
   }

   SDL_WM_SetCaption("Maze", "Maze");
   
   //Game status 0 means Game Menu
   Game.status=0;
   Game.players=1;
   Game.difficult=1;
   Reset();
   printf("Start position in matrix: x %d y %d\n", Player.x/32, Player.y/32);
   
   Player.x = Player.x * TILE_SIZE;
   Player.y = Player.y * TILE_SIZE;
   
   //main game loop
   mainLoop();
   printf("Thanks for playing!\n");
      SDL_Quit();
   return 0;
}


Can someone help me or just let me know if there is any C based tutorial on tile collision for games?

Sorry for my bad english ^^"
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Re: Tile collision help

Postby amuzen » 20 Oct 2010, 06:39

Your problem is in how x and y are calculated and used in getInput(). If the screen position of the player is (32, 63), for example, the tile in which the top left pixel of the player lies is (1, 1), which is your (x, y). When you now index the map with (x-1, y), you test against tile (0, 1), which looks about right. However, if you index with (x, y-1), you test against tile (1, 0) even though the player is actually still 31 pixels away from it. Also, your map is in [Y][X] format but you index it with [X][Y] in getInput(), though that's pretty minor with the current map.

In any case, you basically need to calculate the destination tile coordinates with "x = (Player.x + dx)/TILE_SIZE" instead of "x = Player.x / TILE_SIZE + dx". That should work, though only for 1x1 sprites since you only do collision detection for the top left pixel. I tried to write you something that works for larger sprites too. Consider my changes public domain.

{l Code}: {l Select All Code}
int tileCollision(int x, int y, int w, int h)
{
    int i, j;
    int minx, miny, maxx, maxy;
    // Return a collision if the rectangle is outside of the map.
    if (x < 0 || (x + w) > TILE_SIZE * MAX_MAP_X ||
        y < 0 || (y + h) > TILE_SIZE * MAX_MAP_Y)
        return 1;
    // The passed coordinates are in pixels so convert them to tiles.
    minx = x / TILE_SIZE;
    miny = y / TILE_SIZE;
    maxx = (x + w - 1) / TILE_SIZE;
    maxy = (y + h - 1) / TILE_SIZE;
    // Return a collision if the rectangle intersects with a tile.   
    for (i = minx; i <= maxx ; i++)
    {
        for (j = miny ; j <= maxy ; j++)
        {
            if (matrice[j][i])
                return 1;
        }
    }
    // No collision otherwise.
    return 0;
}

int getInput()
{
     int x, y;
   
    x = Player.x;
    y = Player.y;
   
// Grab a keystate snapshot
keystate = SDL_GetKeyState( NULL );
/* Handle key presses */
//The paddles can move only if there's nothing on their way
if (keystate[SDLK_LEFT])
  {
   if(!tileCollision(x-1, y, TILE_SIZE, TILE_SIZE))
    Player.x--;
  }
if (keystate[SDLK_RIGHT])
  {
     
     if(!tileCollision(x+1, y, TILE_SIZE, TILE_SIZE))
     {
     Player.x++;
     }
  }
if (keystate[SDLK_UP])
  {
     if(!tileCollision(x, y-1, TILE_SIZE, TILE_SIZE))
     Player.y--;
  }
if (keystate[SDLK_DOWN])
  {
     if(!tileCollision(x, y+1, TILE_SIZE, TILE_SIZE))
     Player.y++;
  }
}
User avatar
amuzen
LoS Moderator
 
Posts: 327
Joined: 05 Dec 2009, 02:49

Re: Tile collision help

Postby Pix3l » 20 Oct 2010, 10:35

This works great :D
But i have few questions, first:

You said
your map is in [Y][X] format but you index it with [X][Y]
, i think 2D array are row * columns, why you said this one is columns * rows? (yeah, maybe it's a stupid question ._.)

Second, what are that for loop at the end of collision function doing? I see that it takes min X and Y and check those value in the matrix but i can't understand the need of this one.

Anyway, thanks for you help. I really appreciate it :]
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Re: Tile collision help

Postby amuzen » 20 Oct 2010, 11:52

Pix3l {l Wrote}:i think 2D array are row * columns, why you said this one is columns * rows?


Well, it can be used in both ways since. It's just a matter of convention and swapping X and Y. You need to be consistent, though, and since your drawing code basically does the equivalent of matrice[y][x], you can't do matrice[x][y] in the collision detection code:

{l Code}: {l Select All Code}
if(matrice[i][j]==0) DrawRect(j * TILE_SIZE, i * TILE_SIZE, TILE_SIZE, TILE_SIZE, 0xffffff)


Pix3l {l Wrote}:Second, what are that for loop at the end of collision function doing? I see that it takes min X and Y and check those value in the matrix but i can't understand the need of this one.


When you have a sprite larger than one pixel, it can intersect with multiple tiles. For example, in the case of your application, when the top left corner of the player is at (48,16), it intersects with tiles (1,0), (2,0), (1,1), (2,1). You'd have minx=1, maxx=2, miny=0, maxy=1, and the loop would test for collisions against all the four tiles that intersect with the rectangle of the sprite. If you removed the loop and only checked for the tile at (minx,miny), the sprite could go 31 pixels inside walls moving right or down or when walking into L or T-shaped walls.
User avatar
amuzen
LoS Moderator
 
Posts: 327
Joined: 05 Dec 2009, 02:49

Re: Tile collision help

Postby Pix3l » 20 Oct 2010, 20:00

Oh well, i understand, the two for loops check four time for collision, first time for (1,0), second time for (2,0) and so on...
Now the game works right, thanks pal :]
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Who is online

Users browsing this forum: No registered users and 1 guest