0
\$\begingroup\$

I have a player whose who's sprite is 48* 32. I know the player's position. The player can move up and down based on user input.
I have a tilemap which moves. Here is an example of the array I use to draw the tilemap.

int[][] map0 = {
                        new int[]{1 ,2 ,1 ,2 ,1 ,2 ,1 ,17 ,1 ,2 ,12 ,13 ,1 ,2 ,1 ,2 ,8 ,9 ,10 ,11 ,1 ,2 ,1 ,2 ,1 ,2 ,1 ,2 ,1 ,2 },
                        new int[]{29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 ,29 ,30 },
                        new int[]{31 ,32 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,78 ,98 ,99 ,79 ,0 ,0 ,0 ,0 ,0 ,98 ,99 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,37 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,95 ,96 ,96 ,96 ,96 ,97 ,0 ,0 ,0 ,0 ,100 ,101 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,38 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,102 ,103 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,32 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,32 ,66 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{35 ,36 ,67 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{41 ,42 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{41 ,42 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{41 ,42 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{41 ,42 ,64 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{33 ,34 ,67 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,32 ,0 ,0 ,66 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,37 ,0 ,0 ,104 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,38 ,0 ,0 ,105 ,0 ,0 ,0 ,108 ,109 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,108 ,109 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,32 ,0 ,0 ,106 ,0 ,0 ,0 ,110 ,111 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,110 ,111 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{31 ,32 ,0 ,0 ,107 ,0 ,0 ,0 ,56 ,57 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,56 ,57 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 },
                        new int[]{26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 ,26 },
                        new int[]{27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 ,27 },
                        };

Here is some code in my draw method:

for (int y = 0; y < map0.GetLength(0); ++y) {
    for (int x = 0; x < map0[y].Length; ++x) {
        sourceRect.X = 16 * map0[y][x];
        tilePos.X = 16 * x;
        tilePos.Y = 16 * y;
        spriteBatch.Draw(texture, tilePos + mapPos, sourceRect, Color.White);
    }
}

Each tile is 16*16. The texture is a sprite sheet of tiles arranged in a horizontal position. map0[y][x] gives the int in the map array. I multiply it with 16 to get the x coordinate of the tile in the sprite sheet. So the for loop draws the map on screen. In the update method I do mapPos.X += -1f; so that the whole map moves across the screen.
Problem: So my problem is how to detect collision between each tile in the tile map.(0 means transparent tile which the player cannot collide) I have tried looping through the whole map and check if player collides with a tile by using many if statements and coordinates. But this makes the game's fps go to 1.
Can someone suggest me a way to do this without freezing the game? Map and game looks like this

\$\endgroup\$
  • \$\begingroup\$ If you know the position of the player. Why not just check that position relative to the map? \$\endgroup\$ – SchautDollar Feb 17 '15 at 13:05
2
\$\begingroup\$

To check your collision you simply take the players position and divide it with the size of each map element to get the index of where the player is in the map, then check for collision surrounding that. The player is 48x32 and the map is 16x16 so (48,32) / (16,16) = (3,2).

But since the player can move smoothly you need to add one more coordinate to each direction so you check both the tile he is half into to the left as well as the one to the right.

// Vector2 has overloaded division and addition functions so that:
// Vector2.One + Vector2.One is the same as new Vector2(1+1, 1+1)
// new Vector2(4, 9) / new Vector2(2,3) is the same as new Vector2(4/2, 9/3)
var playerTileSize = playerSize / tileSize + Vector2.One
var playerTilePos = playerPosition / tileSize

// But Vector2 holds float values and we are going to be using it with array
// indices, so lets extract the x and y components, width and height don't really
// need the ceiling call here since the player sprite is an exact multiple of the
// block sprites, but if you ever change the sprites size this code will keep working
int w = (int)Math.ceiling(playerTileSize.x)
int h = (int)Math.ceiling(playerTileSize.y)
// As for the offset, we want to make sure we grab the topleft index so we want
// to remove the decimal portion entirely. Of course (int) already does that,
// but I wanted to make it explicit that it is what we want to do.
int offsetX = (int)Math.floor(playerTilePos.x)
int offsetY = (int)Math.floor(playerTilePos.y)

// Now simply go through all the cells in the map that the player is covering in whole
// or partially, we can simply go over the width and height of the player sprite in the
// for loops and add the offset value we calculated above, it makes the for loops a 
// little neater and easier to read
for (int y = 0; y < w; y++)
  for (int x = 0; x < h; x++)
    // And finally we need to "check the collision"
    checkCollision(map0[y + offsetY][x + offsetX])

In your question you said that it's a collision if the map0[y][x] is anything but 0, so we can implement checkCollision with this rule as follows:

public static bool checkCollision(int mapTile) {
  return mapTile != 0;
}

This way you can change the collision detection later if you want. For instance, say you want tile 128 to be darkness but still not cause collisions, then you only have to change that one place instead of remembering to look for it everywhere.

I have a player whose who's sprite is 48* 32. I know the player's position.

I can only assume this statement means you know the position on the screen. It is a common start for developers to think of it in terms of screen position, but it makes more sense to describe the world, and the players position in it, in terms of its own coordinate system that is mostly unrelated to the screen position.

For instance you would have the players position in relation to the top left corner of the map and treat the shifting map as if a wall is coming up from behind the player, when the player touches the wall they die (at least that is how I guess your scrolling map would work).

spriteBatch.Draw(playerTexture, playerPos + mapPos, ...);

And for the death condition (in update)

if (playerPos.x - mapPos.x <= 0)
  playerDied();
\$\endgroup\$
  • \$\begingroup\$ Can you please comment your code? Actually this is my first game so I know very little about game development. \$\endgroup\$ – Winger Sendon Feb 17 '15 at 17:21
  • \$\begingroup\$ @EmpereurAiman Of course, I added comments to the code. Is it clearer now? \$\endgroup\$ – Daniel Carlsson Feb 17 '15 at 20:23
  • \$\begingroup\$ Are you sure that looping through the whole map in each update and check for collision with each tile will not slow down the game? \$\endgroup\$ – Winger Sendon Feb 18 '15 at 12:37
  • \$\begingroup\$ @EmpereurAiman My comment wasn't so clear, the code only checks the map squares that the player is partially or wholly covering. I updated the answer to clarify that. As you can see the code only checks the 4x3 blocks that the player occupies so it only checks 12 coordinates, not the entire map as the comment claimed at first. \$\endgroup\$ – Daniel Carlsson Feb 18 '15 at 12:45
  • \$\begingroup\$ Thanks, I will try to see if this works tomorrow and I will update :) Thank you so much for your time \$\endgroup\$ – Winger Sendon Feb 18 '15 at 19:17
2
\$\begingroup\$

I have a tilemap which moves

First of all you start from wrong way. Player should move inside world and not world should mo around player. It's not a Futurama. In other words do

player.Position.X += dx

instead of

world.Position.X -= dx

It's mutch cleaner solution.

Next. Let's you have viewRect that is your visible part of world

Rectangle viewRect = new Rectangle(0, 0, screen_width, screen_height);

And you have your player position of tyte Vector2. You can simply calculate current view:

Rectangle currentViewRect = new Rectangle(
    viewRect.X + player.Position.X + drawOffset.X,
    viewRect.Y + player.Position.Y + drawOffset.Y,
    viewRect.Width,
    viewRect.Height);

Then you can determine visible tiles like I in my answer on this question.

In the same way you can determine tiles that you characted potentialy can intersetc and check only them. You use grid for keep tiles, so if you have coord in pixels you can find coord of grid cell.

Vector2 minTileCoord = new Vector2(
    (int)player.Position.X / tileWidth,
    (int)player.Position.Y / tileHeight);
Vector2 maxTileCoord = new Vector2(
    (int)(player.Position.X + player.Width / tileWidth,
    (int)(player.Position.Y + player.Height) / tileHeight);

And now you need only check tiles from maxTileCoord.X to maxTileCoord.Y by X and maxTileCoord.Y to maxTileCoord.Y by Y axis.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.