I’m excited to get into Brackeen’s version of 3d, but there’s a lot of 2d stuff I haven’t done yet. For example, side-scrolling (gif):
The caveat is the window size must be wide enough to stop scrolling. If the game window is not wide enough, the character will collide with the edge before the last tile is reached.
Brackeen’s double-buffering strategy is better; my updates still have tears and subtle flicker. His threading code enables a 0.5f velocity to move faster than my 5f dx change. Maybe it is due to using GraphicsDevice in the Screen class.
The scrolling itself is possible because the moving sprite’s x-position is absolute. So in Brackeen’s x-offset equation, the rest of the variables are constant:
x_offset = screenWidth/2 - player.X - ONE_TILE_WIDTH;
The second constant, ONE_TILE_WIDTH, is the tile width; launch Paint, resize the canvas, and make some nice 128×128 tiles.
The first constant determines when to start scrolling: begin scrolling once the sprite moves to the right half of the screen. This is because player.X > screenWidth/2 past the halfway mark, making the whole equation negative. As the sprite moves right, positive, positive, zero – then negative. That explains
x_offset = Math.min(x_offset, 0);
At the far right, scrolling stops because screenWidth-as-a-point minus the player’s current x-position is way far to the left, way, way over there, versus screenWidth minus mapWidth, which is less so (but still way out to the left).
| | |
D/2-C D-A +---------+---+
^ ^ ^ (C)
B E D
A is map width in pixels
B = (D/2 - C)
C is anywhere on the screen at the end of the map
D is screen width treated as point
E is a *constant*, large distance = (D - A)
I imaginary screen
D/2 - C < D - A
As player.X (C) approaches mapWidth, the subtraction is such that the larger number is (screenWidth – mapWidth) instead of (screenWidth/2 – player.X – ONE_TILE_SIZE):
x_offset = Math.max(x_offset, screenWidth - mapWidthInPixels);
Twist: the offset is negated to determine the ordinal of the first tile to draw! x_offset is negative all this time, and then we flip the sign to determine which tile will be drawn first from the left of our screen. Yet the x-position of the tile is that number plus the raw (negative) x_offset.
This makes sense considering the first tile: if it is drawn at (0,0),
x_offset = Math.min(0, screenWidth/2 - player.X - ONE_TILE_WIDTH);
x_offset = Math.max(screenWidth/2 - player.X - ONE_TILE_WIDTH,
screenWidth - mapWidthInPixels);
first_tile_ordinal = -x_offset / ONE_TILE_WIDTH;
first_tile_x = inPixels(first_tile_ordinal) + x_offset; // 0