2D grid based games : represent passability
An answer to this question on Stack Overflow.
Question
Considering a tiled based game, where each agents can move straight/diagonally (in 8-directions). Basically, a map like this can be represented as a regular 2D grid, where 0 would represent an walkable location and 1 unwalkable locations (I'm using Lua):
-- Example : 3x3 sized map
local map = {
{0,0,0},
{0,1,1},
{0,0,0},
}
At this point, how can we represent tile walkability depending from the direction an agent comes from ? I.e. cell [2][2] above, which is statically unwalkable, would now be walkable if coming from [1][2] (above) or [2][1] (left), but not, for instance, from [3][2] (down).
I have given to this some thoughts, but I couldn't come up with something clean enough, to me.
Thanks in advance.
Answer
I'd overlay another 2D grid with of single bytes. Each bit of the byte corresponds to a possible entrance direction with a 1 meaning it can be walked on from that direction and a 0 meaning not. You can then check for enterability using binary masking.
If most of your cells can be entered from any direction, then you may consider using a [map][1] with the tile's absolute ID (X*MaxY+Y, for instance) as a key and the byte scheme described above indicating enterability. This is slower to access, but takes less space.
For instance, let the directions be laid out as so:
Bit # X offset Y offset
123 -1 0 1 -1 -1 -1
4 5 -1 0 1 0 0 0
678 -1 0 1 1 1 1
If I go in the northeast direction, this corresponds to bit #3. I can perform masking by translating the above values into bit masks:
1 2 4
8 16
32 64 128
I can enter from a direction if the following returns true
Enterability(CurrentX+Xoffset(Dir), CurrentY+Yoffset(Dir)) & BitMask(Dir)
(Sorry, I'm afraid I don't know Lua well enough to write this up in that language)
Edit
So, say my directions and such are as above and I want a square that can be entered only from the North. To do this, I set bit #2:
Enterability(X)=2
If I want a square that is enterable from both the north and the southwest, I would use:
Enterability(X)=2 | 64
where | is the bitwise OR operation.
If I want a square to be enterable from any direction but west I use:
Enterability(X)=(~8)
where ~ is the not operation.
If I need to close a door, say to the east, I could unset that bit:
Enterability(X)=Enterability(X) & (~16)
To open the door again, I use:
Enterability(X)=Enterability(X) | 16
or, more simply,
Enterability(X)|=16
The ~16 produces a bitfield which is all ones except for the bit referring to 16. Using this with the AND operator (&) leaves all the bits on, except the one referring to 16.
Also note that hexadecimal addressing can be more convenient:
Decimal Hexadecimal
1 2 4 0x1 0x2 0x4
8 16 = 0x8 0x10
32 64 128 0x20 0x40 0x80
[1]: https://en.wikipedia.org/wiki/Hash_table