[WIP] Defenduino - A Defender clone for the Arduboy


(Alex M) #1

Hi there. I recently acquired an Arduboy and have been working on a version of Williams’ classic arcade game Defender.

Progress Video 1
Progress Video 2
Source.

It’s been a long time since I’ve done any C++ and I’m enjoying re-learning it, as well as coping with the restricted RAM of the Arduboy. Hopefully I can pack in everything that made the original great, though the amazing sound effects might be a stretch.


(Simon) #2

This is looking really good.

I noticed in the video that the player stops dead when you let go of the buttons. It might be a bit more realistic if you continued on your original path until you pressed another button. Alternatively, the ‘friction’ in space could slow you down gradually to a stop.

If this didn’t make sense, have a look at my Juno First game > https://felipemanga.github.io/ProjectABE/?hex=https://raw.githubusercontent.com/eried/ArduboyCollection/master/Action%2FJunoFirst%2FJunoFirst.V1.00.hex

I haven’t had a chance to look at your code so not sure how hard or easy my suggestion might be.


(Alex M) #3

Thanks for the feedback. Actually there is a bit of inertia, but you’re right that it’s not too obvious in the video. I should probably reduce the friction level a bit to make it feel closer to the arcade version.


(Pharap) #4

I think this is the first time we’ve had someone host code on Bitbucket.


To clarify for people that don’t realise that space doesn’t actually have ‘friction’.
(Despite the fact hollywood think it does - see ‘Space Friction’.)
In real space you keep going until you apply force to slow yourself down - there is no friction.

I wrote a game like that once (dodging asteroids in space). It confused people that the was no friction.
Having no friction added to the difficulty though because people had to remember to manually slow down.

That said, you can always claim that the ship has thrusters that automatically slow it when you aren’t trying to accelerate it.
It’s a more realistic explanation than ‘space friction’.


(Alex M) #5

And in this case you’re flying over a planet, which presumably has an atmosphere that is slowing down the craft. :slight_smile:

I tend to use Bitbucket because they gave free private repositories before Github did, and now I just like having everything in one place.


(Josh Goebel) #6

OMG, awesome - just watched the video. I always thought Defender would play great on this platform (with the 2:1 screen, etc). If you need any help with optimizing or speed/size, feel free to ask.


(fred ) #7

This looks great! Another classic I’ll play on arduboy.


(Alex M) #8

Got the scanner working, tweaked inertia, and added catching/dropping of humans (not shown in the video, 'cos it’s tricky at the best of times and harder when squinting at it through your phone.)

Video


(Josh Goebel) #9

I remember thrust coming out of the back of the ship when you accelerated (Commodore 64).


(Alex M) #10

Yeah, that’s on the to-do list. :slight_smile:


(Josh Goebel) #11

@McChessers I see you debugging memory usage. Were you having issue running out of RAM?


(Alex M) #12

Yes. Early versions were way overengineered and I had to refactor and simplify it. There’s probably still a lot more optimization/simplification I could do, but I’ll cross that bridge if/when things get tight again. Maintaining a linked-list of gameobjects to iterate over each frame seems to make sense until you remember that each pointer costs you two bytes per object, and bytes aren’t cheap.


(Josh Goebel) #13

Yikes, yeah you don’t want to bring much larger system programming principles to chips this small. If all your objects are similar (in size) you can use typecasting and have like a single large array for objects… vs an array for people, enemies, etc… giving you some flexibility if the ratios of things changes during gameplay.

Would you consider adding a LICENSE file to your repo?

Do you know how to use avr-objsize or are you just shooting blindly at reducing the size of things?


(Alex M) #14

No, not doing any kind of profiling. Just looking for savings where I can find them.


(Josh Goebel) #15

objsize isn’t profiling. I just analyzes your compiled executable and can tell you the exact compiled size of every single object/method. Though to get full benefit of the report you have to turn off inlining on the file you run the report against. So you can look and see that say you have 28k total and you have e SINGLE function that’s wasting 3,000 bytes all on it’s own, that’s a good place to go looking for flash savings… vs spending time trying to optimize a function that’s only 200 bytes.

IE, go after the bigger fish first instead of just swinging blindly at everything.


(Pharap) #16

Presumably you have a lot more private repos than you do public.

I must admit the idea of only having public repos put me off at first,
but I soon realised that most of my public repos hardly ever get read anyway,
so effectively they’re as good as private.

Linked lists are rarely a good data structure to use.
On modern systems they thrash the cache, and even on old systems they have an awkard amount of overhead.

I’m going to throw out some unsolicited suggestions anyway.
Just disregard them if you aren’t interested.

I’d recommend looking into ways to avoid virtual functions.

As soon as you add virtual functions it introduces a ‘vtable’,
which means an extra (hidden) pointer is added to the class and an array of pointers to functions is hidden somewhere.
(I haven’t determined if it ends up in progmem or in RAM though.)

Also I think inheritance in general can end up using additional memory by causing common code to be duplicated once per class in some cases,
but it’s been a while since I’ve tested that so I can’t remember the specifics.

Using inheritance for things like Flaggable and Sprite are pretty much a no-go on embedded systems.
The amount of added overhead is generally too wasteful.

In the case of Sprite, if you change your images to use the Sprites format (i.e. prefix the image data with the width and height) and use the Sprites::drawOverwrite instead of arduboy.drawBitmap for rendering then you don’t need to keep the width and height in RAM.

As for flaggable, you’re quite possibly preventing functions from being inlined.

Either way, Flaggable being a parent class doesn’t really give you any benefit because the flags mean different things for different kinds of objects - i.e. it breaks the liskov substitution principle.

If you’re just using it for the sake of utility then you’d be better off using free functions rather than resorting to inheritance.
In many other OO languages free functions either aren’t possible or are sometimes frowned upon,
but in C++ they’re a viable alternative and sometimes an invaluable tool.

And you could provide a ‘magnitudeSquared’ function for your vectors so you can avoid using sqrt in certain circumstances.
E.g. if you’re just comparing the magnitude of two vectors then you don’t need the exact magnitude,
you can compare the magnitude squared instead because the relative scale will be maintained.
That’s usually used as a speed ‘hack’, but could result in better memory usage in some circumstances.


Going off on a tangent briefly:

Talking about inheritance reminded me, the Humanoid inherits MovingGameObject inherits GameObject approach is considered somewhat old fashioned these days due to its inflexibility.
Modern games tend to be moving towards ‘component-based architecture’.

The downside of the component approach is that it’s marginally more expensive,
but the amount of flexibility it affords greatly outweighs the downsides.

It’s not really applicable to an embedded system though,
it’s only really suited for desktop programming or at least reasonably powerful consoles.

Thought I’d mention it on the off-chance that it’s of interest.


By the way, your use of Font3x5 appears to be violating the licence because you aren’t including the licence and copyright notice.


(Alex M) #17

Thanks for the feedback. I’ll definitely take it into consideration. Some comments below:

My original architecture was a lot more reliant on virtuals - the base GameObject had an “update” method that would be overridden, for example, but I did away with them for the reasons you list. I kept the one in the State class though, as I felt it worth the tradeoff to not have a big switch statement to hit every frame.

Yeah, I’m aware of it and initially considered it, but it was obviously too heavy. I figured inheritance solved some of the same problems without needing to keep lots of pointers to components around.

Thanks for catching that. I’ll get it fixed.


(Pharap) #18

A big switch statement would actually probably work out cheaper in terms of memory.
Vtables are surprisingly expensive when you don’t have much memory to work with.
(Alternatively using an array of function pointers to create a jump table might be similarly cheap. Sometimes the compiler optimises switch statements into jump tables anyway.)

Obviously it would be a bit more awkward to maintain,
but the scale of your game will be limited anyway,
so there’s an upper limit to the scale of the switch.

I say this from experience.

I wrote a game called Minesweeper that uses a traditional state system, where every state has an update, draw, activate and deactivate virtual function.
(With a little twist in how the states are allocated - instead of allocating them on the heap, they all share the same block of statically allocated memory thanks to a bit of wizardry.)

Then @filmote adapted it for one of his own games and found that changing the behaviour to use a switch statement and non-virtual functions made it significantly smaller.

Yeah, I wasn’t suggesting it for use on the Arduboy,
just pointing out that deep object hierarchies are a bit of a nuisance.


(Simon) #19

And I am still using it! I am not convinced that the memory overlaying technique you were using actually worked properly as I experienced excessive RAM use as well. It should have worked but I think the compiler has other ideas.


(Pharap) #20

It worked in Minesweeper.
I suspect there’s something elsewhere that’s causing the problem.
I forget which game you used it in.