Help me with C++ for my game


#103

My approach is to use 16 tiles (so, uint8_t). So the tiles themselves are just numbers with a corresponding frame from the sprite

Anything equal to or greater than 4 is considered solid. For caverns and houses I’m thinking of simply using a different spritesheet, but stick to 16 tiles. So everything could be stored as a uint8_t

I have watched this video so I’m aware of some strategies https://youtu.be/ZWQ0591PAxM?t=376
But let’s pretend I don’t compress anything and put everything in one giant array on PROGMEM, how many sections of 16 tiles by 8 tiles high could I have?

(I know you can’t be very precise about this because it will depend on how many sprites I have and my code, but any approximation would be good to know)

EDIT: I guess my question might become irrelevent because of how much it can be compressed with the meta-tiles and meta-meta-tile technique. I think the answer is simply pretty big"


(Pharap) #104

If it’s all in progmem without compression so it’s one tile per byte, then it depends on how much code you end up with, because code eats into progmem too.

16x8 = 128, there’s 28672 bytes of progmem in total.
I can’t remember how much you’re using at the moment, but it’s probably something like 30-50%, so you could probably fit a few hundred of those in.

In that case you can fit two tiles per byte, which will half your memory usage (i.e. 16x8 will fit in 64 bytes).

enum class TileType : uint8_t
{
	None,
	Grass,
	Path,
	Wall,
	// Etc
};

struct TilePair
{
private:
	uint8_t value;
	
public:
	TilePair(TileType high, TileType low)
		: value(static_cast<uint8_t>(high) << 4 | static_cast<uint8_t>(low))
	{
	}
	
	TileType getHigh() const
	{
		return static_cast<TileType>((this->value >> 4) & 0x0F);
	}
	
	TileType getLow() const
	{
		return static_cast<TileType>((this->value >> 0) & 0x0F);
	}
};

True, though meta tiles can be quite fiddly.

That’s the third time I’ve seen someone post that same video. :P

Here’s something more impressive: all the original zelda dungeons are actually just a second overworld - the pieces fit together like a jigsaw puzzle:


#105

Before I brick my Arduboy, could you make sure with me I’m not gonna screw it up? (meaning I don’t want to brick my Arduboy). I’m 99% sure I have to use the PROGMEM keyword otherwise it loads in the 2500 bytes or so instead of the big old 28000 something bytes of storage

Here’s my code:

World.h

#pragma once

constexpr uint8_t TOTAL_WORLD_WIDTH = 48;
constexpr uint8_t TOTAL_WORLD_HEIGHT = 32;
extern uint8_t world[TOTAL_WORLD_WIDTH*TOTAL_WORLD_HEIGHT];

constexpr uint8_t WORLD_SECTION_WIDTH = 16;
constexpr uint8_t WORLD_SECTION_HEIGHT = 8;
extern uint8_t loaded_world_section[WORLD_SECTION_WIDTH*WORLD_SECTION_HEIGHT];

int8_t x_on_grid(int16_t x);
int8_t y_on_grid(int16_t y);
uint8_t get_tile(uint8_t xIndex, uint8_t yIndex);
bool is_tile_solid(int16_t x, int16_t y);
void draw_world();
bool detect_wall(int8_t x, int8_t y);

World.cpp

#include <Arduboy2.h>
#include "World.h"
#include "Images.h"

extern Arduboy2 arduboy;

constexpr uint8_t TILE_WIDTH = 8;

uint8_t world[TOTAL_WORLD_WIDTH*TOTAL_WORLD_HEIGHT] {
    0,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,11,11,11,11,11,11,11,11,11,11,0,0,0,0,0,0,0,0,0,
    1,12,13,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,12,13,0,0,0,0,0,1,1,1,1,0,11,11,11,11,11,11,11,0,0,0,0,0,0,0,0,0,0,0,
    1,14,15,0,0,0,1,1,1,12,13,0,0,0,0,1,1,1,14,15,0,0,0,0,0,1,5,5,5,0,0,11,11,11,11,0,0,0,0,0,0,0,12,13,0,0,0,0,
    0,1,0,0,0,1,1,1,1,14,15,0,0,0,11,0,1,1,1,1,1,0,0,0,0,0,5,5,5,1,0,11,11,11,0,0,1,1,0,0,1,1,14,15,0,0,0,0,
    0,0,0,0,0,1,12,13,1,1,1,0,0,1,11,11,0,1,1,0,0,0,1,1,1,1,6,7,6,1,0,11,11,0,1,1,1,0,0,0,1,1,1,1,1,0,0,0,
    0,0,0,0,0,1,14,15,1,1,0,0,1,11,11,11,0,0,0,0,0,2,2,2,2,2,2,2,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,
    0,0,0,0,0,0,1,1,0,0,0,0,1,0,11,0,0,1,1,0,1,2,1,0,1,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,1,0,0,0,0,0,1,1,0,0,0,1,5,5,5,0,0,0,0,0,0,0,0,0,
    0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,0,1,1,0,0,0,2,0,0,1,1,0,12,13,1,1,1,0,0,1,5,5,5,5,1,0,0,0,0,0,0,0,0,
    0,0,12,13,0,1,1,1,1,12,13,1,1,1,1,0,0,5,5,5,0,2,1,12,13,4,4,4,15,0,11,11,0,0,1,5,5,5,6,1,0,0,0,0,1,1,0,0,
    0,1,14,15,1,0,0,1,1,14,15,1,1,0,0,0,0,5,5,5,5,2,1,14,15,4,4,4,1,0,11,11,11,0,0,6,7,6,1,0,0,0,1,1,1,1,0,0,
    0,1,0,0,0,12,13,0,1,1,1,0,0,0,0,0,1,6,5,5,5,2,1,1,0,6,7,6,1,1,1,11,0,0,0,0,2,1,1,1,0,0,1,1,1,1,0,0,
    0,0,1,1,1,14,15,1,1,1,1,0,0,1,1,1,1,1,6,7,6,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,1,1,1,1,1,1,
    0,0,0,1,1,1,1,1,1,0,0,1,1,2,2,2,2,2,2,2,2,2,1,1,1,11,0,1,1,1,0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,1,1,0,
    0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,1,11,11,11,11,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,
    0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,2,1,0,11,11,1,1,1,1,1,1,0,1,12,13,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,1,0,0,0,0,1,1,1,1,0,0,0,14,15,1,1,1,0,0,0,0,0,0,0,0,0,
    0,1,1,0,1,0,0,12,13,1,1,1,0,0,0,12,13,0,0,0,1,2,1,4,4,4,0,0,0,0,0,0,0,0,1,1,1,0,12,13,1,0,0,0,0,0,0,0,
    0,12,13,1,1,0,0,14,15,1,1,1,1,0,1,14,15,0,0,0,1,2,1,4,4,4,0,5,5,5,1,0,0,1,0,0,0,1,14,15,1,0,0,0,1,0,0,0,
    0,14,15,1,1,0,1,1,1,1,12,13,1,0,1,1,1,0,0,0,1,2,0,4,4,4,1,5,5,5,1,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,
    1,0,1,1,1,0,0,0,1,1,14,15,0,1,1,1,1,12,13,0,1,2,0,6,7,6,1,5,5,5,1,0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,
    0,1,0,0,12,13,0,0,1,1,1,1,0,1,1,1,1,14,15,0,1,2,0,0,2,1,1,6,7,6,1,0,0,1,0,0,1,1,1,0,1,1,0,1,1,1,0,1,
    0,0,0,0,14,15,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,2,2,2,2,2,2,2,2,2,2,1,0,1,0,0,1,0,1,1,1,0,0,1,1,1,1,1,
    0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,
    0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,
    0,0,0,12,13,0,0,0,1,1,12,13,1,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,12,13,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
    0,0,1,14,15,0,0,12,13,1,14,15,0,0,0,0,0,12,13,0,0,1,0,0,1,1,1,1,14,15,1,0,0,1,0,0,0,0,0,0,1,0,0,1,1,1,0,0,
    0,0,0,1,1,1,1,14,15,1,1,0,0,1,1,1,1,14,15,0,0,0,0,0,1,12,13,1,1,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,0,0,0,0,
    0,0,1,0,0,1,1,1,0,0,0,0,0,12,13,1,1,1,0,0,0,0,0,0,1,14,15,1,1,1,1,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
    0,1,12,13,1,1,0,0,0,0,0,0,0,14,15,1,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,
    0,0,14,15,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

//9928 - 9796 = 132
uint8_t loaded_world_section[WORLD_SECTION_WIDTH*WORLD_SECTION_HEIGHT] = {
    1,1,0,0,0,2,0,0,0,0,0,12,13,1,1,1,
    0,5,5,5,0,2,1,12,13,4,4,4,15,0,11,1,
    0,5,5,5,5,2,1,14,15,4,4,4,1,0,11,11,
    1,6,5,5,5,2,1,1,0,6,7,6,1,1,1,0,
    1,1,6,7,6,2,2,2,2,2,2,2,2,2,2,2,
    2,2,2,2,2,2,1,1,1,11,0,1,1,1,0,0,
    1,1,1,1,1,1,0,11,11,11,11,0,0,0,0,1,
    1,1,1,0,0,0,0,0,11,11,1,1,1,1,1,1,
};


int8_t x_on_grid(int16_t x) {
    x = (x / TILE_WIDTH);
    x = max(x, 0);
    x = min(x, WORLD_SECTION_WIDTH-1);
    
    return x;
}

int8_t y_on_grid(int16_t y) {
    y = (y / TILE_WIDTH);
    y = max(y, 0);
    y = min(y, WORLD_SECTION_HEIGHT-1);
    
    return y;
}

uint8_t get_tile(uint8_t xIndex, uint8_t yIndex) {
    return loaded_world_section[WORLD_SECTION_WIDTH * yIndex + xIndex];  
}

bool is_tile_solid(int16_t x, int16_t y) {
    return (4 <= get_tile(x, y));
}

bool detect_wall(int8_t x, int8_t y) {
    int8_t x1 = x_on_grid(x);
    int8_t x2 = x_on_grid(x+TILE_WIDTH-1);
    int8_t y1 = y_on_grid(y);
    int8_t y2 = y_on_grid(y+TILE_WIDTH-1);
    
    return ((is_tile_solid(x1, y1) || is_tile_solid(x2, y1) || is_tile_solid(x2, y2) || is_tile_solid(x1, y2)));
}

void draw_world() {
    for(uint8_t x = 0; x < WORLD_SECTION_WIDTH; x++) {
        for(uint8_t y = 0; y < WORLD_SECTION_HEIGHT; y++) {
            uint8_t frame = get_tile(x, y);
            //Sprites::drawOverwrite(x*8, y*8, spr_world, frame); 
            Sprites::drawSelfMasked(x*8, y*8, spr_world, frame);
        }
    }
}

(Pharap) #106

If you overload the RAM at compile time, I think the IDE will refuse to upload.
(Or at least I sincerely hope it would.)

But yes, to put things in progmem you must use the PROGMEM macro.

E.g.

uint8_t world[TOTAL_WORLD_WIDTH*TOTAL_WORLD_HEIGHT] PROGMEM =
{
    0,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,11,11,11,11,11,11,11,11,11,11,0,0,0,0,0,0,0,0,0,
    1,12,13,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,12,13,0,0,0,0,0,1,1,1,1,0,11,11,11,11,11,11,11,0,0,0,0,0,0,0,0,0,0,0,
    1,14,15,0,0,0,1,1,1,12,13,0,0,0,0,1,1,1,14,15,0,0,0,0,0,1,5,5,5,0,0,11,11,11,11,0,0,0,0,0,0,0,12,13,0,0,0,0,
    0,1,0,0,0,1,1,1,1,14,15,0,0,0,11,0,1,1,1,1,1,0,0,0,0,0,5,5,5,1,0,11,11,11,0,0,1,1,0,0,1,1,14,15,0,0,0,0,
    0,0,0,0,0,1,12,13,1,1,1,0,0,1,11,11,0,1,1,0,0,0,1,1,1,1,6,7,6,1,0,11,11,0,1,1,1,0,0,0,1,1,1,1,1,0,0,0,
    0,0,0,0,0,1,14,15,1,1,0,0,1,11,11,11,0,0,0,0,0,2,2,2,2,2,2,2,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,
    0,0,0,0,0,0,1,1,0,0,0,0,1,0,11,0,0,1,1,0,1,2,1,0,1,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,1,0,0,0,0,0,1,1,0,0,0,1,5,5,5,0,0,0,0,0,0,0,0,0,
    0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,0,1,1,0,0,0,2,0,0,1,1,0,12,13,1,1,1,0,0,1,5,5,5,5,1,0,0,0,0,0,0,0,0,
    0,0,12,13,0,1,1,1,1,12,13,1,1,1,1,0,0,5,5,5,0,2,1,12,13,4,4,4,15,0,11,11,0,0,1,5,5,5,6,1,0,0,0,0,1,1,0,0,
    0,1,14,15,1,0,0,1,1,14,15,1,1,0,0,0,0,5,5,5,5,2,1,14,15,4,4,4,1,0,11,11,11,0,0,6,7,6,1,0,0,0,1,1,1,1,0,0,
    0,1,0,0,0,12,13,0,1,1,1,0,0,0,0,0,1,6,5,5,5,2,1,1,0,6,7,6,1,1,1,11,0,0,0,0,2,1,1,1,0,0,1,1,1,1,0,0,
    0,0,1,1,1,14,15,1,1,1,1,0,0,1,1,1,1,1,6,7,6,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,1,1,1,1,1,1,
    0,0,0,1,1,1,1,1,1,0,0,1,1,2,2,2,2,2,2,2,2,2,1,1,1,11,0,1,1,1,0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,1,1,0,
    0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,1,11,11,11,11,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,
    0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,2,1,0,11,11,1,1,1,1,1,1,0,1,12,13,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,1,0,0,0,0,1,1,1,1,0,0,0,14,15,1,1,1,0,0,0,0,0,0,0,0,0,
    0,1,1,0,1,0,0,12,13,1,1,1,0,0,0,12,13,0,0,0,1,2,1,4,4,4,0,0,0,0,0,0,0,0,1,1,1,0,12,13,1,0,0,0,0,0,0,0,
    0,12,13,1,1,0,0,14,15,1,1,1,1,0,1,14,15,0,0,0,1,2,1,4,4,4,0,5,5,5,1,0,0,1,0,0,0,1,14,15,1,0,0,0,1,0,0,0,
    0,14,15,1,1,0,1,1,1,1,12,13,1,0,1,1,1,0,0,0,1,2,0,4,4,4,1,5,5,5,1,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,
    1,0,1,1,1,0,0,0,1,1,14,15,0,1,1,1,1,12,13,0,1,2,0,6,7,6,1,5,5,5,1,0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,
    0,1,0,0,12,13,0,0,1,1,1,1,0,1,1,1,1,14,15,0,1,2,0,0,2,1,1,6,7,6,1,0,0,1,0,0,1,1,1,0,1,1,0,1,1,1,0,1,
    0,0,0,0,14,15,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,2,2,2,2,2,2,2,2,2,2,1,0,1,0,0,1,0,1,1,1,0,0,1,1,1,1,1,
    0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,
    0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,
    0,0,0,12,13,0,0,0,1,1,12,13,1,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,12,13,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
    0,0,1,14,15,0,0,12,13,1,14,15,0,0,0,0,0,12,13,0,0,1,0,0,1,1,1,1,14,15,1,0,0,1,0,0,0,0,0,0,1,0,0,1,1,1,0,0,
    0,0,0,1,1,1,1,14,15,1,1,0,0,1,1,1,1,14,15,0,0,0,0,0,1,12,13,1,1,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,0,0,0,0,
    0,0,1,0,0,1,1,1,0,0,0,0,0,12,13,1,1,1,0,0,0,0,0,0,1,14,15,1,1,1,1,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
    0,1,12,13,1,1,0,0,0,0,0,0,0,14,15,1,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,
    0,0,14,15,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

And then to read from progmem you must pass an address/pointer to pgm_read_byte (to read a uint8_t) or pgm_read_word (to read a uint16_t).

In case you don’t know what that looks like:

return pgm_read_byte(&world[index]);

(Related thread here, official documentation here, relevant Arduino topic here.)


#107

Thank you, two quick questions

  1. Do I need the PROGMEM keyword and the CONST keyword in both the header file and the cpp file?
  2. It seems that the world doesn’t use any memory… when I compile it says this:

Sketch uses 9928 bytes (34%) of program storage space. Maximum is 28672 bytes.

Shouldn’t it be using more? it says the exact same thing if I comment it out in both the header and the .cpp file


(Matt) #108

That’s actually a simplification, there’s more to it than that. But that is the overall result in the end.

It’s what I did for Ardynia

I actually regretted doing it and won’t do it again for Ardynia 2. Having to fit your dungeons into a rectangle really limits how interesting they can be.


(Matt) #109

just a heads up, you will do this eventually. It’s just part of programming for this platform, you can’t be perfect 100% of the time :slight_smile: Learning how to reset it using the reset button is well worth doing.


#110

I thought bricking it meant that there was nothing to do to fix it, I didn’t know that the reset button worked for that. That’s good news!

I suppose it’s good to know this before I make my game, thanks for the heads up


(Matt) #111

Yeah you can pretty much always recover. It is possible to truly brick it, but that involves using custom bootloaders and other advanced stuff that most people don’t get involved with.

But with that said, not laying them out in a rectangle and still being efficient with progmem is quite the challenge.


#112

I’m looking into ways to store my world in PROGMEM properly and read from it, and this library seems promising…
http://arduiniana.org/libraries/flash/


(Matt) #113

That library looks fine if you want to use it. But honestly reading and writing from progmem isn’t hard. pgm_read_byte and pgm_read_word are about all you need, maybe pgm_read_ptr sometimes. They are easy to use.


#114

I just figured it out literally 5 seconds ago! I’m feeling pretty good. You’re right, no need for the library
And I think I might be able to just put the whole overworld in there without needing to load it in RAM. At least it works for a 16x8 tiles level

Here’s my code to read it:

uint8_t get_tile_progmem(uint8_t xIndex, uint8_t yIndex) {
    return pgm_read_byte(&(loaded_world_section[WORLD_SECTION_WIDTH * yIndex + xIndex]));
}

Something I’m wondering about, how do I make sure not to store PROGMEM stuff over save data or things I don’t want to overwrite?


(Matt) #115

progmem and eeprom are separate. The compiler will ensure they never cross paths. No need to worry.


#116

Okay so I have a 48x32 world right now and a ‘view’ or ‘camera’ that follows the player, for now I made it move at intervals of a screen (like Ardynia)

Collision and sprite drawing works, and I do it straight from the program data (PROGMEM), I had planned on loading the data on a 16x8 area, and I might still do it if I find it necessary later, but for now the colision detection and the tile rendering is straight from PROGMEM.

Now would be a good time to upload it on your Arduboys and check out what’s been done so far, because it’s already pretty cool https://github.com/TheProgrammer163/arduboy


(Simon) #117

Does look pretty cool.

I would spend some time and try to make the screen scroll with the player rather than update a ‘page’ at a time. This might make the game flow a little better.


#118

Thank you!

Yes I’ve planned to try it out, it shouldn’t be too difficult to set up. A big advantage of making everything 8x8 is that you can see further up and further down (I’m making a hack-and-slash so it’s important to see the enemies before they reach you)

I’ll be going to bed, have a good night and good night everyone


(Pharap) #119

You can just put it all in the header file, you don’t need to keep separating things into a .h and a .cpp unless you want the separation for some reason.

It won’t use memory unless you’re actually referencing it somewhere.

In C++ you don’t pay for what you don’t use - any functions/arrays you don’t use don’t end up in the final .hex.

Like what exactly?

In the original I’m pretty sure that’s exactly how it worked,
they just shoved the dungeons into a different RAM bank from the overworld and treated it the same as the overworld,
thus bank-switching only happened when entering and exiting dungeons.

So far nobody has 100% bricked their Arduboy.
Worst case scenario you overwrite the bootloader and have to reburn it using either another Arduino board or (from what I’ve heard the easier option) a specialised ISP programmer.

Overwriting the bootloader during normal use shouldn’t be possible, but the factor has been known to forget to set the protection fuses from time to time.

Reminds me of that time I wrote a ‘progmem pointer’ class.
You could use it just like a normal pointer, but it read from progmem and you obviously couldn’t write to it.
Such is the power of C++.

I thought you were going to be using loaded_world_section as a buffer in RAM to read world data into?

This isn’t an issue, progmem, RAM and EEPROM all have different address spaces.
If they didn’t, you wouldn’t need special functions to read from them.

For example, on the ARM Cortex chip you don’t need any special instructions for accessing the read-only memory area,
the ROM area is in the same address space as the RAM area,
so it can be accessed with a normal pointer dereference.


#120

I thought so too and I still might, but for now it works well straight from PROGMEM.

When I add the interior of houses and caves then it might become necessary, or maybe I can put everything in the same array. For caverns/dungeons. I’m also hesitant about wether or not the view should be as it is now (like Ardynia or the first Zelda on the NES) or make it follow with the player always in the center

I’ll also have to figure out a way to to know which door brings to which house. Depending on how all of this gets solved I might goback to loading a section of the world (I kept all my functions so it’s easy to revert back)


(Pharap) #121

In which case you no longer need loaded_world_section, you just need world.

I like the old Zelda style approach because then it’s easier limit the number of monsters you need by having a fixed number of monsters per ‘screen’ (which is one of the reasons Zelda did it).

Having it free-flowing runs better, but makes things slightly more difficult.

Maybe a hybrid approach?

I.e. scale up the room size so it’s maybe 4x what you have now and allow free movement within that room, but when you reach the borders either stop the camera so it doesn’t look outside the room (better for immersion but needs more compelx code) or simply have the background black (cheaper and easier but doesn’t look as good).

const uint8_t room1 = { /* blah blah blah*/ };
const uint8_t room2 = { /* blah blah blah*/ };

const uint8_t * const rooms[] =
{
	room1, room2,
};

class Room
{
private:
	const uint8_t * room;
	
public:
	Room(const uint8_t * room)
		: room(room) {}
		
	uint8_t getTile(uint8_t x, uint8_t y)
	{
		return pgm_read_byte(&room[y * width + x]);
	}
};

Room getRoom(uint8_t roomNumber)
{
	const void * ptr = pgm_read_ptr(&rooms[roomNumber]);
	return Room(reinterpret_cast<const uint8_t *>(ptr));
}

Then you just need a mechanism for relating doors to room numbers and coordinates.

That’s how roughly Pokemon does it anyway.

Whether you load ‘rooms’ into RAM or not depends on whether the environment is fixed or changeable, and exactly how changeable it’s supposed to be.


#122

I had an idea, I’m not sure how it relates (or doesn’t relate) to yours but here it is:

If it were a map (which I think is the same as a hash table? Each key (string) points to a single value?), each door would point to a different value.

x coordonate, y, coordonate. So if there’s a house at 64, 32, it points so one room and one room only. I can probably do something similar with numbers only

I think anything changeable will be entities anyway. If I’m not loading any level into RAM I can afford more entities