Introduction to retrogame programming with Pyxel

Pyxel

Pyxel is a tribute to game consoles of the golden age, like the SNES or the Game Boy Color : large pixels, few colors and minimalistic sounds (on 4 channels).

With such a minimalistic way, you can build games quickly, and they will run smoothly on a dog-slow computer (thanks, Rust), on every OS, even on a web page (with WASM).

Setup

Pyxel is tested on Linux, Mac, and Windows.

You have to use the terminal and type commands the geeky way.

Text editor

You have to pick a text editor, like VSCode, Sublime Text, or whatever works. You can download them from their webpage, or through an app store.

On Mac, you will use Homebrew

brew install vscode

On Ubuntu, you will use Snap.

Python

Check you have a decent Python installed (>= 3.10).

python3 -V

If you don’t have Python, or an old version, you have to install a fresh version.

On Mac, you will type

brew install python

On Linux, you will type

sudo apt install python3-dev python3-venv python3-pip

On Windows, you will click-click on python.org.

Installation

Pyxel is not an application but a framework. A media editor is provided.

So, to use it, you must have Python installed, create a project with its own virtual environment (a venv), install the pyxel library with the pip tool.

Don’t be messy; put all your code in a dedicated folder.

mkdir pyxel-demo
cd pyxel-demo

Create a virtual environment in order to install libraries locally, only for this project.

python3 -m venv .pyxel-env
source .pyxel-env/bin/activate

On Windows, the activation command will be different (but similar).

Install the library. First, update pip, or it will yell, and then install pyxel.

pip install -U pip
pip install pyxel

Pyxel examples

pyxel copy_examples

Pyxel will download its own examples in the pyxel_examples folder.

Use pyxel run to start demos.

pyxel run pyxel_examples/02_jump_game.py

The Jump Game uses the right/left arrows to accelerate or decelerate the hero.

Pyxel Media Editor

Pyxel provides a media editor for sprites, tilemap, sound and music.

Pyxel handles traditional data formats.

Pictures are PNG (with transparency). Be careful, video games used to put the zero in the top left corner. You can upload a PNG/GIF/JPG file with a drag and drop in the editor window.

Fonts are pixel only and use the BDF format (you can edit them with FontForge).

Maps use the TMX format, editable with Tiled.

Pxyel editor creates a pyxres archive (a ZIP file with a fat TOML file inside).

Pyxel sounds use elderly synthetizer instructions using a specific format.

pyxel edit

Demo

Demo party !

OK, it works; you can launch and tweak the examples. Now you should read the doc.

The code uses type (yeah) without comments (woooh), so the text editor can provide a few hints.

Media editor

pyxel edit initiation.pyxres &

Draw two spaceships, 16px tall and wide.

It’s your own spaceship; draw wahtever you like, with the correct sizes.

The first spaceship looks to the right side and the second spaceship looks left.

The first color of the palette is transparent; it’s the 0 value in the code.

pyxel

If you are struggling, you can cheat and download the file initiation.pyxres.

Code

Create and edit a file initiation.py, then write the example code and tweak it as you will.

# only for UNIX
touch initiation.py

A static spaceship

The application is put in a class.

In the __init__ method, you pick a screen size and its name. The assets are loaded.

The update method handles inputs (the keyboard, here).

The draw method draws the full screen.

import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120, title="UFO static")  # width, height, title
        pyxel.load("initiation.pyxres")
        self.player_x = 72  # 🛸 X position
        self.player_y = 72  # 🛸 Y position
        self.ship = (0, 0, 16, 16, 0)  # x, y, width, height, transparent color

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)  # clear screen with color 0

        # Draw 🛸
        pyxel.blt(self.player_x, self.player_y, 0, *self.ship)


App()

Ask Pyxel to watch the source of your game.

pyxel watch . initiation.py

The spaceship is in the middle of the void of space. You can only quit the application with the Q key.

The spaceship is shaken

A little bit of match, just a cosine is used to bounce the spaceship smoothly.

import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120, title="UFO shaken")  # width, height, title
        pyxel.load("initiation.pyxres")
        self.player_x = 72  # 🛸 X position
        self.player_y = 72  # 🛸 Y position
        self.ship = (0, 0, 16, 16, 0)  # x, y, width, height, transparent color

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

        # 🛸 bumps slowly 1/16 per tick starting with y = 72
        self.player_y = pyxel.cos(pyxel.frame_count * 360 / 16) + 72

    def draw(self):
        pyxel.cls(0)  # clear screen with color 0

        # Draw 🛸
        pyxel.blt(self.player_x, self.player_y, 0, *self.ship)


App()

The spaceship can move forward or downward

The spaceship can looks frony or back, using the two sprites.

Using the arrows, you can move forward or backward, with a step of two pixels. You can’t go outside the screen.

import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120, title="UFO exploration")  # width, height, title
        pyxel.load("initiation.pyxres")
        self.player_x = 72  # 🛸 X position
        self.player_y = 72  # 🛸 Y position
        self.spaceship_r = (0, 0, 16, 16, 0)  # x, y, width, height, transparent color
        self.spaceship_l = (16, 0, 16, 16, 0)
        self.ship = self.spaceship_r  # 🛸 looks ->

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

        if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_LEFT):
            self.player_x = max(self.player_x - 2, 0)  # go ->
            self.ship = self.spaceship_l  # 🛸 looks <-
        if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_RIGHT):
            self.player_x = min(self.player_x + 2, pyxel.width - 16)  # go ->
            self.ship = self.spaceship_r  # 🛸 looks ->

        # 🛸 bumps slowly 1/16 per tick starting with y = 72
        self.player_y = pyxel.cos(pyxel.frame_count * 360 / 16) + 72

    def draw(self):
        pyxel.cls(0)  # clear screen with color 0

        # Draw 🛸
        pyxel.blt(self.player_x, self.player_y, 0, *self.ship)


App()

Here it is; you have a wiggling spaceship, and you can drive it with your keyboard.

To infinity and beyond

What’s next? Some noise, a background with stars, some laser beams, some villains?

blogroll

social