Click “Run it anyway”:
← | go left (moving disables flapping) |
→ | go right (moving disables flapping) |
z, ↑ | flap! (don’t hold too long, best to hold for about 0.3s at a time) |
Backspace | Go to start screen |
Enter | Go to Game Over |
This is a game I made as part of a class assignment in the summer of 2017. I wrote it in C using structs, pointers, etc. , running “bare metal” on the Game Boy Advance with no operating system.
Why?
While modern, or “high level” , languages have often “abstracted away” such nitty-gritty details of hardware through fancy software trickery, its still important to have an appreciation for how hardware works in order to write better and more efficient code.
Much like how pilots who train how to fly in a Cessna can have a better “aircraft feel” than those that train in simulators even when working with larger aircraft, a programmer can make costly algorithmic mistakes if he/she’s only worked with higher level languages and is not familiar with what’s happening “under the hood”, as others have noted +
I’ve worked mainly with Java in my computer science courses for data structures and algorithms and Python for AI, but I’ve also enjoyed the opportunity to write things such as linked lists, memory allocation, paging algorithms, etc. in C as part of my college curriculum.
I’ve also learned the fundamentals of computing working up to logic and circuits from the transistor level, to processor design and assembly language, to memory caching, instruction pipelining, etc. It’s much harder to keep track of the pointers, memory, and debugging with tools like valgrind when working with C, but it can ultimately be very rewarding to work at the lowest levels of computing for being able to write “go fast” code, as well as for practice and educational purposes.
Emerging languages like Rust are beginning to offer some of the speed advantages akin to C/C++, but the affordances the language offers to programmers makes it easier to write more complicated code with less memory safety mistakes, pointer issues, etc.
For instance, in the talk below, a poorly implemented data structure in Rust performed better than a well written one in C because the backing data structure implementations were able to be written with more complicated and performant algorithms.
Tech Details:
Most of the display work is done through Mode 3 and Direct Memory Access (DMA) (for the image, background, etc) as per the scope of this assignment. This is much more demanding on the GBA than doing sprite-based graphics, so some performance considerations were made to keep the game running smoothly.
The vertical pipe obstacles are generated by DMA’ing a green pipe, and a trailing blue background color behind it constantly to keep the pipe moving to the left while “erasing” its previous position from the frame buffer in memory.
The score text is only drawn after every “screen wipe” by the pipes rather than every frame. The score text draws pixel-by-pixel and would have been too expensive to re-draw every frame without slowdown or complicated optimization.
Because the GBA doesn’t have native floating point capability (i.e. decimal numbers for movement speeds, etc.), the bird’s velocity is only updated on certain frames as to allow for a kind of “fractional” change in velocity over time. This means that gravity and velocity changes from movement input only “kick in” on certain frame intervals.
I definitely would have implemented input buffering of some kind (so that a “flap” button press could be registered at any time with a press, and will not keep adding to the velocity if held down) if I had more people play-test this before completion.