Evade 2 is an Arduboy game, the sequel to Evade on the same platform.
Arduboy is a credit card sized classic style game device, with joystick, two buttons, and a monochrome display. It has a 128×64 pixel monochrome screen, 2.5 megabytes of RAM, a 16 mhz ATMEL CPU core, and 32K bytes of FLASH (for program and assets). Debugging was done entirely by printing messages over the USB serial port.
Developing for the Arduboy is a great way for Modus Create developers to step outside the client/server/HTML5 world and demonstrate their abilities on a severely resources limited platform. The Arduboy makes a nice electronic greeting card to send to the friends and family of Modus Create, and a game is a fun gift.
Jay Garcia was the visionary behind the game. The game he described was a first person space shooter, and to sell this to the team, he developed a minimal prototype of a cockpit mockup with a star field effect. The original intent was to render bitmap images of enemy craft and to shoot and evade them as the game progressed.
Given these marching orders, we set out to implement a game engine that would model a 3D environment, cheating as much as possible to minimize resource usage while maximizing the potential of the device. The challenges were the limited CPU speed, nearly half the RAM (128×64 / 8 = 1024 bytes) required for the screen buffer, the need for multiple sizes of bitmap graphics so the enemies and bullets can scale as they move into, or out of, the distance, while 25% of the CPU is used to generate music and sound effects. Fitting all this into the 32K of FLASH (32K minus the boot loader!) was certain to become an issue as we came close to the end of the project.
What evolved is a “2.5D” engine with a viewing pyramid where for every 1 Z into the screen, the
X and Y dimensions increase by 1 in each direction (e.g. by 2). This math to translate world coordinates (X,Y,Z) to screen coordinates is simple to derive – X’ = X / (Z distance *2 + SCREEN_SIZE) and same for Y’. The scale of the item to be rendered is easily derived from Z distance as well.
The limitation of the engine, the part that makes it 2.5D, is that we don’t rotate the camera around X or Y or Z axes. The Camera simply moves up and down and left and right in space as the player uses the joystick.
We chose to use vector style graphics instead of bitmap images. This allows us to smoothly scale the items in the view to virtually any size. Bitmap images would have been problematic as there would be a noticeable jump as we choose an appropriate sized bitmap to render.
The graphics were drawn as SVG by Michael Tintiuc and run through a Node JS program, that Jay wrote, which converts the SVG data into coordinate data, C source code suitable for inclusion in the game. We wrote a tool in C++ to generate coordinate data for the font we use in the game. The font is rendered vector style, which allows it to trivially scale it and it is consistent with the look and feel of the rest of the game. Michael deserves credit for the terrific look and feel of the game.
The game engine focuses on Processes, Objects, Camera, Graphics, and Effects. The sound and music were developed outside the engine and we integrated it into the engine as well.
The ProcessManager manages a linked list of active Processes. A Process is simply a state machine, in a cooperative multitasking scheme. This allows us to script the AI in the game. An enemy might “patrol” (one state) until the camera/player is within a certain Z distance. Then it might seek the player (a second state) and fire bullets at the player. When the player bullets hit the enemy, it goes into an exploding state (a third state). A small number of Process structures allowed us to dynamically have some number of enemies and asteroids in the universe, and to reuse the same fixed amount of RAM over and over.
The ObjectManager manages all of the visual things in 3D space. An Object features a world X,Y,Z coordinate, a theta rotation (around Z), velocities in X,Y,Z, an image pointer (what to draw), and some state variables. There are considerably more Object structures preallocated than Process structures. There is not a 1:1 correlation, though if we had more RAM, we would have had this 1:1 relationship. The ObjectManager moves and renders all the active Objects.
In order to minimize our use of RAM, player and enemy bullets were moved from Process based to subroutine based. A Bullet::run() method iterates through the Object list and does the logic just for the player bullets. Similarly, the EBullet::run() method does the same for enemy bullets. The states for bullets are few and always the same – move, collide, time out and die.
The Camera is actually positioned at the center of the screen at Z distance of 0. It has an X,Y,Z coordinate as well as velocities in X,Y,Z. The Object rendering considers the position of the Camera to determine scale and screen coordinates.
The Graphics class focuses on point and line rendering at speed. Some of the code was lifted from the Arduboy2 library and modified for performance, while the line drawing algorithm was replaced with the Extremely Fast Line Algorithm. Point plot logic is inlined in the line drawing routine to avoid the overhead of a subroutine call for every point. We know the points are always drawn white, so we could get rid of the logic to decide whether to draw white or black. The trickiest part of the Graphics system is that the screen is physically the lower right quadrant of a coordinate system (X increases as you go right, Y increases as you go down). Mapping all X,Y so they are the center of the Object and center of the screen was a must.
Effects focus on the explosion algorithm and 2D rotation. The explosion algorithm simply takes the lines that make up an Object and moves them in X and Y (screen coordinates) based upon a fraction of their X and Y. For each step in the explosion, X’ = X + X/16, and same for Y’. It’s really that simple, and the effect is quite good. The 2D rotation allows the bullets and ships to bank and roll, which makes up for the lack of frames of animation for each.
The CPU time spent rendering lines is highly dependent on the number of points in each line. The shorter the lines in the original (full sized) graphic, the less CPU time spent rending those lines at any scale.
Enemy ship SVGs were designed to be 128×64, and we scale them down as they move into the distance.
The font, on the other hand, was designed to fit in a 9×9 pixel box for each character. Since we typically render it at 9×9, these is no need to define lines that might need to be scaled smaller. We do render the font at 2x size and at .8x size and it looks good. If we had used a bitmap font, like the Arduboy frameworks use, we’d be stuck with far less control of the font size (1x and 2x, for example). As well, we added a theta rotation angle for font rendering that rotates the text around the X axis, a nice bit of eye candy for a little bit of math.
Each star in the star field is rendered in the same way as Objects, though there is no scaling. This makes it so the star field looks correct as you move the Camera with the joystick.
Music and Sound Effects
Even though the audio system of the Arduboy is rather limited in volume, music was an extremely high priority for Jay, our lead Music producer. Like with movies, music can increase senses and overall enjoyability of video games. Without music, a video game can feel rather lifeless.
When developing our first Arduboy game, we wanted to stuff the game full of music, and we chose to use the Arduboy PlayTune library and its music format. However, we could only fit one song that was fit for gameplay. Due to limitations of PlayTune, any score was limited to two channels of audio, both of which are only for notes and the gameplay score itself is 1,331 precious bytes. After the game code was complete,wWe leveraged what little space we had to stuff in smaller songs for the intro jingle and game over screen.
For Evade 2, we wanted to pack in more music and knew that PlayTune wasn’t up to the task due to the music format being so large. Thankfully, Team A.R.G. had something called ATMLib that we could leverage. ATMLib is vastly different than PlayTune, and is more of a Music Tracker and provides four total channels of audio (3 x tones, 1 x noise) all while reducing the overall footprint of audio.
During a test-run, Jay manually converted Evade’s stage score to ATMLib format and added a percussion track. With use of this format, he noticed a nearly four fold decrease in space requirements.
— Jay Garcia (@ModusJesus) November 1, 2017
Use of ATMLib was clearly the right path for Evade 2, but there was one major thing missing — the ability to play sound effects within our game via an easy to use API. To overcome this, we enlisted the help of Delio Brignoli from the Arduboy Community. After digging in, Delio mapped out some major changes that would provide us with the SFX API and greatly increase the library’s capabilities. We forked ATMLib and made the following major changes:
- Updated score format for simpler and smaller decoder.
- Add reduced size score formats: minimal scores have 1 byte overhead (for the header).
- Command encoding designed for simpler and smaller decoder.
- Macros for manual song creation.
- C++ API built on top of C API.
- 4 channels: 3 square wave with independent programmable duty cycle + 1 noise.
- LFO and slide effects can be applied to square wave duty cycle.
- Asynchronous playback of 1 sound effect as a mono music score on an arbitrary channel with independent tempo. Music is muted on the channel used by sound effects and resumed when the sound effect is stopped or playback finishes.
- Dual timer design: 93kHz PWM carrier for improved audio quality (was 31kHz).
- 10bit PWM resolution: mixing of four 8 bit full volume channels without clipping.
- Reduced CPU usage by interrupts: interrupt rate halved without reducing sample rate, simplified interrupt handler.
- Note frequencies are closer to the expected value within the limits of the MCU’s clocksource.
- Effects can selectively disabled at compile time to reduce code size.
- Automatic interrupt and PWM disable when not in use to save CPU cycles and battery.
If you’d like to use this library, please check it out here: https://github.com/moduscreate/atmlib2
To support our efforts for ATMLib2 we forked TrackerEditor, a web-based music production tool that Team A.R.G. created. Seth Lemmons and Vadim Popa made necessary changes to our fork and published it here: http://labs.moduscreate.com/trackerEditor/.
Once the library and web UI work was complete Jay went to work on designing the music for Evade 2. It is because of ATMLib’s awesomeness that we were able to stuff an impressive eleven total songs in the game. The complete soundtrack is hosted on Soundcloud below.
Pixel art is often times mistaken for either child’s play or lack of talent. However when the game’s canvas is roughly the size of a modern app icon and the palette consists of only 2 colors it becomes evident that creating recognizable and engaging forms is enough of a challenge even for seasoned designers. In case of Evade 2, the team’s grandiose gameplay ideas and hardware limitations posed an absolutely different set of problems, both artistic and technical in their nature.
One of the first things to be designed for the game was the Evade 2 and Modus Create logos which were engraved on the back of the Arduboys. The idea was to expand on the original works by reflecting the faster paced gameplay and overall complexity of the sequel.
During the course of development art direction made several hard turns before finally landing on mostly vector based assets. As the game was coming to life it became clear that the initial sprite based approach was inefficient, resulting in less content, features and music. It would not be an exaggeration to say that the vector demo was like a breath of fresh air, showcasing fluid scaling and rotation while being more attractive and less taxing on the hardware at the same time.
Designing vector assets and the font presented a new band of challenges, we had to move away from the familiar pixel grid and rethink the overall aesthetics. The vector gems of video game history were too simplistic for a modern game that we had in mind, but provided helpful insight nonetheless. The process could not be described any better than pure trial and error. After numerous sketches, demos and self-imposed restrictions, such as specific angles and number of lines, we had finally found an appropriate style which was alien-like yet easily recognizable. The result was surprising and much more abstract than what is usually expected of a retro themed game.
Working on the HUD was particularly interesting, as we intended it to be as unobtrusive as possible yet helpful and multi-functional. It followed the same principles of appearing “out of this world” and familiar at the same time providing a layer of presence in a cockpit of a spaceship. The addition of the HUD’s animation inverse to the player’s movement during the later stages of the development was a small but powerful enhancement to the controls.
It is a must for developers and designers of bigger titles to create their own tools, however it is less common for smaller games as open-source solutions are readily available. Due to an inexplicable love for tech and a desire to streamline the “asset to code” pipeline we decided to go an extra mile and build our own tools for handling both vector and raster files, resulting in 2 tools, which are a part of the code-base:
- SVG to C header files parser
- Image to byte array parser originally written in Python with a Ruby alternative and later translated to C.
This allowed the developers to perform desired conversions straight from the command line and provided the community a more performance-oriented alternative.
Mere days before launch there was a feeling that the game could be better, that there’s room for improvement and diversity even though there was none on the hardware. Luckily the team’s effort, which was nothing short of Herculean, freed up enough space for extra music, enemy assets, behaviors, asteroids, attract-mode and credits, all of which added variety, greatly improved replay-ability and the overall fun factor of the game.
Running the later versions of the game for the first time was overpowering, watching the code, the static assets and the rest of the set pieces now working in unison to create this magical experience on a credit card sized device brought up an extremely rewarding feeling.
Overall, the game and support libraries were developed in just 4 weeks, from concept to code complete. We shipped hundreds of Arduboys flashed with the game to our clients, friends, and family.