Collision detection with a tilemap- raping the walls

Started by
12 comments, last by Trienco 12 years, 10 months ago
I am using Java to make a 2d platformer, and I am having trouble with the enemies. I am using rectangles and the intersects method to detect collisions. I just want the enemy to run back and forth between two bricks.
Here is my code:
public void checkCollision() {
for (int i = 0; i < map.tiles.size(); i++) { //map.tiles is an ArrayList of the tiles on the map and size() is how many tiles are on the map, we are looping through each tile
Tile temp = map.tiles.get(i); //temp is the handle to the tile we are on (not physically on, but what tile we are on in the loop)
if (map.tiles.get(i).index != 0) { //checking if the tile is not blank, if there is actually a brick or whatever there
if (bounds.intersects(temp.bounds)) { //bounds is the rectangle that encapsulates the object. It is used for detecting intersections.
if (getX()+32>temp.x) //getX(), the x coordinate of the object, temp.x, the x coordinate of the current tile
setX(getX()-1); //push the enemy outside of the block, so he doesn't collide with it again.
if (getX()<temp.x+32)
setX(getX()+1); //same, but in opposing direction if he is going the other way
hspeed = hspeed * -1; //reverse the horizontal speed
}
}
}
}


The enemy is suffering from what I call 'rape the wall syndrome', meaning he is just bouncing back and forth, on a single brick like this when he runs into one:
[~] = enemy
[_] = brick
vertical order of frames:
[~] [_]
[~][_]
[~]_]
[~][_]
[~] [_]
[~][_]
[~]_]
[~][_]
[~] [_]
and repeat.

More about the enemy- he is a 32x32 square, and he is raised one pixel above the tiles on the ground so as not to collide with them, and have him reverse his direction in the same spot. In his 'main loop' his X coordinate is updated by his vertical speed like so:
setX(getX()+hspeed)
I don't know why he is doing this, and I don't know how to solve this. Thanks in advance to whomever can help me find the solution.
Advertisement
Man not trying to be harsh but drop the list totally first.
This will cause LOTS of problems with Logic, Performance and Memory!

Lets say you want to have a level the size of Super Mario Bros 1-1:
SuperMarioBros-World1-1.png

Memory:
This level contains 5936 blocks!
If thier represented by ID's that would only be 5936 bytes

Performance:
Meaning for everything on screen that's moving you are going to have loop running that loops with all those statements 5936 times!
Again in the Mario level you would be lucky to get 60fps even with something this simple.

Implementation:
The best way to do this is to have a [color="#0000FF"]unique id for each tile.
A great practive for collision would be to have [color="#0000FF"]0 [color="#000000"]for an empty block.
Then have a [color="#0000FF"]2d Array [color="#000000"]and store all the id's in the array.

Drawing:
Basically when drawing have 2 for loops to draw the level, very simple.

Very Fast! Collision Routine:
(This routine will always take the same amount of time,
while the above routine gets Exponencially slower when the number of blocks increases)
Then when checking for collision just:
Take the coordinate of the enemy and say for example

bool collide()
{
if (tile[enemy.x-1,enemy.y] != 0)
{
return true;
}
else if (tile[enemy.x+1,enemy.y] != 0)
{
return true;
}
else
{
return false;
}
}


If this was helpful please Thumbs Up this post.

CoderWalker
If this post was helpful please +1 or like it !

Webstrand
Well, what if he is not moving an entire block over every frame? He is only moving two pixels, and your method suggests that there is a block at each pixel, but there is a block at every 32 pixels. Also, the enemy (and the player) are rectangles, not pixels.
Most games use the tiles for [color="#0000FF"]static objects (ones that dont move).
Sorry I didn't mention this.

Meaning the enemy would not be drawn with the map.
if each tile is 32x32.

The solution is:
tile.x = enemy.x/32;
tile.y = enemy.y/32;

Since this is integer division they will get "Snapped" to 32x32 positions

ex:
1 = 36/32;
0 = 14/32;

then check tile[1][0]

Anything that is [color="#0000FF"]Dynamic (not snapped to the map) should:
Draw itself
Have Actual Pixel coordinates instead of tile coordinates

What I talked about above was implemented in many games Mario, Zelda, Donkey Kong.

Evidence:

Attached to map?
--------------------------
Link - No
Soldier - No
Weapon - No
Grass - Yes
Dirt - Yes
Fence - Yes
Plant - Yes
link-to-the-past.jpg
If this post was helpful please +1 or like it !

Webstrand
Codewalker's advice is good. I'd take it.

[size="1"]@codewalker: Asking for members to rate you up is like asking a girl to get in your pants. The chances of it working (for free) are negligible. Removing any suggestion to it from your post will increase your chances.
[size="2"]I like the Walrus best.


if (getX()+32>temp.x) //getX(), the x coordinate of the object, temp.x, the x coordinate of the current tile
setX(getX()-1); //push the enemy outside of the block, so he doesn't collide with it again.
if (getX()<temp.x+32)
setX(getX()+1); //same, but in opposing direction if he is going the other way



Are you sure +/- 1 is going to be enough to not still be stuck and just causing collisions (or rather intersections) every single frame? You could also consider the amount of movement and how much of that was used to get to the block and how much is left and should be used to move away from the block (at least pretend the world doesn't stand still between frames).

Since I have no idea if your getX returns the left side or the center, I'll take a guess that X is left and 32 is the size of the object AND block (consider using a named const instead of magic numbers).

So,

insideX = getX() + 32 - temp.x

is how far inside the block you ended up. Now either your new position should be

setX(temp.x - insideX)

(since you should bounce off when you HIT the block, not when you've already moved halfway into it)
or at the very least

setX(temp.x) (-1 if you want to be on the safe side).
f@dzhttp://festini.device-zero.de
Very nice! Now I don't have to rewrite my entire game code. But now he bounces one direction but goes straight through blocks in the other direction.

Very nice! Now I don't have to rewrite my entire game code. But now he bounces one direction but goes straight through blocks in the other direction.


How did you adjust the lines for the other direction? The code above only handles collision to the right.
f@dzhttp://festini.device-zero.de
Oddly enough I was bored yesterday and made this. Right click view source and look at the update code to see how to handle tile collision and response between an entity. It might help you to see what your problem is. In regards to an enemy moving left until he hits the edge and reversing you just do Math.floor(entity.Position / tileSize) or in C++:
int tileX = static_cast<int>(entity.x / tileSize);
int tileY = static_cast<int>(entity.y / tileSize);
if (!map[tileX - 1][tileY + 1].Collidable)
{
// Set internal minimum move left to tileX * tilesize which is the far left of the current tile the entity is on.
}
For moving right:
if (!map[tileX + 1][tileY + 1].Collidable)
{
// Set internal minimum move left to tileX * tilesize + tileSize which is the far left of the current tile the entity is on.
}

Also if your enemy collides with a tile and the minimum translation distance for x is > 0 then just set direction to right and if it's < 0 set it to left.
Okay. Solved. Although I think the best way would have been the 2d array of tiles, and the separation of object-space and tile-space, however as to avoid a complete rewrite, my new working code is as follows:

public void checkCollision() {
for (int i = 0; i < map.tiles.size(); i++) {
Tile temp = map.tiles.get(i);
float insideX;
if (map.tiles.get(i).index != 0) {
if (bounds.intersects(temp.bounds)) {
if (hspeed > 0 && !collided) {
insideX = (getX() + 32) - temp.x;
setX(temp.x - insideX - 32);
hspeed = -1;
collided = true;
break;
}
if (hspeed < 0 && !collided) {
insideX = getX() - temp.x + 32;
setX(temp.x + insideX - 24);
hspeed = 1;
collided = true;
break;
}
collided = false;
}
}
}
}

This topic is closed to new replies.

Advertisement