Recently, I’ve been asked twice about how to check if there’s something obstructing the path from (x1;y1) to (x2;y2). Here I’ll provide two implementations equally useful – to check if there’s something obstructing the way for a player (wall or obstale) and to check if there’s something obstructing the way for a bullet (wall).
First, let’s use our handy function for moving objects forward (refer to this post). As will be demonstrated next, it’s not only used for that.
So, we inserted the function into the code – now let’s do the functions that check for obstructions.
function checkfreepath(x1,y1,x2,y2) local rot=-math.deg(math.atan2(x1-x2,y1-y2)) local cx,cy=x1,y1 while math.floor(cx/32)<=math.floor(x2/32) and math.floor(cy/32)<=math.floor(y2/32) do if not tile(math.floor(cx/32),math.floor(cy/32),'walkable') then return false end cx,cy=angledir(rot,cx,cy,32) end return true end
The function checkfreepath checks if the path for a player is free. It takes in four arguments – x1 and y1 are the coordinates of the initial position, and x2 and y2 are the coordinates of the terminal position.
It functions following this algorithm:
- Find the angle between the two positions
- Declare local variables for current X and Y
- Run a while loop to execute code while the current X and Y being checked aren’t beyond where we want it to check
- Check if the tile being checked is walkable – if it’s not, we end the function and return false
- Move the current position checked by 32 pixels (1 tile) forward
- If the function hasn’t ended already – meaning the path is free – return true
Pros and cons:
- Pro: the while loop checks for floored positions divided by 32 and the angledir function moves the current position by 32 to increase efficiency
- Con: the function accepts pixels as the coordinate measurement – with tiles supplied the function will not work
Knowing the big turndown of our function, we could make it versatile, adding a fifth argument being the mode of the function. The code would look like so:
function checkfreepath(x1,y1,x2,y2,tiles) local f=1 if tiles then f=32 end local rot=-math.deg(math.atan2(x1-x2,y1-y2)) local cx,cy=x1,y1 while math.floor(cx/f)<=math.floor(x2/f) and math.floor(cy/f)<=math.floor(y2/f) do if not tile(math.floor(cx/f),math.floor(cy/f),'walkable') then return false end cx,cy=angledir(rot,cx,cy,f) end return true end
As you can see, there are new lines as well as some minor changes on the highlighted lines. The factor now isn’t 32 – it’s f, which is a local variable we declared on line 2. On line 3, we check if tiles is specified – if the tiles argument is anything except nil and false, the factor automatically sets to 1, which means the measurement is tiles. Otherwise, the factor is set to 32, and the measurement becomes pixels.
Good luck!
This really could be improved.
Take the following (untested) code:
http://www.cs2dinfo.com.ar/pastebin/index.php/view/45218860
It drastically reduces the number of iterations
(x1-x2)^2+(y1-y2)^2)^0.5 iterations vs math.max(x1-x2, y1-y2) iterations
Remember that the hypotenuse is always greater than any of the cathetus.
Moreover, it avoids using expensive functions such as sin() and cos()
Of course, this method could have failed in cases where the path would be a vertical line, so I made it able to “invert” itself in those cases. When the vertical vector would be bigger than the horizontal one it should invert too to avoid missing any pixels that could be inside a tile.