This post was written with AI assistance. Vania Volpe is a small point-and-click adventure written in C99 and SDL2. Its main gameplay gate in CI is a headless scripted playthrough: a test harness brings the game up with SDL’s offscreen video driver and a dummy audio driver, replays a fixed sequence of taps and waits, and asserts that the expected dialogue lines were spoken, in order. The script…
This post was written with AI assistance. This post describes a change to how scenes in the game declare and use their assets. The work was specified in SCENES.md and tracked in issue #129, and it landed in five separate pull requests. The goal was to reduce the amount of per-scene framework code by moving it into the engine, so that a scene declares data plus a small number of behavior functions…
This post was written with AI assistance. The game now builds as a native Android app. make android produces a debug-signed APK you can install directly on a phone, and a GitHub Actions workflow builds one on every push to main and uploads it as a downloadable artifact. Same C code, no web view involved: the NDK compiles the exact sources the desktop build uses into a shared library, and SDL’s…
This post was written with AI assistance. Every scene in the game used to declare its assets twice. The C file listed them the way the engine loads them — filenames, directories, frame counts, loop styles, hard-coded in static tables. And the art pipeline listed them again in a JSON file, the way an artist needs them — what still has to be drawn, at what size, with how many frames. Two lists…
This post was written with AI assistance. The pool adventure has had a fixed puzzle structure for months, but that structure was never written down anywhere except the code. This post fixes that with the tool adventure game designers actually use for the job: the puzzle dependency chart , introduced by Ron Gilbert — he describes it in a post on Grumpy Gamer as “the single most important tool”…
This post was written with AI assistance. During play-testing, my daughter kept trying to pick the player character up with her finger and carry it around. The game had no such feature, so nothing happened. This post covers the spec that turned that gesture into one — press and drag picks the character up, release drops it, it falls to the ground, and normal play resumes — and the implementation…
This post was written with AI assistance. After a project health review, one refactoring stood out as worth doing right away: every scene in the game handled clicks with the same hand-rolled if -chain, and every scene wrote its interactions down twice . This post is about replacing those chains with a table and a twenty-line dispatcher: instead of implementing its click handling as control flow,…
This post was written with AI assistance. When the talking animation started following mouth-shape cues , the same spec called for a second half: the words themselves, on screen, with the one being spoken highlighted karaoke-style. Not as a debugging aid and not only as subtitles — as a learn-to-read feature . The game’s audience is a child who can’t read yet; a line of text where a bright box…
This post was written with AI assistance. The fox can walk behind things , be far away , and roam a scene wider than the window . This post is the last piece of that plan — parallax planes — and the one where all of it finally shows up in the same picture. A scene’s background stops being one flat image and becomes layers , each scrolling at its own speed, so distance reads the way it does when…
This post was written with AI assistance. Every scene in VaniaVolpe has been exactly one screen: 800×600, the whole world visible at once. That assumption was everywhere and nowhere — no line of code said “scene equals screen,” but positions, hotspots, clicks, the walk grid and the renderer all quietly assumed it. This post is about taking it apart: scenes larger than the window, a camera that…
This post was written with AI assistance. The fox can be drawn behind scene props now, but she’s exactly the same size at the back of the playground as at the front. Depth cues come in pairs — occlusion says behind , size says far — and the game only had one of them. This post is the second slice of the depth plan ( DEPTH_AND_CAMERA.md ): the fox reads as nearer or farther by switching between…
This post was written with AI assistance. Since pathfinding gave the fox routed walks , one flatness kept bothering me: she is always in front of everything. Every scene’s render function ended with the same line — draw the background, draw the objects, draw the fox last — so she floats over the acorn pile even when she’s clearly standing behind it. The world reads as a painted backdrop with a…
This post was written with AI assistance. Vania’s dialogue audio has played since January 2025 : play the line’s WAV, loop a three-frame talking animation for exactly as long as the audio lasts, done. It worked, but it was the same mouth-flapping loop whether the line was “Evviva!” or a two-sentence hint about the squirrel — and it kept flapping straight through the pauses in the audio. This week…
This post was written with AI assistance. The oldest documented limitation in VaniaVolpe was that the fox walks in a straight line. Click across the playground and she marches right through the sandbox, the slide, whatever the scene says is not ground. It had its own design note ( MOVEMENT.md ) written mostly so the problem wouldn’t be forgotten, and the note ended with “keep the engine simple…
This post was written with AI assistance. In the last post I built a headless native test: run the game offscreen, script some clicks, and assert the right dialogue comes out. But I already had a second playthrough test — a Puppeteer script that drives the WebAssembly build in a real browser. Two tests, same adventure, and — this is the bad part — two copies of the entire playthrough: the same…
This post was written with AI assistance. A while back I gave the game a terminal front end : a terminal build that renders the whole thing as coloured ASCII art with libcaca and needs no display server at all. It was a fun way to play over SSH — but the more interesting thing it unlocked was hiding in plain sight. If the game can run with no window, no GPU, and no sound card, then it can run in…
This post was written with AI assistance. Animations in VaniaVolpe are humble: a sprite sheet plus a little .anim file listing each frame’s rectangle, played at 12 FPS. For a long time the code that advanced them lived in the wrong place — inside the render function — and pulling it out turned into a small lesson about where responsibility belongs. I got it wrong once before I got it right, and…
This post was written with AI assistance. With the engine split into adventures and Gina the hen now living alongside the fox, the codebase had grown enough that I wanted a second pair of eyes on it. So I ran the engine through another round of the AI-assisted code review I’ve leaned on before — this time file by file, all the way through main.c , game.c , asset.c , actor.c , image.c , sound.c ,…
This post was written with AI assistance. The game is in good shape now — it runs on the desktop, on iOS, in a terminal, and in the browser. So I started thinking about what comes next: more adventures. But first the code had to stop being one adventure with the engine baked in. One hardcoded adventure The building blocks were already generic — a scene framework, an animation engine, audio — but…
This post was written with AI assistance. The terminal build proved the game logic was portable; the obvious next target was the browser, so anyone can play without installing anything. Building with Emscripten Emscripten compiles the same C sources to WebAssembly. make web runs emcc with the SDL2, SDL2_image (PNG), and SDL2_mixer ports and emits build/web/index.{html,js,wasm,data} . The asset…
On paper, the lowest BPM supported by the PO-33 K.O.! is 60. However, there’s a hidden easter egg, revealed by Jonatan Blomster for the 10th anniversary of the Pocket Operator, that allows you to go as low as 15 BPM. To enable it, hold the BPM button and press any pad five times (this normally sets the volume). Then, while still holding BPM, turn knob B to adjust the tempo anywhere between 15 and…
After getting the game running on macOS and iOS, I wanted to run and test it without a display server at all — straight inside a terminal. A second front end, same game I added a terminal build target that renders the game as coloured ASCII art using libcaca . It lives in three new files — src/terminal.c , src/terminal.h , and src/main_terminal.c — and reuses every other source file untouched. The…
I recently started using the PO-33 K.O.! by Teenage Engineering and decided to collect my notes in one place. This post is a personal reference and cheat sheet rather than a full tutorial. Interface overview Buttons Sound - Select and edit sounds Pattern - Select patterns and chain them BPM - Tempo control 1-16 - Sound/pattern slots and effects Record - Sample audio, clear sounds or patterns FX -…
When I first ported the project to iOS, I encountered a build error due to having image files with the same name, even if stored in different directories. This was followed by a runtime error because I was referencing assets in code by specifying their relative paths. Initially, I worked around the issue by updating all the asset references to include only the file name. However, this change…
After porting the game to iOS, I encountered two issues: The animation implementation relied on the render function being called a fixed number of times per second, matching the screen refresh rate, due to the SDL_RENDERER_PRESENTVSYNC renderer flag. The refresh rates differ between my 2014 MacBook Pro (60Hz) and iPhone (120Hz), causing animations to play twice as fast on an iPhone and iPad. I did…
Initially, I set a fixed window size of 800x600, but this caused issues as the iPhone screen is smaller and the iPad screen is larger. On the iPhone, most of the game screen was not visible, making it impossible to play: On the iPad, the game looked broken, occupying only a small portion of the screen: I resolved this issue by calling SDL_RenderSetLogicalSize in the initialization function: 1…
I added a new outro scene, and now the game is essentially complete. Checkpoint Intro scene: Playground entrance scene: Playground scene: Building for iOS (iPad) After encountering an error including SDL_image, I was finally able to build the project using Xcode 16 on a newer PC. The SDL_image Xcode project does not support Xcode 12. When I attempted to open SDL_image.xcodeproj to change the…
Today, I recorded music, sound effects, and dialogs for the game. Music Between Scenes I used the same music for both the playground entrance and playground scenes. However, the audio recording stops and restarts during the transition between the two scenes. Ideally, the music should continue to play in a loop, so I should hoist the code that plays music from the scene level to the game level.…
I attempted to build the project for iOS because I wanted to play the game on an iPad and to see how portable the project really was. Building for iOS (iPad) I initially attempted to update the Xcode project that I was using to build the macOS executable to target iOS, but the build phase failed because the SDL2 frameworks I was linking against were compiled for a different architecture. I created…
I need to execute a series of actions in sequence. For instance, the fox should walk to the gate, and if the key is not in the inventory, it should say, “The gate is closed. I need a key to open the gate.” If the key is already in the inventory, the gate should open, and the fox should proceed to the next scene. The simplest way to implement these sequential actions is by passing function…
Animation metadata includes an SDL_Rect array used to identify frame coordinates in the sprite texture. I decided to replace the manual definition of the array values with a file in the following format: 1 2 3 4 x1,y1,w1,h1 x2,y2,w2,h2 ... xn,yn,wn,hn Where: x , y , w , and h are the input values of an SDL_Rect , known as the source rectangle in SDL_RenderCopy . Each row represents a single…
I thought it would be useful to get a quick code review to catch any trivial mistakes, as I’m new to C programming. I found this simple web app that reviews your code. It takes a GitHub repo address and an OpenAI API key, then uses ChatGPT for the review. However, I was hesitant to share my OpenAI API key. As a workaround, I could have created a new API key for the review and deleted it afterward,…
I have developed a set of functions to easily draw shapes on the screen for debugging purposes. For instance, I want to visualize the position and size of hotspot rectangles on the screen. These hotspots are areas that react to mouse clicks, such as the gate hotspot in the playground entrance scene, which is an area that accepts mouse clicks to interact with the gate object. The debug overlay can…
After repeating a lot of code, I began to recognize some emerging patterns. Animation Library Previously, an animation was a reference to an SDL_Texture containing an animation sprite image data and an array of SDL_Rects to store each frame’s coordinates in the sprite. I consolidated all the metadata of an animation into a new struct called animation_data and defined a new type AnimationData . I…
I’m not skilled at drawing, but I decided to have fun and create the game image assets myself. This is supposed to be a fox, the main character of the game: I tried a couple of free drawing tools for macOS ( Paintbrush , Pencil2D ), but without a drawing tablet, the result was too rough. Procreate I ended up using Procreate on iPad to draw background images and animations. After choosing the right…
Game Scene A game scene is a collection of functions and state that models a single scene in the game. For instance, in the game there will be four scenes : Intro - Title screen and main menu Playground Entrance - The fox needs to find a way to enter the playground Playground - The fox must fix the slide with help from her friend, the squirrel Outro Each scene must implement these functions: init…
Until now, I was updating code without keeping track of changes, so I added version control using Git. Git and Xcode I added the GitHub .gitignore template for Xcode, which is very straightforward. I decided to add .DS_Store to the ignore list. Assets I could use Git LFS to track only references to asset files (images, sounds), but I decided to put everything under version control to keep things…
During each iteration of the game loop, the function process_input handles events. process_input is a while loop that iterates through all events in the queue: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void process_input ( void ) { SDL_Event event ; while ( SDL_PollEvent ( & event )) { switch ( event . type ) { case SDL_QUIT : game_is_running = false ; break ; case SDL_KEYDOWN : switch ( event .…
SDL_mixer is a simple multi-channel audio mixer that I am using to play sound effects and music in the game. How to Install SDL_mixer I’ve taken an approach similar to the one used for installing SDL_image : Download the library from GitHub. I downloaded and installed SDL_mixer 2.8.0 . Add the SDL2_mixer.framework to the project (under the “General” tab). Change imports to #import…
I initially implemented animations by following Lazy Foo’ Productions’ tutorial on animating sprites , but I encountered an error with the #import <SDL2/SDL_image.h> directive because SDL_image is an additional library that needs to be installed separately. What is SDL_image? SDL_image is a simple library for loading images of various formats as SDL surfaces. How to Install SDL_image I followed…
There are different ways to implement a game loop. For this project, I decided to use the game speed dependent on variable FPS approach, which I found implemented in github.com/gustavopezzi/sdl-gameloop . This implementation works well and doesn’t utilize 100% of the CPU. The basic structure of an SDL2 game loop looks like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int main ( int…
After my initial failed attempt to set up a macOS project using SDL, I decided to try an older version of SDL. Using SDL2 I followed Lazy Foo’ Productions tutorial to set up a macOS Xcode project using SDL2: lazyfoo.net/tutorials/SDL/01_hello_SDL/mac/xcode/ Here are the steps I followed: Create a new macOS app with interface: XIB and language: Objective-C. Delete the template project files (…
Here are the notes from my initial, unsuccessful attempt to set up a macOS project using SDL. Installation I initially followed the installation directions from the SDL3 wiki. However, the wiki page no longer exists, as it was deleted: github.com/libsdl-org/sdlwiki/commit/e7f66042 . I installed SDL 3.1.8 by copying the SDL3.xcframework and shared directory into /Library/Frameworks . Build I…
This is the first in a series of posts documenting the process of creating a simple point-and-click adventure game for macOS and iOS, written in C using the SDL framework. Background I attempted this project about five years ago without success. At that time, I used Swift and SpriteKit , but lacked a clear game design concept. This time, I began with a game design in mind, significantly reduced…
I decided to use the OpenAI API to proofread and improve all the content on this blog. First, I created a script to reformat all the posts to remove redundant new lines, such as single new lines in a paragraph that don’t affect text layout. This was done to make it easier to review changes generated by the AI-powered content editor. Then, I developed another script to call the OpenAI API with a…
Introduction In the conclusion of the last post in this series, I intended to discuss how to render sprites, but I need to delay that topic because I still lack a clear game design idea. I like to see this as applying the “Give the player options” rule from Gilbert’s Rules of Thumb for adventure games that don’t suck. Although I’m stuck on the “game design” puzzle, that shouldn’t prevent me from…
Introduction Last week, I implemented the callbacks for handling touch events on iOS and tvOS. This week, I’m implementing a callback for handling mouse events on macOS. Moving the Cursor (on macOS) The only method I need to override for now is mouseMoved , which informs the receiver that the mouse has moved. Each function call updates the position of the cursor node with the relative position of…
Introduction All games support some form of user interaction, right? In the case of classic point-and-click adventure games, the player should be able to move the cursor and click to interact with the game environment, talk to other characters, and select actions from the HUD . In the next two posts, I’m going to add support for mouse and touch events, depending on the target device, for moving…
In the realm of adventure games, a significant innovation introduced by Maniac Mansion was the replacement of the text parser with a cursor and clickable elements such as verbs, inventory items, and objects within the scene. This innovation revolutionized user interaction and the usability of adventure games. While a cursor may not be entirely practical for touch screen devices (iOS), it remains…
SpriteKit SpriteKit is a general-purpose framework for drawing shapes, particles, text, images, and video in two dimensions. Developed by Apple, SpriteKit is supported on iOS, macOS, tvOS, and watchOS, and it integrates well with frameworks like GameplayKit and SceneKit. I’ve chosen it to create a prototype adventure game reminiscent of those from the late ’80s and early ’90s. Typically, people…