Rooftop Rescue


(Bert Veer) #1

rooftop

Hello, the last couple of weeks I’ve been working on a small Arduboy game. This is beta, please let me know if you encounter any bugs. Enjoy!

https://github.com/BertVeer/Rooftop


(Simon) #2

What a great little game. Was it inspired by a game and watch or similar?

Great graphics too!


(Bert Veer) #3

Thanks! The game is indeed a remake of the game & watch ‘Towering Rescue’ from Gakken. I still have it.


(Stephane C) #4

Looks really sharp. One of the best looking game for sure. Good job!


(Scott) #5

Just a few notes after a quick glance at the code:

  • It’s better to use EEPROM.update() instead of EEPROM.write(). Even though, in your case, you may never write the same value that’s already stored, it still good practice.

  • You don’t need your sound_on flag. You’re only using it to mirror what ardu.audio.enabled() would return. You can just use the ardu.audio.enabled() function directly to determine if sound is on or off.

  • You don’t need to call ardu.idle() at the end of loop(). It will be called in ardu.nextFrame() while waiting.

  • If the Arduino IDE Compiler warnings preference is set to All you get some warnings. You may wish to address these.

  • In your GitHub repository, it would help if you create a folder called rooftop and move rooftop.ino and the .h files into it. This way it’s easy for a user to install the game into the Arduino sketchbook just by moving or copying the rooftop folder to it. They won’t have to deal with the -master or other suffix that may be added to the downloaded folder name, plus the sketchbook won’t contain any files not required to compile the game.


(Bert Veer) #6

Thanks for the helpful hints, Scott. I will address these ASAP.

On another note, are there any other Arduboy-related sites where I should plug my game?


(Pharap) #7

You can post about it anywhere if you want to try to bring the Arduboy publicity
(e.g. some people post on Twitter),
but this is the only domain that’s directly owned by Arduboy Inc as far as I’m aware, and thus is the only official Arduboy forum.

Aparently there is an Arduboy subreddit, but it’s unofficial and I have no idea if anyone actually uses it.
All the most well known users use this forum.


By the way, I’m readlly glad to se you using constexpr, bool and the fixed width integer types.

One suggestion I’d like to make is that you should avoid using new and delete.
E.g. Game& game = *(new Game()); ought to just be Game game = Game(); or even just Game game; (which will still call the default constructor).

I don’t know if you know the difference between dynamic memory and static memory or not.
If you don’t I can explain it.


(Holmes) #8

CUUTE! If you upload the .HEX file, we’ll be able to use the online emulator to test it out.


#9

Great game. Love it!

It’s in the bin folder. So you can Play online


(Simon) #10

Have you posted it to @eried’s and @crait’s game repositories?


(Bert Veer) #11

Thanks for checking! I’m using new here because I don’t want my object hierarchy to occupy the stack. I’m aware that runtime allocations should generally be avoided on embedded systems.


(Pharap) #12

Global objects* aren’t allocated on the stack,
they’re allocated in a particular part of RAM designated for global variables.

* Technically ‘statically allocated objects’ is the more correct term.

Local variables are allocated on the stack (though they may only live in registers if they’re small enough and they have short lifetimes) and dynamically allocated objects are allocated on the heap.

Basically the arrangement is a bit like this:
malloc-std
(Bearing in mind that only some chips have access to external RAM. The Arduboy has no external RAM as far as I’m aware.)

And on AVR chips, the heap and the stack areas ‘grow’ into each other,
so actually you’re not buying any more memory for the stack than you would by using static allocation.

You may actually be using slightly more through dynamic allocation because dynamic allocation has additional bookkeepingdata for tracking the allocated memory blocks, whereas static data doesn’t need any overhead because the addresses are known at compile time rather than runtime.

Hopefully that all makes sense?
The bottom line is that you aren’t actually gaining anything by using new.
And in fact the RAM usage reported by the compiler will be inaccurate because it only reports the memory usage of statically allocated variables (i.e. it doesn’t include dynamic memory or the maximum depth that the stack will reach).

There are some occaisions where using new might make sense, but static is almost always the better option.

Some of the times when dynamic allocation makes sense are:

  • When the maximum amount of memory needed can’t be decided at compile time
  • When being able to deallocate objects to reclaim memory will provide a tangible benefit

I forgot about that.

@Prototype, if you haven’t, there’s instructions about how to submit to Eried’s repo here:


#13

Just a a suggestion / request. The A button doesn’t do anything during game play. How about add it as alternative rope control. No rope: lower it on A press, Rope down: rise rope on A press


(Bert Veer) #14

Thanks for the detailed explanation. I wasn’t quite sure how the memory was arranged on the Ardu. However, statically allocating the game object would take up 90% or so of dynamic memory, producing the dreaded warning. This ‘hack’ put it in storage space, so I kind of went on with that.


(Bert Veer) #15

It was like that at one point, but it felt more consistent and easier to explain to have dedicated buttons. Not sure, I might reconsider it.


(Pharap) #16

I make it 83%.

Before:

Sketch uses 25662 bytes (89%) of program storage space. Maximum is 28672 bytes.
Global variables use 1471 bytes (57%) of dynamic memory, leaving 1089 bytes for local variables. Maximum is 2560 bytes.

After:

Sketch uses 24662 bytes (86%) of program storage space. Maximum is 28672 bytes.
Global variables use 2136 bytes (83%) of dynamic memory, leaving 424 bytes for local variables. Maximum is 2560 bytes.

But the thing is, that memory is being used regardless of whether you do it statically or dynamically.

new does not put it in ‘storage space’.

What the compiler is calling ‘storage space’ is what the rest of us normally call ‘progmem’ (technically it’s flash memory) and ‘dynamic memory’ is what the rest of us normally call ‘RAM’.
(I.e. the compiler calling RAM ‘dynamic memory’ has nothing to do with ‘dynamic allocation’.)

Progmem is a type of flash memory that can only be read from by the program,
the program can’t write to it.
(It can be erased and then written to in blocks, which is what ‘flashing’ a .hex does, but that can’t really be done from within a regular program.)

RAM on the other hand is both readable and writable, and where all your writable objects live.

The reason the progmem usage (‘storage space’ as the compiler insists on calling it) goes up when you use new is because the code that makes new work is being added to the code area.
The reason the RAM usage (‘dynamic memory’ as the compiler insists on calling it) goes down when you use new is because the memory is then being allocated at runtime rather than compile time, so it’s still being used but it’s not being reported.

Hopefully that makes sense.


Out of interest, sizeof(Game) is 677 bytes.

static_assert(sizeof(Game) == 677, "");

(Bert Veer) #17

Yes I do have to dig deeper into the hardware if I want to do more Ardu development. This was my first sketch so I didn’t want to bother with memory management that much. Just get rid of the warning :slight_smile:

I do appreciate your input however and will certainly look into it.


(Pedro) #18

This game is so good and so neat! Great job! I will update my top Arduboy games.


(Bert Veer) #19

Hi guys, I updated the repository with all the suggested improvements. Thanks for all your replies and considerations thus far.

@Pharap: I understand my trick will just allocate the same memory at runtime, however I don’t see any other apparent solution to get around the warning. What is the common approach to this, besides cutting down as much as possible on globals?


(Pharap) #20

This is a bit of an X-Y problem.

The problem isn’t “there’s a warning”, the problem is what the warning is warning you about: the lack of free RAM.

Using dynamic allocation doesn’t fix the lack of free RAM, it only shuts up the warning because the compiler can’t detect your dynamic allocation, so it can’t warn you.
Essentially it’s the same result as ignoring the warning.
(Except it uses more progmem and perhaps eats a bit more RAM because of the bookkeeping overhead.)

The usual approach is to either cut down on RAM usage or just ignore the warning.

I could help you to find ways to reduce the amount of RAM in use,
but it would have to be a bit later because I’m a tad busy at the moment.

I had a very quick look and the first obvious thing I saw was in the Rope struct.
Instead of having 5 bools taking 1 byte each you could have just 1 byte and use bit flags.

E.g. something like:

constexpr uint8_t moving_mask = (1 << 0);
constexpr uint8_t stopped_moving_mask = (1 << 1);
// etc

uint8_t flags;

bool is_moving() const
{
	return ((this->flags & moving_mask) != 0);
}

void set_moving(bool value)
{
	if(value)
		this->flags |= moving_mask;
	else
		this->flags &= ~moving_mask;
}

// etc

(There’s a slightly better way using enum class, but I don’t have time to explain that right now, so I’ll explain later.)