Firestarter (WIP and a couple questions...)

Ohh whoops! Yeah, the example i used for the empty Arduboy program didnt have any curly braces around the return statement so i added them since that screws me up, only i did it in the wrong spot because… well, thst screws me up lol. Thanks!

Ohh, alright… i was under the impression it would be better to make a sprites variable since id be reusing it… is one allocated differently than the other? Seems making a sprites variable would add the variable to rom while using the function statically would use more ram instead (unless i was mistaken, that’s just whst i thought). Thanks!

Hmm… it displays now, but the sprite is showing up scrambled, my guess would be something involving how i exported it in gimp but im not seeing it

PROGMEM const unsigned char building0[] = {
38, 35,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x00, 0xc7,
   0x1f, 0x00, 0x00, 0x80, 0x1f, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0xfc, 0x01,
   0x00, 0xe0, 0xff, 0xf1, 0x07, 0x00, 0xf0, 0xff, 0xc7, 0x1f, 0x00, 0xf8,
   0xff, 0x1f, 0x7f, 0x00, 0xfc, 0xff, 0x7f, 0xfc, 0x01, 0xf8, 0xff, 0xff,
   0x01, 0x00, 0xe2, 0xff, 0xff, 0x03, 0x00, 0x8e, 0xff, 0xff, 0x01, 0x00,
   0x3c, 0xfe, 0xff, 0x00, 0x0f, 0xf0, 0xf8, 0x7f, 0xc0, 0x03, 0xcc, 0xe3,
   0x3f, 0xf0, 0x0c, 0x0c, 0x8f, 0x1f, 0x3c, 0x0c, 0xc0, 0x3c, 0x0e, 0xcf,
   0x00, 0xcc, 0xf0, 0x80, 0xc3, 0x0c, 0x0c, 0xcc, 0xc3, 0x0c, 0x0c, 0xcc,
   0x0c, 0x03, 0xcc, 0x0c, 0xcc, 0xc0, 0xc0, 0xc0, 0x0c, 0xcc, 0xcc, 0xc0,
   0xcc, 0x0c, 0xcc, 0x0c, 0x0c, 0xcc, 0x0c, 0xcc, 0xcc, 0xcc, 0xcc, 0x0c,
   0xcc, 0xcc, 0xc0, 0xcc, 0x0c, 0xc2, 0xcc, 0xcc, 0xcc, 0x10, 0xcc, 0xcc,
   0xcc, 0xcc, 0x0c, 0x30, 0xcc, 0xcc, 0x0c, 0x03, 0xc0, 0xcc, 0xcc, 0xcc,
   0x00, 0x00, 0xc3, 0xcc, 0x30, 0x00, 0x00, 0xcc, 0xcc, 0x0c, 0x00, 0x00,
   0x30, 0x0c, 0x03, 0x00, 0x00, 0xc0, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x33,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

What sprite function do you use and does the sprite contain a mask or not? also the sprite height should be a multiple of 8 pixels

That’s perfectly legal. The curly braces are only required if there’s more than one statement in the if's body.

(Technically speaking the curly braces actually form a single ‘block statement’, so the if still technically only has one statement as its body, it just happens to be a single statement that consists of a possibly empty list of other statements.)

The compiler optimises the variable away because the variable itself never actually gets used…

Sprites is a class with only static member functions and no member variables, so its size is effectively zero*. sprites.drawSelfMasked would produce the same code as Sprites::drawSelfMasked because they refer to the same function and the sprites object isn’t used (a ‘static’ member function doesn’t involve the use of an instance of the class). And then, because none of the functions actually use the variable, the compiler figures out that the variable is never used and effectively discards it as if it never existed in the first place.

Hence, you might as well just stick to using Sprites::drawSelfMasked because A) you’re effectively doing what the compiler would do anyway (not bothering to make a variable), and B) it’s easier to use Sprites because you only have to #include <Arduboy2.h>, whereas if you use a variable you have to make sure all of your code that needs to use that variable can see that variable.

* It would actually have a size of 1 byte for technical reasons, but I won’t bore you with that now.

You’ve got a broken picture of how RAM and progmem (what you’re calling ROM) are used - it’s the other way around.

A function consists of machine code, so it occupies progmem. If a function is never called (and its address is never taken) then it won’t be included in the output program. If a function is called anywhere in the program it will increase progmem usage because the function will be included in the program.

Global variables are likely to occupy RAM, but it depends on the circumstances. If they are modified anywhere in the program then they are almost guaranteed to occupy RAM. If they aren’t modified (especially if they are const or constexpr) then might be optimised away under the right circumstances, or possibly they might be put in progmem if the compiler is capable of doing so. It all depends on the circumstances.

Local variables may or may not occupy RAM depending on the circumstances. If they are small then they are most likely to occupy registers (small and fast blocks of memory built into the CPU itself), which makes them more efficient. (They can even end up using both at different stages of their lifetime, it depends on the circumstances.)

What did you export it as (.png, .bmp, .jpg?), and which converter did you use to get it into the Arduboy format?

drawSelfMasked… not using a mask of my own. And i thought i had used sprites of whatever size before (though maybe im misremebering), i was just going off the size of the original image.

Oh i know its perfectly legal, doesnt mean it doesnt still mess me up though lol.
And the rest of that makes much more sense now, thanks!

I imported a png to gimp then exported it as an xbm (for some reason i thought thats what i did before but maybe not), i couldnt find an image converter that worked without me needing to install a bunch of other stuff… thought i had exported it straight from gimp before without an image converter though

That’s your problem then.
XBM does not match the format that Arduboy uses. (I found this out a while back.)
Arduboy sprites are stored columnwise, XBMs are (as far as I know) stored rowwise.

Export as .png and pick a converter.

There’s a chance it might work with arduboy.drawSlowXYBitmap (as the ‘slow’ part should indicate, this is a bad idea), or that there’s some really obscure image format that does actually match the Arduboy’s requirements. I expect you’re probably just misremembering though.

Ahh ok, ill mess around a bit more with getting one of the image converters working then, thanks!

Ok, i got one of them working and my sprite is displaying fine now, but when i try to establish a screen vs world space the tile is still displaying relative to screen space. Which i kind of understand since Sprites::drawSelfMasked probably uses 0 as screen 0 as its drawing everything relative to the screen, but i dont want to move the world in relation to the camera so im not sure it would make sense to increment or decrement those values either. This is where i knew id get stuck lol. Heres a repo with my current code:

Yes, Sprites has no concept of ‘world space’, it expects screen space coordinates. You have to make sure the coordinates are properly transformed before you pass them to Sprites’ functions.

Actually, you do sort of want to move the world in relation to the camera, but only for the sake of calculating where the camera is looking.

I.e.

Sprites::drawSelfMasked(buildingLoc.x - camera.x, buildingLoc.y - camera.y, building0, 0);

To translate a coordinate into the position at which the camera will see it, you subtract the camera’s position. (Assuming the camera’s position refers to the top left of the viewport.)

(Note that the building moves opposite to the direction you press on the direction buttons because it’s the camera that’s moving, not the world.)

Ordinarily I’d draw a diagram to illustrate why this works, but I’d have to dig out my scanner and it’s getting late. I can do one either tomorrow or the day after if need be though.

Edit:
I knocked these together in paint…

This first image represents ‘world space’, including the camera’s coordinates and an object’s coordinates, before being translated into screen space.
WorldSpace

This second image represents screen space, which is what you get after subtracting the camera coordinates from the object, thus making the coordinates relative to the top left of the camera (i.e. the origin point, point (0, 0), of screen space).
ScreenSpace


Some other points:

  • Don’t call arduboy.flashlight in setup, that’s arduboy.begin's job (i.e. you’re effectively calling it twice).
  • You don’t need to call arduboy.clear in setup because you’re calling it in loop. (It does make a difference for the first frame, but nobody is going to notice.)
  • arduboy.setFrameRate(60); is technically redundant because 60 is the default fps, but if you’re planning to attempt to use a different framerate then it’s up to you whether you leave it in.
2 Likes

Ahh ok, that makes sense. And the rest of that is super helpful as well, thanks!

1 Like

Are there resources out there that actually explain how to create a tilemap using a 2D array? I spent quite a bit of time searching the forums for an answer but it seems all i could come.up with were theads confirming thats what i should do, though unfortunately nothing that explained how to actually do it. Thanks!

That really depends on what you mean by ‘create’. I.e. whether you mean the basics of drawing the array or whether you also mean loading the map into memory and so forth.

The absolute simplest way to render a tile map is to just have a 2D array of uint8_t, and then iterate through the whole array rendering each tile one by one, deciding which number represents what kind of tile.

I.e. it’s really just something like:

// A 2D array of tile types/tile indices
uint8_t world[height][width] { /* Fill the map data in as necessary */ };

void drawMap()
{
	for(uint8_t y = 0; y < height; ++y)
	{
		// Calculate the y position to draw the tile at
		int16_t drawY = ((y * tileHeight) - camera.y);
		
		for(uint8_t x = 0; x < width; ++x)
		{
			// Calculate the x position to draw the tile at:
			int16_t drawX = ((x * tileWidth) - camera.x);
			
			uint8_t tileType = world[y][x];
			
			// Assuming that your tile types are the same as
			// the frames used in your tilesheet. Otherwise
			// you'll need a way to determine the sprite index
			// from the tile type.
			Sprites::drawOverwrite(drawX, drawY, tilesheet, tileType);
		}
	}
}

Note however that drawing the whole map is inefficient. It’s much more efficient to only draw the tiles that are actually on screen if you can.

(Personally I also prefer to use enumerations so the tile types are named rather than just remembering what the numbers stand for, but that adds a bit of complexity.)


Crait gives a basic example in the Dino-Smasher tutorial:

Note that in addition to the shortcomings of my earlier example, this version also wastes twice as much memory by using int instead of uint8_t/unsigned char.

There’s also this thread:

Which isn’t a tutorial but does involve and discuss making a tile map, and resulted in this simple platformer demo:

This demonstrates drawing only the visible tiles and using an enumeration type to represent the tiles. (If you skip to the map drawing code you’ll see it’s effectively a more embellished version of the code I gave at the start of this reply.)

I also stumbled upon this:

Which was supposed to be demonstrating using hash functions to generate map tiles, but happens to include tile map rendering code. This demonstrates only drawing the visible parts of the map but doesn’t use an enumeration type (partly because all the tiles in this case are just different arrangements of stars).


I went looking for some external ones, but nearly all the ones I found were far more complicated than what you need, or were too concerned with doing things ‘the OOP way’, or wasted too much memory because they’re designed for desktop computers rather than embedded systems.

Even the best one I could find uses tiles that are comprised of 4 sub-tiles and starts talking about Perlin noise halfway through.

This one from Mozilla is alright, though it’s JavaScript so it will look a bit weird compared to what you’d need to do in C++. (E.g. JavaScript doesn’t really have a concept of ‘integers’, hence floor crops up a lot. If you need the floor function to draw a tile map in C++ then you’ve possibly gone wrong somewhere.)

This one from tutsplus covers some useful theory, but the actual implementation is a bit questionable (and it’s ActionScript again).


In future if you can’t find a decent tutorial get a pencil and paper out and think about the problem.
Even a half-baked solution that sort-of works is better than nothing.

1 Like

It’s not a tutorial, but since you’re not the first person to ask me about making a tile map lately, I decided to make a demo that I can point people to.

Hopefully this will help. If you have any questions about it, feel free to ask.

1 Like

Awesome, thanks so much! Yeah, i had looked through all the aforementioned links (even both games you helped me with since they both employed a camera system) but still wasnt wuite seeing it- that definitely helps to clarify though, thanks!

Hmm, im a little unsure how to proceed since my building sprites arent all on the same sprite sheet (because they differ in size) so it wouldnt be as simple as populating the array with random frames… that and im unsure what to make my world size since the sprites are originally 38x36 (and even if i halved that would only fit 13 tiles which doesnt seem like much, so i might need to use a uint16_t instead)… also i dont want to draw my map the same way im creating it (since its supposed to be isometric) so that’s tripping me up a bit too

Sorry, I haven’t read all the thread in detail but can you convert each sprite individually with this converter > Arduboy Image Converter

With regards to the size constraints - welcome to the world of 128x64 pixel graphics. Its always a compromise between image fidelity and real-estate.

Then you’ll have to make do with arrays. E.g.

#include "Sprite0.h"
#include "Sprite1.h"
#include "Sprite2.h"
#include "Sprite3.h"

// For uint8_t
#include <stdint.h>

constexpr const uint8_t * tiles[]
{
	sprite0,
	sprite1,
	sprite2,
	sprite3,
};

And then instead of Sprites::drawOverwrite(drawX, drawY, tileSheet, tileIndex); you’d be doing Sprites::drawOverwrite(drawX, drawY, tiles[tileIndex], 0);.

(This is actually closer to how it would be done on desktop, it just happens to be the case that the other approach is cheaper on Arduboy.)

Though drawing your isometric tiles using this grid system won’t look pretty. The idea is just so you have a starting point and then you can either decide whether to try to upgrade to isometric or get some of your game logic working first.

If you want to go straight into figuring out isometric rendering then stick with your current images. If you want to work on the logic, you might want to throw together a few 2D placeholders (e.g. plain tiles with letters on them) to work with in the mean time.

I have absolutely no clue what you mean by this.

It’s isometric, but it’s still a grid. Whether you use a flat top-down view, an isometric view or even a 3D view, something with a 2D grid structure will still have a 2D grid structure.

The logic - a 2D array of tiles/buildings - doesn’t change. What changes is the rendering, and perhaps how the character moves around it.

To put it another way, if you had a carton of six eggs arranged 3x2, no matter how you rotate the carton it still has the same 3x2 arrangement of holes.

The same is true for the grid-based city in Fire Starter - whether you draw it top-down or with a side-on isometric view, the city still runs on a grid.

In fact, if you wanted you could do both an isometric and a 2D render of your city within the same game, be it as a mini map, for debug purposes or just to prove to yourself that it’s possible (and perhaps see the relationship between the two).

Thats actually the one ive been using, thanks!

Ah ok, that makes much more sense. Sorry, like i said im still pretty rusty on a lot of this stuff so i couldnt quite remember how id use it in an array (compared to a 2d array like im using for my map), thanks!

I think id rather start with getting the isometric part down first since thats what i anticipate will be one of the more difficult parts (aside from generating roads on the map), then once i get those parts out of the way the rest should go a lot smoother. I can just make 2d placeholders for now, just need to think a bit more on how the proportions would work.

Sorry, that was a typo, meant to say since my tiles are 38 wide even if i made them half the size i could only fit 13 if i used a uint8_t (which seems like itd be a small map) so i might want to use a uint16_t instead, but ill have to see how big it actually looks first anyway. Thanks!

Your best bet would probably be to make some mockups in an image editor (e.g. GIMP or MS Paint) and then cut away a 128x64 pixel window to see what sort of view you’d get on the Arduboy.

Perhaps even convert the mockup to a sprite and draw it on the Arduboy screen to give you an idea of what it would be like on-device.

Use a uint8_t/uint16_t for what? Player coordinates?
If so then yes, you’ll probably want either uint16_t or int16_t for your world coordinates.

Yeah thats what ive been doing (both of those things lol) but 13 tiles would be too large to all fit on screen anyway so i wont know how it looks til its drawn in real world coordinates and i can navigate the camera around it. But if it seems small once i get there then i can make it bigger and use a uint16_t instead. But i just made placeholder sprites for the buildings so i can populate the map and draw them in 2d first then work from there (since im still not entirely clear on how to translate that to isometric yet anyway)