Wraparound chunks and player jumps
Let's consider the problem in 1D (1 axis): Say I want to make an infinite runner game that consists of 3 chunks, looped over and over again. Boring game, but it serves to explain how we make a game look like we are moving in a straight line forever. Consider chunks A, B and C:
ABC ^
I start in chunk B. As I move forward, I leave B and enter C:
ABC ^
Oops, there is nothing in front of me. So at the moment that I cross the boundary between B and C, I grab chunk A, and move it ahead of chunk C:
BCA ^
As I move out of C into A, I do the same thing again, this time moving B in front:
CAB ^
And moving back into B, we move C ahead (aaand we've completed a full cycle):
ABC ^
What matters is that there is always something in front of me, and behind me. If each chunk were represented by a class instance, the code would look roughly like:
//1. declarations and initialisations class Chunk { ...chunk data... } Chunk[] chunks = new Chunk[3]; Chunk tmp = null; //player position and speed float x = 0; float vx = 1.4; //2. each frame / game loop iteration x += vx; //update position by speed int pxi = (floor(px) - 1) % 3; //chunk behind player (previous) int cxi = (floor(px) ) % 3; //chunk where player is (current) int nxi = (floor(px) + 1) % 3; //chunk ahead of player (next) Chunk chunkCurrent = chunks[cxi]; //...do jump collision detection based on current chunk... etc.
Rendering
It's important to understand that you can either do this using modulo wrapping as above, OR by literally cycling the Chunks inside the array, every time you move forward one chunk; this also means you can assume the player is always at chunks[1], so there is always something behind and ahead of you. You don't need to move the player's index, just the chunks it refers to.
The reason I mention this is that you will need to do exactly this for the rendering side of things. If you have each of the 3 chunks displayed by an individual canvas or texture, you are going to need to swap these _in front of the player each time as they move forward.
About the math
In terms of the math, Philipp's answer is roughly correct. Performance-wise however you may prefer bitwise logic than the (relatively expensive) modulo operator and branching conditionals (if), provided you can use powers of 2 and unsigned integers (8, 16, 32 or 64 bit).
Unoptimised:
//each frame x = x % MAP_WIDTH; //costly modulo operator if (x < 0) x = MAP_WIDTH + x; //possibly costly conditional (depends on compiler) y = y % MAP_HEIGHT; //costly modulo operator if (y < 0) y = MAP_HEIGHT + y; //possibly costly conditional (depends on compiler)
Optimised:
//initialisation int x = ...; int y = ...; const int MAP_WIDTH = pow(2, 8); //256 const int MAP_WIDTH_MOD = MAP_WIDTH - 1; //will set all 8 bits to 1 //each frame x = x & MAP_WIDTH_MOD; //same as 'x % MAP_WIDTH' y = y & MAP_WIDTH_MOD; //same as 'y % MAP_WIDTH'
When running these for thousands of entities per frame, it can make a considerable difference.
The exact same math as the infinite runner example applies in 2D, we've just added a y axis.