Archived

This topic is now archived and is closed to further replies.

ferdkuh

map collision detection

Recommended Posts

ferdkuh    122
hi there. be prepared for a confusing question I've read all articles i foun and also looked through the whole turbo section of this board but I could not find an answer to my question. I think I should describe my whole situation first: I'm trying do create a small tile-based "game" with the help of delphix (you can't really call it game, it's just for fun) I created a map and saved it in a text file which looks like this for example: 1111 1001 1111 I load this file into a multi-dimensional array. (I think I haven't made too many mistakes till now) To draw the map on the screen, I wrote a procedure which parses the array and draws the correspondending tile to the screen using the imagelist.items.draw procedure. Is it right to use the draw procedure ? Or should I create a sprite for every till ? Anayway, I've put this procedure into the dxtimer.ontimer event. (I read somewhere that it would be better to draw it just once to another surface and copy it everytime to the 'main-one', but I've no idea how I could do this and my way works, but if it's better and you know how to do it, please tell me !) And now here comes my real problem: Of course the player can't walk through walls and so on. So I have to do collision detection, and since the map tiles aren't sprites I have to write my own. I've tried and tried but I didn't get a working one. Here's my collision detection for the "right" direction for example: if (player.x = (player.mapx+1)*32-32) and (level.map[player.mapx+1,player.mapy] = 2) then beep else player.x := player.x + 4; player.mapx := round((player.x - 32) / 32) + 1; the first if-expression checks if the player has reached the position of the next tile. The second expression checks if the next tile is walkable or not (tile 2 is not walkable). if a collision occurs, beep is fired. the third line calculates the players position on the map. I hope this is enough to understand, but tell me if i shall explain further. This works fine as long is you're always moving straight towards the "blockers". But if you don't, my collision detection doesn't work. Let's say the player is moved just 4 pixels down from row three. he touches row four a little bit. my game will think he's on row three (because of the "round((player.x........"))) And what happens if there's a block in row four ? The player will walk through it. I've made a screenshot to show what I want to express, because I'm really not sure if my English is good enough to describe what i want to say. Click here for screenshot I have absolutely no idea how to solve this problem. I guess my whole "collision detection" is silly and not usable. But I have no idea how to do it "right". I would be really thankful if anybody could help me with any of my problems. btw, if ANYTHING in my explanation sounds silly to you, please tell me how to do it a better way. Thank you in advance, Jonas Edited by - ferdkuh on August 8, 2001 10:02:17 AM Edited by - ferdkuh on August 8, 2001 10:03:24 AM

Share this post


Link to post
Share on other sites
Xorcist    122
Looks like you'll want to use the "four corners" technique. What you want to do is check each corner of your sprite for a collision with a wall tile. This is best set up in a boolean return function, something like this:

{Let's assume 1 is used to denote a wall}
    
function HitWall(Sprite: TSprite; Map: TMap;): Boolean;
begin

//Hitwall returns false by default

Hitwall := false;

//Check Upper-Left Corner Collision

if Map.Grid[Sprite.X div Map.Tile.Width][Sprite.Y div Map.Tile.Height] = 1 then
Hitwall := true;

//Check Upper-Right Corner Collision

if Map.Grid[(Sprite.X + Sprite.Width) div Map.Tile.Width][Sprite.Y div Map.Tile.Height] = 1 then
Hitwall := true;

//Check Lower-Left Corner Collision

if Map.Grid[Sprite.X div Map.Tile.Width][(Sprite.Y + Sprite.Height) div Map.Tile.Height] = 1 then
Hitwall := true;

//Check Lower-Right Corner Collision

if Map.Grid[(Sprite.X + Sprite.Width) div Map.Tile.Width][(Sprite.Y + Sprite.Height) div Map.Tile.Height] = 1 then
Hitwall := true;

end;


* Keep in mind if you are using DelphiX's intrinsic sprites you might need to tuncate your sprite X and Y coordinates before attempting to div them as such: (trunc(Sprite.X) div Sprite.Width)

* Note that the Hitwall function expects your sprite's width and height to be one less than that of your actual width and height. So if your Sprite's width is 16, that's actually 0 to 15, so the Sprite.Width used in the function should be 15 (just subtract one if necessary). If you use 16, and for example your tiles are 16x16, and let's say you are moving east, and you encounter a map section where there is a wall directly above your sprite, and directly below your sprite, you will be stopped. When instead you should have been allowed to pass through the opening between to top and bottom walls. (Did that make sense?)


Anyways now you can use the Hitwall function to check whether or not the next move you make should actually happen, as such:


var OldX,OldY: Double;
.
.
.
//Save Previous Coordinates

OldX := Pacman.X;
OldY := Pacman.Y;
//Attempt to move Sprite

Pacman.Move;
//If Sprite hit a wall return it

//to it's previous coordinates

if Hitwall(Pacman, Board) then begin
Pacman.X := OldX;
Pacman.Y := OldY;
end;
.
.
.


Hope that helps... if you need more info feel free to ask.


Edited by - Xorcist on August 8, 2001 5:00:37 PM

Share this post


Link to post
Share on other sites
ferdkuh    122
hm, sounds really logical. I''m going to try it as soon as possible ! It''s really interesting how "simple" this problem can be solved. If you just get the idea ...
Thank you very much for your answer, you helped me alot !

Share this post


Link to post
Share on other sites