Periodically save game data in EEPROM

(Filipe Madureira) #1

Hello,

what’s the best way to periodically save data in the EEPROM during gameplay?

The solution i’m using is this function:

void saveGame() {
  if (arduboy.everyXFrames(255)) {
    EEPROM.update(100, water);
    EEPROM.update(101, food);
    EEPROM.update(102, happiness);
    EEPROM.update(103, sleep);
    EEPROM.update(104, health);
  }
}

If i’m calling this function every loop() and my framerate is 30 this means i will save data in the EEPROM every 8,5 seconds, is this correct?

I’m sure there’s a better approach at this, any ideas?

0 Likes

(Simon) #2

You could save it after you increase / decrease one of the settings.

But unless you are saving the player’s position and other environment settings, it seems a little wasted as you cannot simply turn the arduboy off and back on and pick up where you left off.

1 Like

(Pharap) #3

Pack all the data you want to save into a single struct and then save that struct.

So have a struct like this:

struct SaveData
{
	uint8_t water;
	uint8_t food;
	uint8_t happiness;
	uint8_t sleep;
	uint8_t health;
};

And then do something like this:

constexpr uint16_t saveStartAddress = 100;

SaveData saveData;

void saveGame()
{
	EEPROM.update(saveStartAddress, saveData);
}

Or like this:

constexpr uint16_t saveStartAddress = 100;

void saveGame(const SaveData & saveData)
{
	SaveData * eepromPointer = reinterpret_cast<SaveData *>(saveStartAddress);
	eeprom_update_block(&saveData, eepromPointer, sizeof(SaveData));
}

Or a mixture of the two.

Personally I’ve found that eeprom_update_block and the rest of the avr-libc family uses less memory than the EEPROM library, even if it’s slightly less convenient.

It’s correct, but probably not the best way of saving.
You probably won’t burn the flash out because you’re using update (which doesn’t write to eeprom, but still reads from eeprom),
but it’s better to either have a manual trigger (e.g. an option on a pause menu) or have an event-based trigger for saving (e.g. touching a checkpoint).

You can use the millis function to get the number of milliseconds elapsed and you can use that to do more accurate timing, so you could save every minute or something.

Or if your game has a day and night cycle (presumably you’re making a survival game) you could save at the end of each day/night, or each quarter of a day (morning, noon, evening, night).

1 Like

(Kevin) #4

Yeah in general it is best practice only to write to EEPROM as the result of a user action, like selecting save or something.

2 Likes

(Filipe Madureira) #5

In this case, it’s a virtualpet-like game; it’s costantly running and i want to give the user the option to “reset” the game, just in case.

The reason why I want to periodically save in EEPROM it’s to give a person the ability to turn on and off the game and find its virtual pet in a similar state from which the pet was the last time the game was turned off or the battery died.

Does it make sense? How resource-intensive is saving to the EEPROM?

At the moment the game is designed to decrease the stats by ~ 0.001 or similars every single frame, again, i’m not sure if this is a good idea.

@Pharap Thank you so much for all the info. I’ll implement my solution in the next few days and report back!

1 Like

(Pharap) #6

It’s probably best to do a ‘quicksave’ feature, where the player can choose an option to save before they switch off rather than just switching off.

Saving takes about a fraction of a frame, so getting the player to pick an option from a menu won’t be that much slower than allowing the player to just turn the Arduboy off at any point.

The only real advantages would be to prevent the player escaping a bad decision/event (i.e. they could reset their game to before something bad happened) and/or to prevent progress being lost if the battery suddenly runs out.

A few notes about virtual pet games:

Once you turn the Arduboy off, time is effectively ‘frozen’ from the pet’s perspective.
The Arduboy doesn’t have a real time clock, so there’s no way of figuring out how much time has passed, so you can’t even simulate the lost time after turning the Arduboy back on.

Some other people have experimented with the Arduboy’s ‘watchdog timer’ to try to put the Arduboy into a low power state that periodically wakes up to keep everything running.

You might be better off using large integers rather than floats because the Arduboy doesn’t have an FPU (floating point unit), but I’ve never actually run any tests to compare the processing speed/progmem size for each approach.

Another alternative is to accumulate frames with one variable and increment a stat when that variable passes a certain number of frames.

Or if you want to be framerate independant, use millis to accumulate time and make changes based on actual time passing rather than frames.

0 Likes

(Josh Goebel) #7

I’d consider just saving every minute or so… if someone is running it for hours/days losing a minute (or even a few minutes) is likely to go completely unnoticed.

0 Likes