Dinosaur Game for ClockworkPi PicoCalc

Introduction

The Dinosaur Jump and Run Game is a classic endless runner game developed for the ClockworkPi PicoCalc. The player controls a dinosaur that must jump over obstacles while the speed continuously increases. The game is fully programmed in MMBasic and uses the PicoCalc's graphics capabilities for smooth animation.

Dinosaur Jump and Run Game

The gameplay is simple: Press the Enter key to jump and avoid obstacles. The score increases continuously, and the speed increases every 100 points. The game saves the highscore on the SD card, so your best performance is preserved even after a restart.

About the PicoCalc

The ClockworkPi PicoCalc is a compact, portable computer based on the Raspberry Pi Pico. It features a 4-inch IPS display with 320x240 pixels, a full QWERTY keyboard with backlighting, and an SD card slot for data storage. The device runs on PicoMite, a special version of MMBasic optimized for the Raspberry Pi Pico.

MMBasic (or PicoMite) is a complete BASIC interpreter environment specifically developed for 32-bit microcontrollers. It offers extensive graphics functions, filesystem access, timers, and many other features that enable programming games and applications.

MMBasic Programming

MMBasic is a modern BASIC programming language developed for microcontrollers. It combines the simplicity of BASIC with powerful features such as floating-point numbers, extensive string processing, multidimensional arrays, and structured programming with subroutines and functions.

For the PicoCalc, PicoMite is used, a special port of MMBasic for the Raspberry Pi Pico. PicoMite supports all important MMBasic features and additionally provides hardware-specific functions for the PicoCalc display and peripherals.

The most important graphics commands used in the Dinosaur game are:

Coding with Cursor

The entire Dinosaur game was developed using Cursor, an AI-powered code editor that significantly accelerates the development process. Cursor combines the familiar interface of VS Code with advanced AI capabilities that can help write, refactor, and understand code.

When developing the Dinosaur game, Cursor's AI features were instrumental in several ways. To begin developing with Cursor, I started with making a plan like this:

# Dinosaur jump n run on the clockworkpi picocalc

create a jump and run game inspired by the dinosaur game in the chrome browser, when no internet is present.

The game is to be implemented in PicoMite MMbasic 6 (see Documentation here https://geoffg.net/Downloads/picomite/PicoMite_User_Manual.pdf)

- [ ] the dinosaur should be a sprite and can jump with enter button.
- [ ] The score is calculated by the distance the dino is running without hitting an obstacle.
- [ ] There a different kinds of obstables:
	- [ ] cactus small
	- [ ] cactus big
	- [ ] flying pterodactyl
- [ ] the speed of the dino is increasing during the game every 100 score points
- [ ] in the the background we can see mountains, that are moving much slower as the foreground
- [ ] after hitting an obstacle the current score and the highscore are shown on the screens

Cursor's AI will help generate the initial code structure based on this plan.

One of Cursor's strengths is its ability to understand context. As you build the game, you can ask Cursor to add features incrementally:

Cursor understands the existing codebase and can add new features that integrate seamlessly with what you've already written.

As the program grows, Cursor can help refactor and improve the code. You can select a section of code and ask Cursor to:

When working with MMBasic, Cursor can help with syntax questions. If you're unsure about a command, you can ask Cursor directly, and it will provide examples and explanations based on MMBasic documentation.

For instance, when implementing the sprite drawing functions, Cursor helped ensure the correct Box command syntax:

' Box syntax: X, Y, W, H, LW, C, FILL
Box x+8, y+12, 8, 6, 0, RGB(100, 150, 50), RGB(100, 150, 50)

When using Cursor to develop MMBasic programs, keep these best practices in mind:

Once the code is complete in Cursor, save the file as dinosaur.bas. The file can then be transferred to the PicoCalc via SD card. Copy the file to the root directory of the SD card, insert it into the PicoCalc, and load it with:

LOAD "b:dinosaur.bas"

Using Cursor for development provides a much more efficient workflow than coding directly on the PicoCalc, especially for complex programs like the Dinosaur game with its multiple sprites, physics simulation, and file I/O operations. Of course cursor does not do everything right from the start, but you can feed the error back to cursor and it will try to fix it. Of course its helpful to also have some knowledge of the language and the syntax, to decide where to head. Sometimes the AI is lost in a dead end and you need to guide it back on track.

The Dinosaur Game

The Dinosaur game is divided into several logical sections. At the beginning of the program, all global variables are declared. These must be declared in MMBasic before they are used:

Dim HighScore As Integer
Dim DinoX As Integer
Dim DinoY As Integer
Dim DinoYVelocity
Dim IsJumping As Integer
Dim GroundY As Integer
Dim Score As Integer
Dim GameSpeed
Dim BaseSpeed
Dim ObstacleList(20, 3) As Integer  ' x, y, type, active
Dim ObstacleCount As Integer
Dim GameOver As Integer
Dim FrameCount As Integer
Dim DinoFrame As Integer
Dim GameOverDrawn As Integer

The game saves the highscore on the SD card in drive b:. The highscore functions automatically load and save the value:

Sub LoadHighScore
  HighScore = 0
  Open "b:highscore.txt" For Input As #1
  Input #1, HighScore
  Close #1
  If HighScore < 0 Or HighScore > 1000000 Then
    HighScore = 0
  End If
End Sub

Sub SaveHighScore
  Open "b:highscore.txt" For Output As #1
  Print #1, HighScore
  Flush #1
  Close #1
End Sub

The sprites are drawn using Box commands. Each sprite is a subroutine that receives the position as a parameter. The dinosaur has two animations: one for running and one for jumping:

Sub DrawDinoRun(x, y)
  ' Körper (grün-braun) - BOX Syntax: X, Y, W, H, LW, C, FILL
  Box x+8, y+12, 8, 6, 0, RGB(100, 150, 50), RGB(100, 150, 50)
  ' Beine
  Box x+10, y+18, 2, 4, 0, RGB(80, 120, 40), RGB(80, 120, 40)
  Box x+14, y+18, 2, 4, 0, RGB(80, 120, 40), RGB(80, 120, 40)
  ' Kopf (nach rechts)
  Box x+12, y+4, 8, 6, 0, RGB(100, 150, 50), RGB(100, 150, 50)
  ' Auge (rechts im Kopf)
  Box x+16, y+6, 1, 1, 0, RGB(255, 255, 255), RGB(255, 255, 255)
  Box x+17, y+6, 1, 1, 0, RGB(0, 0, 0), RGB(0, 0, 0)
  ' Schwanz (links)
  Box x+0, y+10, 4, 2, 0, RGB(100, 150, 50), RGB(100, 150, 50)
  ' Bauch (heller)
  Box x+10, y+14, 4, 2, 0, RGB(150, 200, 100), RGB(150, 200, 100)
End Sub

There are different obstacle types: small cacti, large cacti, two small cacti side by side, and pterodactyls that fly in the air.

The main program consists of an infinite loop that continuously updates and redraws the game:

Main:
  Print "Programm gestartet..."
  InitGame
  Print "Spiel initialisiert. Score="; Score; " HighScore="; HighScore
  
  Cls
  
  Do
    UpdateGame
    DrawGame
    ' Frame-Timing (ca. 60 FPS = 16ms pro Frame)
    Pause 16
  Loop Until 0

The UpdateGame subroutine handles game physics, input, obstacle spawning, and collision detection. The DrawGame subroutine draws all sprites and the score on the screen.

The jump physics uses a simple gravity simulation:

' Dinosaurier-Physik (Sprung/Schwerkraft)
If IsJumping Then
  DinoY = DinoY + DinoYVelocity
  DinoYVelocity = DinoYVelocity + GRAVITY
  
  ' Auf dem Boden landen
  If DinoY >= GROUND_LEVEL - DINO_HEIGHT Then
    DinoY = GROUND_LEVEL - DINO_HEIGHT
    DinoYVelocity = 0
    IsJumping = 0
  End If
End If

The constants GRAVITY and JUMP_STRENGTH determine the jump behavior. These can be adjusted to make the game harder or easier.

The collision detection uses a simple rectangle-to-rectangle check:

' Kollisionsprüfung
If Not (dinoRight < obsLeft Or dinoLeft > obsRight Or dinoBottom < obsTop Or dinoTop > obsBottom) Then
  ' Kollision!
  GameOver = 1
  GameOverDrawn = 0
  UpdateHighScore Score
  Exit For
End If

For each obstacle type, the corresponding dimensions are used to ensure precise collision detection.

Loading and Running the Program

Dinosaur Jump and Run Game

To run the Dinosaur game on the PicoCalc, follow these steps:

  1. Copy the file dinosaur.bas to an SD card. The file should be saved in the root directory of the SD card.
  2. Insert the SD card into the PicoCalc.
  3. Turn on the PicoCalc and wait until MMBasic/PicoMite has started.
  4. Load the program with the command:
    LOAD "b:dinosaur.bas"
    The b: denotes the SD card drive.
  5. Start the program with:
    RUN

The game starts automatically. Press the Enter key to jump. When the game is over, press Enter again to restart. The highscore is automatically saved on the SD card.

If you want to make changes to the code, you can edit the program with EDIT or F4, change individual lines directly in the MMBasic interpreter. Don't forget to save the program after changes with SAVE "b:dinosaur.bas" or F2, which saves and starts the program directly.

From the idea to this running code it took me about 2 hours, including the time to write the plan and the time to write the code with the help of the AI. I find this quite impressive, since I only have a basic knowledge of the language and the syntax 😉

Dinosaur Jump and Run Game

Disclaimer: I also used the cursor AI to write the text for this tutorial, so if you find any errors, please let me know.