Making a menu using graphics

Hey there fellas, so I’m trying an failing to make a menu using graphics. So it looks like this:


If the the user presses up or down the arrow should move too, and that’s where I’m failing, I can’t get the the arrow to move like I’d like to, I found code made by Pharap here that is what I’m trying to do but I had no idea how to convert it into graphics.

Here’s what I got.

    //arrow is 6 by 5
    const char arrow[] PROGMEM = {0x4, 0x4, 0x4, 0x15, 0xe, 0x4,};
    //menu is 27 by 23
    const char menu[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x51, 0x55, 0x45, 0x7f, 0x7d, 0x41, 0x7d, 0x7f, 0x43, 0x6d, 0x43, 0x7f, 0x41, 0x6d, 0x53, 0x7f, 0x7d, 0x41, 0x7d, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x41, 0x5d, 0x7f, 0x41, 0x6d, 0x53, 0x7f, 0x41, 0x55, 0x7f, 0x41, 0x5d, 0x63, 0x7f, 0x41, 0x7f, 0x7d, 0x41, 0x7d, 0x7f, 0x7f, 0x41, 0x5d, 0x7f, 0x41, 0x5d, 0x41, 0x7f, 0x41, 0x7d, 0x41, 0x7f, 0x7d, 0x41, 0x7d, 0x7f, 0x41, 0x6d, 0x53, 0x7f, 0x41, 0x5d, 0x41, 0x7f, 0x41, 0x5f, 0x7f, };

        
    void setup() 
    {
      arduboy.begin();
      arduboy.clear();
    }

    void loop() 
    {
        arduboy.clear();
        arduboy.setCursor(0, 0);

        //arrow placement
        short arrowX, arrowY;
        //displaying menu options
        arduboy.drawBitmap(99, 39, menu, 27, 23, WHITE);
        //x is the selected item
        short x = 3;

        //is the player presses up or down it will change
        if (arduboy.pressed(DOWN_BUTTON))
        {
          arduboy.print("Down.");
        }
        if (arduboy.pressed(UP_BUTTON))
        {
          arduboy.print("Up.");
        }

        //using x which stores the selected item, the arrow will change it's position
        if (x == 1)
        //like this, the arrow will be placed next to "start"
        arrowX = 98, arrowY = 40;
        if (x == 2)
        arrowX = 98, arrowY = 48;
        if (x == 3)
        arrowX = 92, arrowY = 56;
      arduboy.drawBitmap (arrowX, arrowY, arrow, 6, 5, WHITE);
      arduboy.display();
    }

You need to increment or decrement x on the button presses. The code you have is only printing out up or down. Try something like;

if (arduboy.pressed(DOWN_BUTTON) && x < 3)
{
    x++;
}
else if (arduboy.pressed(UP_BUTTON && x > 1)
{
    x--;
}
1 Like

I don’t get a notification if you forget to use the @ symbol.
E.g. @Pharap, @VivaPerosa

That code’s actually a lot more flexible and quite a bit more advanced.
The reason I wrote it in the first place was because someone was asking about how to make an equipment menu:

The code in the comment below what you linked to is actually probably closer to what you intended:


Firstly, some things I have to point out about your current code.

You don’t have to call clear after begin.
begin already clears the screen.

I don’t know why people keep doing this.
Is one of the tutorials telling people to do this?

You’re missing if(!arduboy.nextFrame()) return;.
arduboy.nextFrame() is what enforces the frame rate.

arduboy.clear() already calls arduboy.setCursor(0, 0), so this is redundant.

These variables aren’t initialised.

You do assign to them later,
but it’s still best to make sure all variables are initialised,
otherwise you could end up with junk memory,
which could lead to bugs.

This x is local, so it won’t persist past the frame even if you were modifying it.

The string literals should be inside an F macro to make sure they stay in progmem and don’t use any RAM.

Please do not use the comma operator like that. It’s a bad idea.
Try to stick to one assignment per line.
The more variables you modify per line,
the harder it becomes to track down assignment-related bugs.


Here’s a modified, fully functional version of your code:

#include <Arduboy2.h>

// The arrow sprite
const uint8_t arrowSprite[] PROGMEM
{
	// Width, Height
	6, 5,

	// Frame 0
	0x04, 0x04, 0x04, 0x15, 0x0E, 0x04,
};

// The menu sprit
const uint8_t menuSprite[] PROGMEM
{
	// Width, Height
	27, 23,

	// Frame 0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x51, 0x55, 0x45, 0x7F, 0x7D, 0x41, 0x7D, 0x7F, 0x43, 0x6D, 0x43, 0x7F, 0x41, 0x6D, 0x53, 0x7F, 0x7D, 0x41, 0x7D, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x41, 0x5D, 0x7F, 0x41, 0x6D, 0x53, 0x7F, 0x41, 0x55, 0x7F, 0x41, 0x5D, 0x63, 0x7F, 0x41, 0x7F, 0x7D, 0x41, 0x7D, 0x7F, 0x7F, 0x41, 0x5D, 0x7F, 0x41, 0x5D, 0x41, 0x7F, 0x41, 0x7D, 0x41, 0x7F, 0x7D, 0x41, 0x7D, 0x7F, 0x41, 0x6D, 0x53, 0x7F, 0x41, 0x5D, 0x41, 0x7F, 0x41, 0x5F, 0x7F,
};

Arduboy2 arduboy;

// The number of available menu options
constexpr uint8_t menuOptions = 3;

// The index of the first menu item
constexpr uint8_t minIndex = 0;

// The index of the last menu item
constexpr uint8_t maxIndex = (menuOptions - 1);

// The index of the currently selected menu item
uint8_t selectedIndex = 0;

void setup() 
{
	arduboy.begin();
}

void loop() 
{
	if(!arduboy.nextFrame())
		return;
		
	// Update the button state
	arduboy.pollButtons();

	// If up is pressed
	if (arduboy.justPressed(UP_BUTTON))
	{
		// Decrease the selectedIndex
		// (Making sure it stays in range)
		if(selectedIndex > minIndex)
			--selectedIndex;
	}
	
	// If down is pressed
	if (arduboy.justPressed(DOWN_BUTTON))
	{
		// Increase the selectedIndex
		// (Making sure it stays in range)
		if(selectedIndex < maxIndex)
			++selectedIndex;
	}

	// Clear the screen
	arduboy.clear();
	
	// Draw the menu
	Sprites::drawOverwrite(99, 39, menuSprite, 0);

	// Store potential coordinates in arrays
	static const uint8_t arrowCoordsX[menuOptions] PROGMEM { 98, 98, 92 };
	static const uint8_t arrowCoordsY[menuOptions] PROGMEM { 40, 48, 56 };

	// Read the arrow coordinates from the progmem arrays
	// (This is usually cheaper than using lots of if statements)
	const uint8_t arrowX = pgm_read_byte(&arrowCoordsX[selectedIndex]);
	const uint8_t arrowY = pgm_read_byte(&arrowCoordsY[selectedIndex]);
	
	// Draw the arrow
	Sprites::drawSelfMasked(arrowX, arrowY, arrowSprite, 0);
	
	// Update the display
	arduboy.display();
}

And here’s proof that it works:

VivaPerosa.hex (22.4 KB)


Aside from getting the code to work, I’ve made a handful of other significant changes:

  • I’ve fixed all the problems I mentioned above
  • x has been replaced with selectedIndex. Selecting good, descriptive names for your variables is important, it makes it easier to understand the code, for you and for everybody else.
  • I’ve introduced some constants (using constexpr) to make the code easier to read, and to avoid ‘magic numbers’ (‘magic numbers’ are bad because they make code harder to understand).
  • I’ve changed your images to Sprites format, so the code now uses the Sprites functions. They’re much more versatile.
  • arrowX and arrowY are now selected through array indexing rather than having a long list of if statements. This is a much more efficient way of selecting values from a list of alternatives when the selection depends on an index.
  • I’ve fixed the button handling code so it properly handles the menu index. This is implemented similarly to how @rprouse suggested, but I’ve got rid of the magic numbers and structured it slightly differently.

If there’s anything you don’t understand, please ask and I will explain it.

(I suspect if you don’t know much about progmem you’ll at least want to know more about pgm_read_byte and what & is, or perhaps why static is needed.)


There are a handful of alternative ways to do this,
but whether they’re worthwhile or not depends on:

  • Is the text being on the right hand side rather than the left hand side important?
  • How many menu options will there be?
  • Is not using the built-in font important?
    • (Having tested quickly, I think your font looks better in this case.)

Well, it’s a frankenstein version of what you made, it’s not something I would make on my own at my current skill-level.

Yeah, in crait’s tutorial had arduboy.begin(); and arduboy.clear(); in the setup function, new people are going to do what he did because we don’t what we’re doing.

Noted.

That explains the errors I kept getting.

I didn’t think there would be an issues modifying multiple variables on one line; I’ve never did it in my class assignments but my teacher had mentioned it something we could do, though I probably misinterpret what she said.

That’s a list, your code is beyond what I understand, I haven’t done advance C++… yet.

Here’s what I know:

  • Expression and Interactivity
  • Making Decisions
  • Loops and Files
  • Functions
  • Arrays
  • Searching and Sorting Arrays
  • Characters, C-Strings, and string Class

Here is what I don’t know:

  • F macro
  • uint8_t
  • That you can use uint8_t to make sprites
  • PROGMEM
  • You can set the height and width in a array like tat
  • constexpr
  • arduboy.pollButtons();
  • static
  • Sprites
  • drawOverwrite and drawSelfMasked

Basically I know nothing but the basics.

Originally it was called menuSelected but I kept editing the code (swapping names and stuff like that) so much that I got confused, threw my hands in the air, said “forget it”, and changed it to x.

Is the text being on the right hand side rather than the left hand side important?

To stay consistent I wanted the menus to be on the right side.

How many menu options will there be?

I haven’t decided, I’m making an RPG so there will be a few.

Is not using the built-in font important?
(Having tested quickly, I think your font looks better in this case.)

Yeah, I’m using a custom font you made, I like the font a lot so I’m sticking with it.

Edit: One last thing, here is pseudocode of what I’m trying to do.

short gamestate = 0;

void loop() 
{ 
  switch(gamestate) 
  {
  case 0:
	//this of course would be the title screen
	//by assigning gamestate a number it can change which screen it's on
	gamestate = shortFuncition();

   break;
  case 1:
	//this would be the start screen
	//these screeens wouldn't do anything other than display which screen it is
	//user would press button "A" to return to the title screen
    break;
  case 2:
	//this would be the credit screen
    break;
  case 3:
	//and this would be the control screen
    arduboy.display();
  }
}

short shortFunction();
{
	/*
	the main idea for this function to be used over and over again through out the game
	the name of the function does not reflect what i want it to do
	but this is just a proof of concept so the name really doesn't matter to to me
	once i plug it in into a main code ill name it properly
	looking at your code i hope to change menuOptions depending on when shortFunction is called
	example: title screen has 3 options, in-game menu has 6 options, npc dialog has 2 options, etc
	when the user press "A" selectedIndex is returned
	in this case selectedIndex will be returned to gamestate
	*/
}

That explains so much.

Technically you can do it, but from experience I’d say it’s better to avoid it.

If you stick to one assignment per statement and one statement per line then it makes it a bit harder to make mistakes and a bit easier to see what’s happening on each line.

Also if you really must do more than one assignment per line, use semicolons, not a comma.
The comma you used is an obscure thing called ‘the comma operator’ which most people probably don’t know about, and although it technically behaves as you intended, it also means you’re technically doing multiple assignments in a single statement instead of having two statements on a single line.

The only bits I’d class as ‘advanced’ is the use of PROGMEM, pgm_read_byte, static and &.

constexpr isn’t commonly used (unfortunately), but it’s not advanced.


I’ll start with the easier stuff and move on to the harder stuff.

constexpr is a C++11 feature.

When applied to a variable it means ‘this variable is constant and can be evaluated at compile time’.
It’s like const, but better. It’s what you should use for constants.
(The tutorials are teaching people to use #define, but that’s a really bad way of doing constants.)

(‘Compile time’ is when the program is being compiled,
‘run time’ or ‘runtime’ means when the program is being run.)

This is a feature of Arduboy2.

This stores the current state of the buttons so it can be compared against the previous state of the buttons,
which is what allows the justPressed and justReleased functions to know when a button has just transitioned from being not pressed to being pressed (and vice versa).

justPressed is only true on the first frame that a button is pressed,
after which it remains false until the button is released and pressed again.

Without calling pollButtons, justPressed wouldn’t work properly.

Without justPressed it would be very hard to accurately select a menu option because using pressed instead would mean that the option selector would fly past the second option too quickly.

uint8_t is an unsigned integer type with a width of precisely 8 bits (i.e. an ‘octet’ or (arguably) a ‘byte’).

It’s one of the fixed width integer types introduced in C++11.
Technically because Arduino uses the C standard library rather than the C++ standard library, it’s actually C’s version, but the definition is equivalent.

uint8_t is actually what you’re supposed to use.
You’re not supposed to use char.

The function signature for drawBitmap is:

static void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE);

(Don’t worry too much about what the * means.)

It’s a similar situation with Sprites, the Sprites functions all use const uint8_t *.

The Sprites class is a more flexible way to draw sprites.
There are several different functions that draw sprites in different ways,
which you can read about here.

drawOverwrite directly draws the sprite to the screen,
replacing whatever was underneath.

drawSelfMasked only draws the white pixels of a sprite.
The black pixels won’t be drawn.

It’s not setting the width and height of the array, it’s putting the image’s height and width in the array.

That’s what the Sprites functions expect - they expect the width and the height to be the first two values in the array.
That’s so that you don’t have to provide the width and the height every time you call the drawing functions, because that wastes memory.

static has many different meanings in many different contexts.
This this case the arrays being static means that they behave similarly to global variables rather than local variables.

If this confuses you, just make them global instead (i.e. remove the static and put them outside the function).

All AVR chips (like the ATmega32u4 found in the Arduboy) have different memory sections:

  • ROM/Progmem (or ‘flahs memory’), which can be modified, but not while the program is running, and persists after the device is turned off (i.e. non-volatile memory)
  • RAM, which is readable and writable at runtime, but gets erased when the device is turned off (i.e. volatile memory)
  • EEPROM, which is readable and writable at runtime, and it persists after the device is turned off (i.e. non-volatile memory)

Progmem is where all the code and some of the data is kept.
There’s more progmem than RAM.

To specify that you want a variable to be stored in progmem, you have to use the PROGMEM macro,
and to retrieve data from progmem you must use one of the pgm_ macros,
which you can read about here.

To read something from progmem using one of the pgm_ macros,
you have to give it the memory address of the data you want to read (every byte in progmem has a distinct ‘address’),
which is what the & is for - getting the address of a variable.

To understand the F macro you first need to understand progmem, so read the above section first.

Normally if you just write a bare string,
that string will be stored in progmem and then copied to RAM later.
In general you want to avoid the string being copied into RAM,
which is what the F macro does.

The F macro makes sure that your string is only ever kept in progmem,
so it doesn’t eat into your RAM, which is important because RAM is limited.


If the progmem stuff confuses you too much then you can get rid of the arrays and just do this:

uint8_t arrowX = 0;
uint8_t arrowY = 0;

switch(selectedIndex)
{
	case 0:
		arrowX = 98;
		arrowY = 40;
		break;
	case 1:
		arrowX = 98;
		arrowY = 48;
		break;
	case 2:
		arrowX = 92;
		arrowY = 56;
		break;
}

// Draw the arrow
Sprites::drawSelfMasked(arrowX, arrowY, arrowSprite, 0);

But it’s generally a little more expensive because it’s not quite equivalent.

Although, I now see that there’s actually a pattern here,
so it’s possible that you could do:

uint8_t arrowX = 0;

switch(selectedIndex)
{
	case 0:
		arrowX = 98;
		break;
	case 1:
		arrowX = 98;
		break;
	case 2:
		arrowX = 92;
		break;
}

const uint8_t arrowY = (40 + (selectedIndex * 8));

// Draw the arrow
Sprites::drawSelfMasked(arrowX, arrowY, arrowSprite, 0);

Instead.

I could rewrite the code to cut out some of the other stuff you don’t understand if you want (e.g. get rid of Sprites), but I don’t really want to cut out too much because the techniques I’ve demonstrated are generally better ones than the alternatives.

When learning to program it’s important to not let new things scare you off because programming by its very nature means you’ll regularly be encountering and learning new things.


Fair enough.

It’s just a little bit more expensive that way, and a little bit more difficult to program.
If it were on the left side then you’d be able to draw the arrow on the same x coordinate each time and just change the y coordinate.

I made?

Edit:

Oh, now I remember!


Is that font going to be used a lot in your game by the way?

If it’s just for the menu then plain old bitmaps will suffice,
but if you’re intending to use it all throughout your game then you might be better off having a custom font rendering class.
(Presumably you don’t have the skill to do that at the moment, but I’m sure I or someone else could help you.)

You edited this in just as I finished posting. :P

There is actually a better way to do this,
but it involves learning another new thing.

Here’s the demo code:

// Include Arduboy2 library
#include <Arduboy2.h>

// Define a scoped enumeration called 'GameState'
// with the size and characteristics of a 'uint8_t'.
enum class GameState : uint8_t
{
	// The possible values that a 'GameState' object can store
	TitleScreen,
	Settings,
	Credits,
	Gameplay,
};

// Declare an 'Arduboy2' object called 'arduboy'
Arduboy2 arduboy;

// Declare a 'GameState' object called 'gameState',
// initialising it with a value of 'GameState::TitleScreen'
GameState gameState = GameState::TitleScreen;

// Define a function for setting the game state
void setGameState(GameState newGameState)
{
	gameState = newGameState;
}

// The 'setup' function, as required by Arduino
void setup()
{
	// Initialise the Arduboy
	arduboy.begin();
}

// The 'loop' function, as required by Arduino
void loop()
{
	// If it's not time to draw the next frame
	if(!arduboy.nextFrame())
		// Exit the function
		return;

	// Update the button state
	arduboy.pollButtons();

	// Do all updating
	update();

	// Clear the screen buffer
	arduboy.clear();

	// Do all drawing
	draw();

	// Update the screen
	arduboy.display();
}

void update()
{
	// Choose an update function to call depending on the current state
	switch(gameState)
	{
	case GameState::TitleScreen:
		updateTitleScreen();
		break;
	case GameState::Settings:
		updateSettings();
		break;
	case GameState::Credits:
		updateCredits();
		break;
	case GameState::Gameplay:
		updateGameplay();
		break;
	}
}

void draw()
{
	// Choose a drawing function to call depending on the current state
	switch(gameState)
	{
	case GameState::TitleScreen:
		drawTitleScreen();
		break;
	case GameState::Settings:
		drawSettings();
		break;
	case GameState::Credits:
		drawCredits();
		break;
	case GameState::Gameplay:
		drawGameplay();
		break;
	}
}

//
// TitleScreen updating and rendering
//

void updateTitleScreen()
{
	if(arduboy.justPressed(DOWN_BUTTON))
		setGameState(GameState::Settings);
}

void drawTitleScreen()
{
	arduboy.println(F("Title Screen"));
}

//
// Settings updating and rendering
//

void updateSettings()
{
	if(arduboy.justPressed(UP_BUTTON))
		setGameState(GameState::TitleScreen);

	if(arduboy.justPressed(DOWN_BUTTON))
		setGameState(GameState::Credits);
}

void drawSettings()
{
	arduboy.println(F("Settings"));
}

//
// Credits updating and rendering
//

void updateCredits()
{
	if(arduboy.justPressed(UP_BUTTON))
		setGameState(GameState::Settings);

	if(arduboy.justPressed(DOWN_BUTTON))
		setGameState(GameState::Gameplay);
}

void drawCredits()
{
	arduboy.println(F("Credits"));
}

//
// Gameplay updating and rendering
//

void updateGameplay()
{
	if(arduboy.justPressed(UP_BUTTON))
		setGameState(GameState::Credits);
}

void drawGameplay()
{
	arduboy.println(F("Gameplay"));
} 

Obviously the real thing would have more complex conditions than just whether the up or down buttons were pressed,
but the point is to demonstrate how to transition from one state to another.

The enum class (technically ‘scoped enumeration’) is a special construct designed for doing precisely this sort of thing.
Underneath the compiler magic it’s still using integers,
but instead of remembering what state ‘0’ is and what state ‘1’ is,
you can just use names instead.
It’s also type safe, so you can’t accidentally do:

// Oops, 10 isn't a valid game state
setGameState(10);

Using an enum class you get a compiler error,
which you wouldn’t get with short.

And here’s the demo .hex:
StateMachine.hex (19.8 KB)


By the way, short on the Arduboy is 16 bits, so it’s equivalent to int16_t (and int for that matter).

If you only need 0 to 255 then you can use uint8_t (or unsigned char).
If you only need -128 to 127 then you can use int8_t (or signed char).

Using smaller types is cheaper.

By the way, I don’t know if you’ve been taught this or not (because I know nothing about your programming course):

The core C++ types (char, short, int and long) are actually different sizes on different platforms, which is why the fixed width types were introduced - to provide a standard way of indicating types of a certain size.

As a side note, AFAIK @crait 's guides with begin() & clear() have been included by @bateske in the canonical PDF https://aws1.discourse-cdn.com/standard14/uploads/arduboy/original/2X/1/1338e75510f67c39ff0e20268cd6d3879762aa8b.pdf
Perhaps worth reviewing for state of current library, etc?

So do this.

x = 1; y = 2;

Instead of this?

x = 1, y = 2;

It does look like something I’ve seen in class.

There’s a Arduboy Library

Thank goodness, I was going to ask if one exists, but oh boy, there is a lot to learn but I have an entire Summer I’ll be fine.

So if a variable like arrowCoordsX acts like a global variable why not just make it global variable instead oppose to static?

So it allows me to use strings without using ram? Neat.

I came into programing and Arduboy knowing I know nothing but the main motivation is creating games. I love technology (software and hardware) so it was only natural for me to gravitate to programming, I like programming even when I hit road blocks.

I have a request: could you add comments to it? My brain broke trying to understand what you did.
Also what is : and ::?

Knowing nothing about Arduino I used short because it’s the only C++ variable I know that used less memory, at least now I know uint8_t and int8_t exist.

I didn’t think I’d be overwhelmed by a open-sourced Gameboy but here we are.

Edit: At this point I’m not even sure where to start with the Arduboy now, like do I try to understand how it works first or should I make this game while learning. I’m thinking about doing the latter.

Preferably

x = 1;
y = 2;

Your code was already making use of one,
but given that you were following crait’s tutorials I suspect it was making use of the old library that is no longer maintained.

Because it’s only being used by one function.
Making it static means that only the function it’s declared in can ‘see’ and make use of it,
and it allows it to be defined close to where it’s used.

If more than one function needed it then it would make sense to make it global instead.

Generally speaking the only time I’d make a static variable in a function is for a one-time-use lookup table like the one used here.
Apart from that they’re actually quite uncommon in general.

If it weren’t for the need to save memory by storing in progmem then I wouldn’t even be using it in the first place.
When programming for desktop I never use them.

That’s the idea.

Also you should generally only use it if you’re giving the string directly to the print or println function (on arduboy, or on Serial),
because in most other cases it won’t make sense.

(I won’t attempt to explain why, because I’d have to explain half a dozen other things first. The mechanism behind F is actually quite complicated.)

That’s good, because you’ll never stop hitting them. :P

It’s actually not all that different from what you did in your ‘pseudocode’.

Though in your case you’re returning the value of the next state and in my case the functions are setting the next state through a function (which is more flexible, but also has a higher chance of being misused).

I’ll add comments when I get chance.

Edit:
I’ve edited the earlier comment to include comments in the code.
If it’s not enough, just ask more questions.
I can give better answers when the questions are specific,
otherwise I’m left guessing what has and hasn’t been understood.

I’m not sure what you mean by :.
You mean the colon in enum class GameState : uint8_t?

If so, that means you’re asking for uint8_t to be the ‘underlying type’ of the enum class (technical name: ‘scoped enumeration’).
That means that when the compiler compiles the code, it will make sure GameState has the same size and binary representation as uint8_t.
Without that, the default underlying type is int, which is 16 bits on Arduboy, which is larger than the type needs to be,
so specifying : uint8_t saves memory.

As for ::, that’s the ‘scope resolution operator’.
It’s another thing that has slightly different meanings depending on how it’s used.

In the case of GameState::TitleScreen et cetera it’s needed because you can’t just say TitleScreen on its own.
If you just said TitleScreen the compiler wouldn’t know where to look, it needs you to specify GameState::TitleScreen.

That’s because of situations like this:

enum class Colour
{
	Red,
	Green,
	Orange,
};

enum class Fruit
{
	Apple,
	Pear,
	Orange,
};

If you were allowed to just say Orange then how would the compiler know whether you meant Colour::Orange or Fruit::Orange?

Another case you’ve seen :: used with is Sprites.
That’s because the functions in the Sprites class are static functions (an example of where static has a different meaning), so you can access them directly through Sprites wtihout making a sprites object (e.g. Sprites sprites;).
Some people prefer to make an object, others prefer not to.
I prefer to avoid making an object for various reasons.

Having keywords and symbols with different meanings in different contexts might seem confusing,
but it’s something that occurs in natural language too.
For example, the word ‘bark’ can mean tree bark or the barking of a dog, and a ‘mine’ could be a place where minerals are excavated or an explosive device.

The reason many keywords have different meanings is because the comittee that decides which features go into the language prefers to reuse old keywords instead of adding new ones.

Firstly, short is a type, not a variable.
If you say short x;, x is the variable, short is the type.

And secondly short doesn’t use ‘less memory’.
I’m assuming you mean 'less memory than int', in which case short and int are actually the same size on Arduboy - they’re both 16 bits (or 2 bytes).

If I had £1 for every person I’ve encountered who thought “oh I know, I’ll learn how to make games, it’ll be easy”, I could probably afford to upgrade my GPU. :P

The fact of the matter is that programming is hard, and so is making games.
Pretty much anything to do with technology is difficult.

But so is playing an instrument, and upholstery, and clock making, and blacksmithing…
Pretty much any skill worth having is difficult to learn.
Anyone who tells you otherwise is lying to you.

The important thing is to not let the difficulty put you off.
If you want to develop any skill (especially programming) then you have to be determined.

You don’t really need to understand the electronics side of things to be able to make games.
There are some low level things that it helps to know about,
but in general you don’t need to know that much because the library already takes care of communicating with the hardware for you.

Just start small and work your way up.
Make a hello world program, make a menu, start with some relatively simple games.
When you’re just beginning even games you consider to be simple (space invaders, pacman, noughts and crosses et cetera) will actually be quite difficult.

There’s no rush, you can take as long as it takes.
The important thing is to not give up.
The only failure is to not try.

This is the first I’ve heard of a ‘cannonical PDF’.

The problem is finding someone who has the time to do so.

To be fair, when I saw:
easy%20to%20learn
And reading through @crait’s tutorial it definitely looked easy but now I’m confused. Like I don’t know what to do, as of now all I can say is that I can confidently make a hello world.

My original plan was to build pieces of a game in separate ino files (menuSelect.ino, worldGeneration.ino, etc.) then splice it together.

Then make a hello world and add to it bit by bit until it’s something else.

Or try doing that.
(Though .h files would be better than .ino files.)

Like I say, start with small things and work your way up.
Nobody’s expecting you to magically understand everything straight away.

Forget about it, I’ll figure something out.

If it helps at all, this code is roughly equivalent to the code using an enum class without using an enum class:

#include <Arduboy2.h>

constexpr uint8_t TitleScreen = 0;
constexpr uint8_t Settings = 1;
constexpr uint8_t Credits = 2;
constexpr uint8_t Gameplay = 3;

Arduboy2 arduboy;

uint8_t gameState = TitleScreen;

void setGameState(uint8_t newGameState)
{
	gameState = newGameState;
}

void setup()
{
	arduboy.begin();
}

void loop()
{
	if(!arduboy.nextFrame())
		return;

	arduboy.pollButtons();

	update();

	arduboy.clear();

	draw();

	arduboy.display();
}

void update()
{
	switch(gameState)
	{
	case TitleScreen:
		updateTitleScreen();
		break;
	case Settings:
		updateSettings();
		break;
	case Credits:
		updateCredits();
		break;
	case Gameplay:
		updateGameplay();
		break;
	}
}

void draw()
{
	switch(gameState)
	{
	case TitleScreen:
		drawTitleScreen();
		break;
	case Settings:
		drawSettings();
		break;
	case Credits:
		drawCredits();
		break;
	case Gameplay:
		drawGameplay();
		break;
	}
}

void updateTitleScreen()
{
	if(arduboy.justPressed(DOWN_BUTTON))
		setGameState(Settings);
}

void drawTitleScreen()
{
	arduboy.println(F("Title Screen"));
}

void updateSettings()
{
	if(arduboy.justPressed(UP_BUTTON))
		setGameState(TitleScreen);

	if(arduboy.justPressed(DOWN_BUTTON))
		setGameState(Credits);
}

void drawSettings()
{
	arduboy.println(F("Settings"));
}

void updateCredits()
{
	if(arduboy.justPressed(UP_BUTTON))
		setGameState(Settings);

	if(arduboy.justPressed(DOWN_BUTTON))
		setGameState(Gameplay);
}

void drawCredits()
{
	arduboy.println(F("Credits"));
}

void updateGameplay()
{
	if(arduboy.justPressed(UP_BUTTON))
		setGameState(Credits);
}

void drawGameplay()
{
	arduboy.println(F("Gameplay"));
} 

But this isn’t as good as the version using the enum class because it lacks some of the important benefits of using an enum class:

  • It’s not type-safe (i.e. you could easily do gameState = 10; by mistake)
  • You have to assign the values yourself, so you could accidentally give two names the same value (e.g. you could accidentally make Credits = 2; and Gameplay = 2;)
  • There could be a clash with the state names if you tried to use them for something else (e.g. if you tried to do something like char Credits[] PROGMEM = "Programming: VivaPerosa\nArt:VivaPerosa";)

(You could use short instead of uint8_t, but gameState would then use 2 bytes instead of 1 byte.)

Okay, I’m doing the basics but I ran into a problem. I’m simply making a code that says something when “A” button is pressed, when I used this code it doesn’t work.

#include <Arduboy2.h>
Arduboy2 arduboy;
  
void setup() 
{
  arduboy.boot();
}

void loop() 
{
  arduboy.clear();
  arduboy.pollButtons();

  uint8_t aButton = 0;

  if (arduboy.justPressed(A_BUTTON))
  {
     aButton = 1;
  }

  if (arduboy.justPressed(B_BUTTON))
  {
    aButton = 0;
  }

  if(aButton == 0)
  arduboy.print(F("A BUTTON NOT PRESSED"));
  else
  arduboy.print(F("A BUTTON PRESSED"));
  
  arduboy.display();
}

But when I use static aButton or make aButtona global variable it works just fine, why? Shouldn’t it work regardless if aButton is a local variable or global?

#include <Arduboy2.h>
Arduboy2 arduboy;
  
void setup() 
{
  arduboy.boot();
}

void loop() 
{
  arduboy.clear();
  arduboy.pollButtons();

  static uint8_t aButton = 0;

  if (arduboy.justPressed(A_BUTTON))
  {
     aButton = 1;
  }

  if (arduboy.justPressed(B_BUTTON))
  {
    aButton = 0;
  }

  if(aButton == 0)
  arduboy.print(F("A BUTTON NOT PRESSED"));
  else
  arduboy.print(F("A BUTTON PRESSED"));
  
  arduboy.display();
}

‘Code’ is uncountable, like sand and water.
You don’t “make a code”, you just “write code”,
just like you don’t “drink a water”, you “drink water”.

This is another one of those mysteries I really wish I could solve.
I don’t think I’ve ever seen any experienced programmers say “a code”,
so I don’t know where people get it from.

No, because local variables are destroyed at the end of the function they’re declared in.

If you want something to persist between calls to a particular function (e.g. loop) then you must make the variable global.

So your code should look more like this:

#include <Arduboy2.h>
Arduboy2 arduboy;

void setup()
{
	arduboy.boot();
}

uint8_t aButton = 0;

void loop()
{
	if(!arduboy.nextFrame())
		return;

	arduboy.pollButtons();

	if (arduboy.justPressed(A_BUTTON))
	{
		aButton = 1;
	}

	if (arduboy.justPressed(B_BUTTON))
	{
		aButton = 0;
	}

	arduboy.clear();

	if(aButton == 0)
		arduboy.print(F("A BUTTON NOT PRESSED"));
	else
		arduboy.print(F("A BUTTON PRESSED"));

	arduboy.display();
}

While I was at it I added the mising if(!arduboy.nextFrame()) return;.
That’s necessary for the frame rate limiting mechanism to function properly.

I also moved the calls to pollButtons and clear.

Usually it makes more sense to clear just before you start drawing things and to pollButtons just before you start handling input.

Because (to get technical for a moment) static local variables and global variables both have what’s called “static storage duration”,
which means they are stored in the section of RAM reserved for global variables.
This means they exist for the entire duration of the program and aren’t destroyed when a function ends.

Local variables have “automatic storage duration”, which means they are stored somewhere else (either in things called ‘registers’ or on a thing called ‘the stack’ or ‘the call stack’) and they are destroyed when the function ends.


While I’m at it, I’d like to introduce you to a new type.
You might already know about it, I’m not sure,
but it’s a very important type so I’ll mention it anyway.

The bool type can hold either true or false,
and it’s the type you should use whenever you have something that should be represented as true or false.

In this case aButton probably makes more sense as a bool:

#include <Arduboy2.h>
Arduboy2 arduboy;

void setup()
{
	arduboy.boot();
}

bool aButton = false;

void loop()
{
	if(!arduboy.nextFrame())
		return;

	arduboy.pollButtons();

	if (arduboy.justPressed(A_BUTTON))
	{
		aButton = true;
	}

	if (arduboy.justPressed(B_BUTTON))
	{
		aButton = false;
	}

	arduboy.clear();

	if(aButton)
		arduboy.print(F("A BUTTON NOT PRESSED"));
	else
		arduboy.print(F("A BUTTON PRESSED"));

	arduboy.display();
}

A lot of people seem to think that using an integer type and treating 0 as false and 1 as ‘true’ is fine,
but if it’s a question of whether something is true or false (and there are no other options) then you should always use bool because that’s what bool is designed for.

Part of the reason you might see other people use 0 and 1 is because back in the early days of C (before C99) there was no bool,
but C++ has always had bool (or at least it has since the first standard, C++98).

That was a typo I meant to type writing code.

Oh, that makes sense.

But I didn’t use setFrameRate or does Arduboy2 has it’s own default frame rate?

Huh, that explains when I use uint8_t aButton and press “A” nothing happens or I see “A BUTTON PRESSED” appear quickly. Before I was questioning why use static but I’m starting to see why.

I have written bool functions before but not many assignments I wrote required it so using bool hasn’t came to mind; actually I did have to use bool a lot in VB.

At this point I’m starting to have a better understanding of Arduboy2 either tonight or when I wake up I’ll I’m going to start messing with sprites.

1 Like

To clarify, there seems to be a group of people that go around thinking code is countable and saying either “a code” or “codes” in a programming context.

I’ve never been able to figure out where this habit comes from,
whether it’s due to mistranslation from other languages or whether there actually is an English-speaking (or partially English-speaking) country out there where getting it wrong (i.e. treating it as countable) is the norm.

Either way, whenever I see someone use ‘code’ as if it were countable,
I correct them because I want to help prevent it spreading.

(Also it drives me (and various other programmers) nuts. :P)

Arduboy2 sets the framerate to 60 by default in the begin function.

Precisely.

Prefer to use global variables rather than static local variables.
static local variables should be avoided in general.

Like I said before, the only time I ever use them is on Arduboy when I’ve got a ‘lookup table’ that’s only used in one function.

Also there are certain situations where static local variables behave differently and can end up being more expensive.

They’re still teaching VB!?

You have my sympathies. It’s an awful language.

(VB was what they taught me in college.)

For the record, what I’m telling you about bool and how locals behave versus globals applies to C++ in general, not just the Arduboy,
just in case you were getting the impression that the language used to program the Arduboy is Arduboy (or even Arduino) specific.

Glad to hear it though.
There’s quite a few threads around with info about sprites,
and obviously there’s the documentation.

For a moment I thought I was on to something.

VB is also taught at my college, I only took it because one of my professors said “If you’re serious about programming make sure to take [langauge1], [langauage2], and Visual Basic.” When I was taking VB my professor said that VB is obsolete but gives us a good idea on how to build a user interface - she was right. When we had to deal with one windows form it was fairly easy but when we had to deal with multiple forms that had to work together was when things got difficult before I dropped the class we was doing parallel arrays and was about to do databases - I could’ve only imagine how hard that would’ve been for me, I’ll find that out next year.

When I talk about Arduboy2 I’m talking about the library.

Honestly I’m using @crait’s tutorial as a base, using your code as a “this is how you should do it”, the documenation on the other hand is kind of useful? In this case aside from drawErase I’m having a hard time understanding knowing what each function does, so I just threw stuff at a wall to see what sticks and this is the code I came up with that moves a sprite around.

#include <Arduboy2.h>
Arduboy2 arduboy;

const uint8_t vbSprite[] PROGMEM
{
  // Width, Height
  16, 16,

  // Frame 0
  0xfc, 0x2, 0x1, 0xe1, 0x41, 0x81, 0x81, 0x81, 0x41, 0x21, 0x21, 0x11, 0xf9, 0x1, 0x2, 0xfc, 0x3f, 0x40, 0x80, 0x87, 0x82, 0x81, 0x81, 0x81, 0x82, 0x84, 0x84, 0x88, 0x9f, 0x80, 0x40, 0x3f, 
};

int8_t vbSpriteX = 30;
int8_t vbSpriteY = 30;

void setup() 
{
  arduboy.boot();
}

void loop() 
{
  if (!arduboy.nextFrame())
  return;

  if (arduboy.pressed(LEFT_BUTTON))
  vbSpriteX -= 1;
  if (arduboy.pressed(RIGHT_BUTTON))
  vbSpriteX += 1;
  if (arduboy.pressed(UP_BUTTON))
  vbSpriteY -= 1;
  if (arduboy.pressed(DOWN_BUTTON))
  vbSpriteY += 1;
  
  arduboy.clear();
  Sprites::drawSelfMasked(vbSpriteX,vbSpriteY, vbSprite, 0);
  arduboy.display();
}

So now I vaguely know how to move a sprite, next order of business to add a background.