Arduboy Classes and Header files?

When I started developing a game for my arduboy, using my logic from python and pygame, I thought

Why can’t I just make a class for my sprites, each with their own image, rect, update, and draw function?
And why cant I just put them into a header file?

So I created a header file in my project and transferred the litany of logic and variables into a sprite object. When it would not compile, I added the flags that arduino wanted and still, no compile.
Then importing the file into my main game, that would not compile because it could not find the header file even though it was in the same directory.

Giving up I went and put the object into my main file and created an instance as a variable to reference in loop(), only for THAT to not compile because it could not find the clearly global object. Putting it in loop? Still could not find it. Bewildered I just gave up and returned to the messy code solution I had before, which I hated doing as Im a more object-oriented programmer.

How could you properly create a sprite object in a header file than use it in your main game?

When making the class, make sure you make the objects you want to be global in “public” if that wasn’t obvious.

Another thing is to make sure you don’t put your actual functions in the header. Have a .cpp that corresponds, and import the headers there.

Another thing is to make sure you actually imported the header. This sounds obvious but it’s happened before and it took me hours to realize. Import with quotes, not <>.

If you are trying to reference Arduboy2 from the header, make sure your class has an initialization list that sets up the reference for arduboy as a private variable. Should keep your functions working.

This is the best I can help with unless you provide some source. That would greatly help us. Good luck!

If you had the code to show and the error messages it gave then there are plenty of people who could have helped debug it.
Without that it’s impossible to know what went wrong, but if I had to guess I’d say there were syntax rules you weren’t following (e.g. you missed a semicolon). C++ has lots of rules and generally stricter syntax than more modern languages.

When programming for the Arduboy it’s important to get a balance of OO and procedural. Too much OO and you chew up memory because OO features generally cost memory (especially virtual functions), but equally not using OO features results in much messier code.

Normally I’d say don’t have a sprite class because it would need its own x and y coordinate, which would most likely lead to duplication because your player/entity will probably already have an x and y, but I’ll give an example anyway.

Also note that you don’t have to have both a .cpp and a .h, you can get away with just a .h in most cases.

// Sprite.h

class Sprite
{
public:
  // Up to you whether you getter+setter these
  // It probably won't have a performance cost either way

  const uint8_t * bitmap;
  uint8_t frame;

  // I'm using int16_t because I assume you might want to go off the side of the screen
  int16_t x;
  int16_t y;

public:
  Sprite(const uint8_t * bitmap);
  Sprite(const uint8_t * bitmap, int16_t x, int16_t y);
  Sprite(const uint8_t * bitmap, uint8_t frame, int16_t x, int16_t y);

  void draw(void); // void in the brackets is optional
}; // Note the semicolon, this is mandatory

// Sprite.cpp

// Required
#include <Sprite.h>

// Note the scoping
Sprite::Sprite(const uint8_t * bitmap)
  : bitmap(bitmap), frame(0), x(0), y(0) // C++11 field initialisers
{
}

Sprite::Sprite(const uint8_t * bitmap, int16_t x, int16_t y)
  : bitmap(bitmap), frame(0), x(x), y(y)
{
}

Sprite::Sprite(const uint8_t * bitmap, uint8_t frame, int16_t x, int16_t y)
  : bitmap(bitmap), frame(frame), x(x), y(y)
{
}

// Again, note the scoping operator
void Sprite::draw(void)
{
  Sprites::drawOverwrite(this->x, this->y, this->bitmap, this->frame);
}

I’d like to reiterate though that this is wasteful for several reasons.

On top of the potentially duplicated state, you’re left with the issue that there are many different ways to draw a sprite, so you’re forced to either have multiple classes for each case (in which case the object is no more than a glorified functor) or to incorporate a mechanism for choosing a drawing function (which means yet more RAM usage).

To someone used to everything being classes it probably seems backwards to think that an object might not be the recommended solution, but OO comes at a cost and that cost is sometimes too much for the embedded world. You have to know when to use an object and when to just use functions even if it’s not as flexable or doesn’t look as clean.

1 Like

You put public: twice. Did you mean private: for the first? Stylistically you don’t even need private: either but it helps document things.

Nope, I put public twice so that it would be easy to change the former to private if getters and setters were added.

Plus I like to use the access specifiers to help group related things (e.g. fields, functions, constants, type aliases), even if consecutive access specifiers are the same access level.

I also almost never omit an access specifier unless I’m trying to type something really quickly for the exact reason you mentioned: explicitness.

1 Like

Welp. Guess I’ll just be using the function method ¯\_(ツ)_/¯

OOP.
Sometimes you only have to do something once (like, do something ONCE in the void loop) (like the “homescreen” of games). But then OOP tell you to create a void for it. It does not use any more RAM but sometimes it will be

  1. complicated
  2. More difficult to find
  3. greater chance for syntax error

They do are complied almost the same way, though. What sohuld I do?
By the way, I can say that the syntax in C/C++/C#/java was strict, but that is what you end up with most of the time. (In fact it makes more sense that way, something I compare to the English language itself.) Python was another story, but you will have a difficult time indenting.

1 Like

I think you mean “create a function”.

void is a keyword that effectively means “no type”.
You cannot create ‘a void’ because it literally means “the absense of a type”, an object of type void cannot exist.

Creating functions to break up large functions into smaller functions has nothing to do with OOP, it’s just general programming style.

In the case of your “homescreen” example, the reason for moving that into its own function is to make the containing function smaller.

As a rule of thumb there are two times you should consider making more functions:

  1. When an existing function is very long
  2. When the same block of code is repeated multiple times

The “homescreen” example is a case of apply rule 1.


I disagree that creating a function makes something more complicated, more difficult to find or increases the possibility of syntax error.

Have you got any examples to show when it is more complicated or increases the possibility of syntax error?


As for strictness, programming languages generally have to be strict because of the way parsers work. I won’t go into too much detail because it’s a very big topic, but basically having a strict syntax means it’s easier to write a program to read the language. The less strict you make the language, the harder it is to write a parser for.

2 Likes

Like I say, there are cases where classes are useful, it’s just that drawing sprites isn’t one of them.

Stuff like the player and the enemies are usually good candidates for classes.

Ultimately though, you don’t want everything in classes anyway, despite OOP purist beliefs there are cases where free functions make more sense than objects.

If you want an example of something where the majority of the code is packed into objects rather than free functions, take a look at Easter Panic.
(I never got round to cleaning it up properly, but bearing in mind it was made in 2 days it’s quite ‘clean’.)

1 Like

I think I will follow the style guide I saw earlier.

Which style guide?

A generic C++ style guide?

No, it was this thread about structuring arduboy code, can’t pinpoint it

Its probably @Pharap’s article in the magazine.

1 Like

In case it is, it’s Volume 10 pages 34 to 37 and Volume 11 pages 20 to 24.

And in case it was something in the magazine, here’s the magazine link:

2 Likes

It was actually with .arduboy files. The magazine articles are a little helpful but I want to share the game I made in practice and get feedback on my first time writing C code.

C or C++? C doesn’t have classes.

C as in C syntax, so C/C++

It’s best to try to avoid the “C/C++” misnomer,
C and C++ are two (very) different languages.
Despite popular belief C is not a subset of C++.

In fact, if you look at the language, it will be arduino, with a possible mix from C, C++, and C# (C Sharp)
Like there is multiple ways of unsigned short. One of the other ways was uint16_t, which should be C (if I remebered correctly), and ushort will be C#. In java you will call it as Char, but that is more for characters.
Still, syntax are also similar. (speaking of syntax, java was also comparable in this field)

There is no “Arduino” language.
It’s just C++ with a bit of preprocessing and some different libraries (including a custom version of C’s stdlib).
(There’s a even header file where they put typedef unsigned char byte; to make the byte type. It’s Arduino.h line 126.)

uint16_t is both C and C++.
The difference is that in C’s stdlib they’re in the stdint.h file and in C++'s stdlib they’re in cstdint.

All Java types are signed, so there’s no equivalent to C++'s unsigned short or C#'s ushort (which aren’t exactly the same by the way, unsigned short doesn’t have to be 16 bits but ushort does).

But you’re right in as much that they all share a similar syntax.
All found of them (C#, C, C++, Java) could compile the following function in the right context:

int SumNumbersTo(int count)
{
  int result = 0;
  for(int i = 0; i < count; ++i)
  {
    result += i;
  }
  return result;
}

As could many other languages.

But there are small details where semantics interfere.
For example, this is valid C but not valid C++:

int* test = malloc(100 * sizeof(int));

As is this:

struct s { int a; int b; };
union s { int a; int b; };
2 Likes