The second in what may be a never ending series:

An acceptably simple ‘card’, powered by Adafruit’s PyGamer board and CircuitPython.

The final completed ‘card’.

Building a breakout board would have been too time consuming for a one-off project, so after laying out the parts the task of soldering things together didn’t seem impossible with enough work, and some kind of simple PCB for the controller. The most intricate connections utilize connectors from the factory, like the display, joystick, battery, and buttons. These parts were chosen for because those connectors simplify things.

The original design was to be a standard 5”x7” card, as that’s an accessible form factor for printing (cheap and easy) and is just the perfect amount of space to create relatively simple art. Copper foil was intended to be used, but given the amount of lines between parts space was too limited.

As one can see from the poor PCB my soldering skills need work. This isn’t the end however, it just mean the project has pivoted in form factor.

So it has instead become a card and tiny handheld game.

Short intermission - Card Artwork

PyGamer to the rescue! I am most comfortable working in python, so I was always intending to use CircuitPython and associated libraries. This board takes all the peripherals I need (buttons, screen, joystick) and integrates them in to one board so assembling the project is massively simplified. The big trade-off in this case is no longer being a 5”x7” card.

PyGamer board. Photo credit Adafruit

Making the game

The game for this project was heavily inspired by the “TileGame” example from Adafruit. The background tiles and heart tile in fact were directly pulled from their repo and used with minimal modification. The use of a state machine was also kept as demonstrated in the example, and extended to include different states representative of what all is going on. It’s very straightforward and works well enough, but it is not the only way to handle keeping track of everything. Python is used for its simplicity, and it performs well though it could be better optimized.

The PyGamer board has another advantage over the original ItsyBitsy M4 that was chosen: Stage and uGame. These together are a library of helper functions for working with sprite and tile based graphics, but are partly written in C to increase performance. In the context of this project it means that not every board’s firmware will come with these modules and it may be required to rebuild python which introduces more headaches into a project that was behind schedule for some time. Purchasing the board that comes with the modules saved me crucial time, but there’s no reason a development environment can’t be setup to recompile this yourself if you' are using a different board compatible with CircuitPython. It does however mean some tinkering may be required with regards to looking at what libraries can safely be removed from your specific environment. I spent some time tinkering with the DisplayIO library, but the performance for animated sprites just was not there.

A simple library for controlling the joystick and buttons was created for use in the main menu as this context does use DisplayIO graphics, which do not mix with Stage graphics. This also means that when using the uGame helper library for joystick and button control, the screen wouldn’t actually render graphics unless stage specifically was used.

Gimp was used for the creation and modification of game graphics. Keep in mind that stage does have limitation. Graphics should be 16 pixels by 256 pixels, which each singular sprite being a 16x16 square from the sprite sheet. There is a 16 color limitation, including the alpha color for transparency, and images should specifically be indexed bitmap files. Consulting Adafruit’s documentation and learning pages were crucial in learning the finer details for this project.

The full project is available (under the MIT license) on my GitHub.

Improving the code

The code is far from perfect. There are a number of optimizations that can (and may) be made:

  • Code Structure: The code could benefit from better organization and modularization. Splitting the code into separate functions or classes can improve readability and maintainability.

  • Variable Names: Some variable names are not descriptive enough, making it harder to understand their purpose. Using meaningful names that accurately reflect the variables' roles and contents will aid in debugging.

  • Magic Numbers: There are several magic numbers scattered throughout the code. Magic numbers are hard-coded values that lack context, making the code harder to understand and modify. It would be better to define constants with descriptive names for these values. This is mostly limited to UI elements and frame numbers in the animation, but there are some position areas where raw pixel values are used, which is suboptimal.

  • Redundant Conditions: In some parts of the code, there are redundant conditions that can be simplified. I have a habit of using if isInverted == True, though if isInverted works just as well and can make the code cleaner.

  • Optimizing Sprite Updates: The update methods for the Rose, Mob, and Hero classes have repetitive code for frame updates, movement, and collision checks. Refactoring these methods can eliminate redundancy and improve efficiency.

  • Optimizing Game Loop: The game loop could be refactored to remove duplicated code and improve readability. Organizing the code into separate functions responsible for different tasks, such as handling input, updating game logic, and rendering the game will speed things up and further aid in debugging game logic.

  • Eliminating Unused Code: There are some unused variables and commented-out sections of code that could be removed to improve clarity and reduce clutter. These were kept because things are “working” but it is best practice to get rid of unused code.

The Recipe:

Electronic Components and tools:

All Electronic parts ordered from Adafruit or Digi-Key

Code:

Previous
Previous

Valentine's 2022

Next
Next

I love you, to the moon and back