Map from an array

Hello everybody! I continue my struggle with arrays. I want to learn how to create a map from an array with which you can interact. Took advantage of the excellent tutorial from @crait but it doesn’t explain wall collisions. I tried to implement it, but unfortunately without success. Tell me what the problem might be? And is there a more optimal method, provided that in the future the card will move with the character. Links will help a lot too!

To prepare your levels, you can use:

enum class TileType : uint8_t {
  None,
  Solid
};

So yes it make line of your world larger but it’s will help to remember that number means later in your big size world array with more types of tiles

You could make a special function too to know if a tile Type is solid or not. Then you draw your world and you can test if a tile is solid when required. You will just have to define a property for each tile types.

Atém you check each time you draw the world all sort of collision. You could only test collisions possibles: For exemple: when you move right, you could check if you collide with a block at the right or if you have no more block under feet then you fall until you reach bottom or a solid block… same with left… when you jump or a solid block stop you or you jump upper than it and then are stopped when you go down after.

2 Likes

Conceptually, the idea of making something ‘solid’ in a virtual world actually means preventing an entity in the world from entering the area of space designated as ‘solid’.

In other words, solidness is actually about preventing movement, which means you should be checking for collisions only when you attempt to move an entity (e.g. your player’s character).

At the moment you appear to be trying to mix the collision checking with the rendering code, which isn’t a good idea even if it worked.

What you ought to be doing is looking at the position the character/entity is attempting to move to, determining if the character/entity is allowed to move to that position (i.e. that position is non-solid), and if it isn’t allowed to move to that position, either prevent the movement or move it somewhere it is allowed to move to.

For example:

if(arduboy.pressed(LEFT_BUTTON))
{
	// Calculate new X position
	int newX = player.x - 1;

	// Calculate tile coordinates of new position
	int tileX = newx / tileWidth;
	int tileY = player.y / tileHeight;

	// If position is non-solid (movement is allowed)...
	if(world[tileY][tileX] == 0)
		// Move player
		player.x = newX;
}

I believe in your case tileWidth and tileHeight are both 8.

Note that for moving right and down you will have to check the right and bottom sides of the character sprite, which means including the character’s width and height in your calculations.

You may want to take a look at this:

Specifically the (fairly well commented) code that handles collisions is here:

It’s intended for a platformer rather than a top-down game, but the principle is similar. The only real difference is the presence of a ‘gravity’ force and how the controls behave - left and right behave as you’d expect from a top-down game, but up and down don’t.

You might also want to look at the topic that lead to its creation:


Lastly, it’s a bad idea to mix movement and collision code with rendering code (see separation of concerns). You shouldn’t have any collision checking code inside drawworld, and you shouldn’t be moving the player inside a function called DrawPlayer, you should have separate functions for drawing and updating/moving the player.

I would also advise using constexpr variables rather than #defines because #defines are old-fashioned, have various downsides and probably don’t work the way you expect them to work. E.g. instead of #define WORLD_WIDTH 16, using constexpr int worldWidth = 16; is better.

If you have any other questions or need any clarification, feel free to ask.


In terms of memory, it would actually make the world smaller because (on Arduboy) uint8_t is smaller than int.

2 Likes

@Pharap @Jean-Charles_Lebeau
Guys, thanks for the useful information! I will understand further :slight_smile:

3 Likes

For the second day I have been trying to figure out the collision, but so far I have only been able to delve into the code that you offered me as an example. I used this method in my sketch ( Uploaded to GitHub), but the wall is solid only if the player is lower on the Y axis. The same thing happens on the X axis. I tried to create a condition, something like:

if (player.y < player. y / 8) { player.y = player.y + 10 }
else player.y = player.y;

but that doesn’t work. In addition, the player in some places cannot move off the screen. Something prevents him from doing it. With what it can be connected?
I don’t understand why the solid world array seems so complicated to me. Apparently I was a bad student at school

You’re checking the top left corner when moving left or up, the top right corner when moving right and the bottom left corner when moving down.

I’ve suddenly realised that I didn’t quite give a complete answer last time, I ended up giving you half an answer. Exactly half, because I only told you how to check one corner when you need to check two…

The simplest way to fix the problem is to check both corners on a side to make sure that neither collide.

This time I have written the code out in full and properly tested it instead of providing half an answer. I’ve split it up into small steps using well-named variables to hopefully make it easier to understand.

void MovePlayer()
{
	constexpr int playerWidth = 8;
	constexpr int playerHeight = 8;
	
	if(arduboy.pressed(RIGHT_BUTTON))
	{
		int newX = player.x + 1;

		int playerRight = (newX + playerWidth);
		int playerTop = player.y;
		int playerBottom = (player.y + playerHeight);

		bool topRightColliding = (world[playerTop / 8][playerRight / 8] != 0);
		bool bottomRightColliding = (world[playerBottom / 8][playerRight / 8] != 0);
		
		if(!topRightColliding && !bottomRightColliding)
			player.x = newX;
	}
	
	if(arduboy.pressed(LEFT_BUTTON))
	{
		int newX = player.x - 1;

		int playerLeft = newX;
		int playerTop = player.y;
		int playerBottom = (player.y + playerHeight);

		bool topLeftColliding = (world[playerTop / 8][playerLeft / 8] != 0);
		bool bottomLeftColliding = (world[playerBottom / 8][playerLeft / 8] != 0);
		
		if(!topLeftColliding && !bottomLeftColliding)
			player.x = newX;
	}
	
	if(arduboy.pressed(UP_BUTTON))
	{
		int newY = player.y - 1;

		int playerTop = newY;
		int playerLeft = player.x;
		int playerRight = (player.x + playerWidth);

		bool topLeftColliding = (world[playerTop / 8][playerLeft / 8] != 0);
		bool topRightColliding = (world[playerTop / 8][playerRight / 8] != 0);
		
		if(!topLeftColliding && !topRightColliding)
			player.y = newY;
	}
	
	if(arduboy.pressed(DOWN_BUTTON))
	{
		int newY = player.y + 1;

		int playerBottom = (newY + playerHeight);
		int playerLeft = player.x;
		int playerRight = (player.x + playerWidth);

		bool bottomLeftColliding = (world[playerBottom / 8][playerLeft / 8] != 0);
		bool bottomRightColliding = (world[playerBottom / 8][playerRight / 8] != 0);
		
		if(!bottomLeftColliding && !bottomRightColliding)
			player.y = newY;
	}
}

! means ‘not’ and && means ‘and’, so if(!bottomLeftColliding && !bottomRightColliding) means “if not bottom left colliding and not bottom right colliding”.

In this case:

  • The player only moves right if the top right and bottom right corners don’t collide with any blocks
  • The player only moves left if the top left and bottom left corners don’t collide with any blocks
  • The player only moves up if the top left and top right corners don’t collide with any blocks
  • The player only moves down if the bottom left and bottom right corners don’t collide with any blocks

Collision checking is fairly complicated in general.
A lot of the simpler solutions have bugs that make them break under certain conditions

It is fairly simple to test whether:

  • A point intersects a rectangle
  • A rectangle intersects a rectangle
  • A point intersects a circle
  • A circle intersects a circle

But to use that information to properly handle a collision can be tricky to get right.

The technique I have show you works fine for this example, but it has two other flaws:

  • If the player entity were larger than the tiles, the player could walk through a tile when neither of its corners touched a tile
    • This can be fixed in several ways, with the simplest being to increase the number of points checked
  • If you try to move more than 1 pixel at a time, the player entity may stop moving before it touches the wall
    • This can be fixed in several ways, with the easiest being to move it one pixel at a time until it either it collides with something or you move as far as you want to

You didn’t actually use the code snippet you posted in your code, but I feel I should point out that this:

is redundant. Assigning something to itself doesn’t do anything. It amounts to a command to “read A, then write that value back into A”.

1 Like

At first glance, collision checking seemed so simple, but in fact it is a whole science :smiley: Thanks for the quick reply! The code works of course. This information will be more than enough for the near future…

1 Like

And once again I want to clarify about the invisible blocks that do not allow you to go outside the map in some places. Is this all because random numbers from 1 to 0 are generated outside the map, or is there another reason?

1 Like

It’s actually geometry, and potentially a bit of physics depending on how realistic and ‘physics-y’ you want your world to be. :P

(If you want to be really ‘physics-y’, see Physix for an example of basic rigid-body physics.)

Conceptually speaking there actually isn’t an ‘outside the map’,
but practically speaking there is data beyond the map…

The map occupies enough memory to store a 16x8 array of int (16 * 8 * sizeof(int) bytes of data), and anything beyond the coordinates (0, 0) to (15, 7) is memory belonging to other parts of the game.

That means if you try to read invalid coordinates, like world[-1][-1], instead of getting a valid map tile, you read some unrelated memory that you shouldn’t be reading.

So there are numbers beyond the map, but they aren’t necessarily 0 or 1. They aren’t random, but you wouldn’t be able to predict what they would be without knowing about how the compiler has chosen to put your variables into the computer’s memory.


In my Platformer Demo that I linked to earlier, I demonstrate a solution to the ‘outside the map’ problem by checking for attempts to read outside the map - if the code tries to read outside the map, the map tells the player that there is nothing but ‘void’ out there, and ‘void’ is a kind of tile the game thinks is solid, so the player cannot leave the map.

If you are hoping to turn this code into a game, you should aim to do something similar.

E.g., create a function getTile that does such checking:

int getTile(int x, int y)
{
	// Claim that there are only solid blocks outside the map
	if((x < 0) || (y < 0) || (x >= WORLD_WIDTH) || (y >= WORLD_HEIGHT))
		return 1;

	return world[y][x];
}

And use this instead of reading world directly.


You may find it interesting to know that actually a ‘2D’ array is really a 1D array with special coordinate mapping.

world[WORLD_HEIGHT][WORLD_WIDTH] is effectively world[WORLD_HEIGHT * WORLD_WIDTH] and when you do an access like world[y][x] it actually becomes world[y * WORLD_WIDTH + x].

1 Like

Oh, thanks for another food for thought! Indeed, all this is a combination of different sciences, the study of which can take forever, but it is a pleasant experience. Thanks again🙂

Hey! I have another question about the array map, but this time I wanted to ask about the inclusion of bonuses in the game. I think the easiest way to interact with them is through “Rect”, but no matter how hard I try, I cannot form an array out of them. Does it even make sense to use Rect for bonuses?

Assuming by ‘bonuses’ you mean powerups (something the player can collide with to score points or gain abilities), then yes, you could represent several ‘bonuses’ with an array of rectangles specifying the area that the player has to collide with.

However, if your ‘bonuses’ are all the same size, you could halve the memory consumption by just storing a list of points instead, and only building the rectangles when you run the collision code.

An array of Rects would just be Rect array[size]; E.g. Rect bonuses[10];.

However, you would also need a way to know which ‘bonuses’ are active. There’s at least half a dozen different ways you could do that.

E.g.

  • Have inactive ‘bonuses’ use off-screen coordinates
  • Use a struct that includes a bool active; member variable, so you can manually set active to true or false

Apparently I didn’t give enough information. I want to understand how to create the same two dimensional array that we discussed earlier. Only this time, I want to try doing it with “Rect”. In my opinion, this method is ideal for receiving bonuses as it is easier to manage. Maybe I’m wrong, correct me if that’s the case

Tried to create something like Rect bonuses[mapHeight] [mapWidth] but the compiler throws an error

That seems a bit of an odd thing to want to do.
What are the rectangles supposed to represent?

More importantly, it will use quite a lot of memory (8 bytes per Rect, 8 * mapWidth * mapHeight bytes total). I suspect there’s a much ‘cheaper’ way to do what you’re trying to do.

I can’t help without more context. Ideally I’d need the error message and the code, but I can probably tell you what’s wrong from the error message if there’s some reason you don’t want to publish your code. (Though partial code would also be useful.)

1 Like

Absolutely - as most coordinates will be empty. Better to have a single dimension array and loop through then to see if you have collided with something.

And build the rectangles as you loop through the array.

1 Like

@Pharap @filmote
Unfortunately, I don’t have any code ready to show you, as these were just sketches that I immediately removed. The fact that using Rect would use more memory was sobering to me. This is a great reason not to use this method. Thanks for the clarification :slight_smile:

1 Like

I was just reiterating @pharap’s original message :slight_smile:

2 Likes

If you want to know how many bytes a type uses, you can use sizeof(type), e.g. arduboy.println(sizeof(Rect)).

I double-checked and Rect is actually 6 bytes, not 8 bytes. I’d forgotten the width and height are uint8_ts. (I’ve been doing more desktop programming recently.)

The type Rect is a struct (structure) with member variables x, y, width and height.

x and y are int16_ts, which are 2 bytes each, and width and height are uint8_ts, which are 1 byte each, 2 + 2 + 1 + 1 = 6, hence a Rect is 6 bytes.

An array Rect array[8][8] would use 6 * 8 * 8 = 384 bytes of memory.

(In fact, I’m wondering if your compiler error was complaining that there wasn’t enough memory left…)

That’s not to say using Rect is bad, just that using lots of Rects is a problem. E.g. an 8x8 array of Rects is actually 64 Rects. A 9x9 array would be 81 Rects, a 10x10 array would be 100 Rects… it soon adds up. The same is true for things other than Rects - this is the nature of 2D arrays.


When programming, there are three forces a programmer must balance: memory, processing power and flexibility. In general, to improve one you must sacrifice another. Saving memory usually requires sacrificing processing power, and saving processing power usually requires sacrificing memory.

1 Like

Thanks for the comprehensive answer! It is important for me that my gaps in game programming, in particular for the arduboy platform, are gradually filled with information. I can’t find enough time to create a complete game for every topic that interests me, so I have to sketch out to figure out how a particular method works. I think now I will stop deleting them🙂