- Learn Linux
- Learn Electronics
- Raspberry Pi
- LPI certification
- News & Reviews
This is a follow on from my guides on creating vector images on a Raspberry Pi Pico with display pack and showing bitmap images and animations on a Raspberry Pi Pico. This guide is how to show how you can create sprites using bitmap (raster) images on a Raspberry Pi Pico.
This guide is based on the use of MicroPython and the port of games or programs from other Python frameworks such as pygame zero. This will explain some techniques that can be used to create games using the Raspberry Pi Pico or that could be applied to other microcontrollers for creating games.
The example that I have used is from my book Beginning Game Programming with Pygame Zero. This uses the final game in the book called Space Shooter. It will not implement the full game, but will show how you can create a sprite (Actor) and move it around the screen similar to how it is created int that game. The image file used is also included in the source code (see later).
In the earlier page on showing images on the Pico I have explained how the data is stored in a byte array, and in particular how the colors are stored in a reduced format referred to as RGB565.
In that article the raw format file was used to store a full image, the size of the screen. For this example I have created a modified format based around the same raw data format, but also adding the width and height as the first two bytes. This is needed to know when one line ends and the next starts. The dimensions are stored as one byte each, which allows a sprite with a maximum image size of 255 x 255 pixels.
I also wanted to provide a way of including transparency in the images. As the data format uses two bytes for each pixel and due to the restricted memory I didn't want to add more information. I therefore chose the null colour #000000 with both bytes are 0. This means that if the sprite includes any black pixels which should not be transparent then they need to use a different colour. I have added 1 to the least significant bit of the blue colour (based on the RGB565 format) which gives a RGB colour of #000400. For a user then this colour looks the same as the black colour.
I created a python program called create_sprite.py to converts from a PNG format image to the raw sprite format.
Similar to my previous example where I ported a game from Pygame Zero to the Pico I decided to make the code similar to creating a game using Pygame Zero. To achieve this I have created an Actor class similar to how this works in Pygame Zero. The Actor class is a cut down version to reflect the processing capability of a microcontroller and as a starting point for further development. Some of those aspects are explained.
The constructor requires the display object so that it can access the display information. It takes the filename for the sprite (in the sprite format mentioned earlier) and a position argument relating to the centre of the sprite. It is not currently possible to use a different anchor point.
The x and y positions for the Actor class can be accessed directly. These will then be picked up when the draw method is next called. The x and y positions can be set to any value and if the image is off the screen it will not be displayed, but will still suffer a similar performance as if it was (due to the processing to determine if any part is on the screen).
There is also an attribute called enable_transparency. This is set to True and should not normally be changed, but can be if you have a sprite where you would rather use a true black colour rather than using that for transparency information.
The image can be changed by updating the image property. This will update the filename and call the setter method which will load the image directly.
There is also a property for an angle. Due to the performance of the Raspberry Pi Pico microcontroller this does not support degree level rotation. It can however be used for rotation in 90 degree increments, which can be handled more efficiently. The angle is rounded to the nearest 90 degree position. The angle value can be positive or negative. It will automatically handle increments going beyond 360 or -360 degrees, but it should not be set to values that are significantly higher or lower than those values.
These 90 degree rotation can be useful for games where there is a 90 degree increment of the rotation. This could be useful for games such as tetris or top down adventure games. If a different angle is required then that could be implemented using different image files which would need the calling function to handle the rotation of the image.
The draw method will update the display buffer at the appropriate position based on the x and y positions. It needs the display buffer to be passed to the method. The display buffer will be updated but it will not update the screen until the main program calls the display update method.
The other methods within the class are used internally. They are not prefixed with an underscore to restrict their use. Some methods that may be potentially useful are the get_rotate_height and get_rotate_width which could be used to return the width and height based on the current rotation of the actor, whereas the width and height properties do not reflect any rotation.
There are other methods using the Pygame Zero actor class that could be useful, but the aim of this was to provide the basics which can then be extended upon based on the needs of your game.
For example I haven’t yet added any of the collision detection code. The use of transparent pixel color means it should be possible to implement that looking for transparency to allow for the actor code to handle that. So that may be a useful addition for you to consider. You would need to work out which pixel on the display related to a specific pixel in the sprite and then perform a comparison on that.
There are some limitations to the Pico which are necessary to keep within the amount of memory available and so as to be able to achieve an acceptable performance. One example I’ve already mentioned is the need for the files to be provided in a specific format, another is that rotation is only performed in 90 degree increments.
When coding in pygame zero, when I’ve wanted to show a person walking I’ve created multiple images with the person’s leg in different positions. This is possible using this code, but is likely to be slow. It may be better to have a single image showing the parts of the sprite that don’t change, but then draw the legs in separately. One way would be to use vector images to create parts of the sprite.
You may also need to be careful about memory use. Due to the relative small amount of memory on the Pico and the amount of memory needed to update the entire display then you may need to ensure your code does not create too many different sprites or that they don’t need so much memory. I’ve already discussed a few possible ideas when creating animations on the Pico.
This has provided some of the starting points for anyone looking to implement bitmap based sprites on the Pico or other microcontrollers that support micropython.
There are more methods that could be added to the Actor class (such as collision detection) which would make it more useful.