Which part?
If youāre still worrying about what goes on in the hash function, donāt worry about it.
You donāt have to understand how it works to understand what it does.
It could just as easily be:
uint32_t hash(uint32_t value)
{
// 127 and 8191 are prime numbers
// (specifically 'mersenne primes')
return ((value + 127) * 8191);
}
uint32_t hash(uint16_t x, uint16_t y)
{
const uint32_t x2 = x;
const uint32_t y2 = y;
const uint32_t value = ((x2 << 16) | (y2 << 0));
return hash(value);
}
uint32_t hash(int16_t x, int16_t y)
{
return hash(static_cast<uint16_t>(x), static_cast<uint16_t>(y));
}
The important thing is the job that the hash function does:
- It takes a pair of coordinates (x and y) and returns a seemingly random value (i.e. there should be no obvious pattern to the output)
- It provides a valid output for all possible combination of input values (i.e. itās a āsurjectiveā function)
- It always provides the same output for the same pair of inputs and it has no side effects (i.e. itās a āpureā function)
Any function that meets those three criteria could be used for the hash,
the only difference would be in the quality of the result.
Iām not sure what you mean by this.
Or this.
I think you might be getting the meaning of ācoordinate systemā mangled.
Usually for a 2D game there are only two coordinate systems that need to be taken into account: world coordinates and view/camera/screen coordinates.
(For a 3D game thereās usually two other systems to take into account.)
The typical way to handle a 2D world is to give all the terrain static coordinates (i.e. non-changing coordinates),
and all movable objects/entities dynamic coordinates (i.e. changing coordinates).
Then thereās a ācameraā object which represents the screen,
and that object moves around the map.
Then when itās time to render, the world coordinates are translated to screen coordinates by subtracting the cameraās position.

(Excuse the hand-drawn diagrams, I find hand drawing quicker and easier than digital drawing.)
For example, if the camera object is at (16, 16) (top left coordinate), and an enemy is at (64, 64),
then when theyāre translated into camera/view/screen space,
the camera will be at (0, 0) (so the cameraās top left coincides with the screenās top left),
and the enemy will be at (48, 48) ((64 - 16) == 48
).
Theyāre literally just a block of memory with values laid end to end.
For example, int array[4]
would be 8 bytes.
In memory it would look something like this:
[0x0400] array[0], low byte
[0x0401] array[0], high byte
[0x0402] array[1], low byte
[0x0403] array[1], high byte
[0x0404] array[2], low byte
[0x0405] array[2], high byte
[0x0406] array[3], low byte
[0x0407] array[3], high byte
The hex values represent RAM addresses, I made up the address values.
The low byte comes first because AVR is ālittle endianā (like x86),
which means lower valued bytes are stored first.
A Point
consists of 2 int16_t
, which are two bytes.
In memory a Point
looks something like this:
[0x0500] x, low byte
[0x0501] x, high byte
[0x0502] y, low byte
[0x0503] y, high byte
So an array of two points, Point points[2]
, would look like this in memory:
[0x0600] points[0].x, low byte
[0x0601] points[0].x, high byte
[0x0602] points[0].y, low byte
[0x0603] points[0].y, high byte
[0x0604] points[1].x, low byte
[0x0605] points[1].x, high byte
[0x0606] points[1].y, low byte
[0x0607] points[1].y, high byte
Thatās literally all there is to it,
the data is just stored in memory contiguously,
and the compiler handles all the maths needed for moving the data around.