Tiny game #4: Groundhog Downhill

This time I wanted to do something with 3D terrain and physics. Did you know groundhogs love having downhill contests in winter? Try to finish the course in under one minute! (My personal best is around 50 seconds.)

click on the image to open the game in a new window – webgl-enabled browser required!

I didn’t spend much time on optimization – the terrain is essentially a giant mesh, and there are a lot of unneccessary realtime light and shadow computations. It should run OK on a current PC, but my old laptop was aching a lot in fullscreen mode.

At first, I though about using Unity’s terrain engine and editor, but that turned out to be unsuitable for a game like this. It’s meant for huge natural terrains, as in an open-world game, but it’s based on height fields – and sculpting a race track with precision is pretty much impossible. So in the end, I modelled the track in Blender using curves, and added a simple polygonal terrain and a few extras here and there.

The physics engine is based on rigid bodies, so our little friend behaves much more like a tennis ball than a floppy soft creature. But it’s fun that way 🙂 I did add an extra force to make him stick to the ground just a little more.

Having this really rough implementation working in WebGL with decent performance out of the box is pretty cool! There’s a bug regarding mesh collisions in Unity with WebGL, though, which has been around for FOUR years and will likely never get fixed. If you check the bug tracker issue and related forum discussions, it is rather disheartening to see how a problem such as this is treated. It’s unfortunately not the first time I see quite relevant issues remaining on the table for many years without getting fixed in Unity. With any luck, someone in the community has come up with a clever workaround (which helped me out this time, too). But in the long term, these things make you wonder about their priorities and whether it’s time to explore other options.

Tiny game #3: Escape

It’s NaNoWriMo, and even though I’m not writing a novel, it’s the perfect month to work on a game with narrative!

There are some great GDC videos on narrative in video games by Jon Ingold from inkle. Over the years, they have developed a scripting language for games based on branching narrative called “ink”, which has recently been open-sourced. The system is completely text-based. They do provide a very lean C# API, too, so it’s almost a no-brainer to use ink for authoring the story backbone of a graphical adventure-type game!

In ink, a “game” is essentially constructed around pieces of content (text) presented to the player, and sets of choices to direct flow to other pieces of content. Programming logic is provided to allow state tracking and flow control. There are no concepts for “rooms”, “objects”, “containers”, “actors”, though. What about action adventure / rogue-like gameplay, where you wander around a dungeon, encounter NPCs, collect and use items?

So that was my challenge, and here’s the result:

click on the image to open the game in a new window – webgl-enabled browser required!

This is very much a proof of concept rather than a full game, but I am quite happy how it all worked out! Gameplay is completely driven by the following .ink text file:

// items that can be picked up

LIST items = nothing, soap
VAR inventory = ()

// states

LIST playerState = walking, (inBed), soaked, soaped
LIST bedState = (down), up
LIST manholeState = (closed), open, examined
LIST showerState = (off), activated

// locations
LIST locations = (none), BED, CELL, SHOWER, SHAFT

VAR playerLocation = BED
VAR prevPlayerLocation = none

VAR bedItems = (BED)
VAR cellItems = (CELL)
VAR showerItems = (SHOWER, soap)
VAR shaftItems = (SHAFT)

-> hub

// functions for moving around

// did we enter location x for the first time?
=== function first(x) ===
~ return x == 1

// did we just enter the current location from somewhere else?
=== function enter ===
~ return prevPlayerLocation != playerLocation

=== function moveTo(x) ===
~ playerLocation = x

=== function removeItem(ref itemList, item) ===
~ itemList -= item

// pick up an item from current player location
=== function getItem(item) ===
{ playerLocation:
  - BED: ~removeItem(bedItems, item)
  - CELL: ~removeItem(cellItems, item)
  - SHOWER: ~removeItem(showerItems, item)
  - SHAFT: ~removeItem(shaftItems, item)
}
~ inventory += item

// main story hub, switching flow to the available locations

=== hub ===
{ playerLocation:
  - BED: <- bed(->hub)
  - CELL: <- cell(->hub)
  - SHOWER: <- shower(->hub)
  - SHAFT: <- shaft(->hub)
}
<- inventory_actions(->hub)
~ prevPlayerLocation = playerLocation

-> DONE

=== bed(->back) ===
#cut
E S C A P E

The same dream, night after night.
A prison cell.
No way out.
One day after the other, your life passes by.
"Hope" is just a faint shadow of a word without meaning.
%%
You awake from restless slumber...
%%
%%
(any key to start)
%PAUSE%
#player
Zzzzz.... hrm?
Where am I?
Oh.
\*Sigh*
How do I get out of here?
%WAIT%

* [!anyaction!Get up] Ouch, my back! This "bed" is killing me. #player
  ~ moveTo(CELL)
- -> back
  
=== cell(->back) ===

+ [!trigger!shower]
    ~moveTo(SHOWER)
* [!pull!bed]
  ~bedState = up
+ {bedState ? up && manholeState ? (closed,examined)} [!pull!grating]
  Uaarrggh! Heavy! #player
  ~manholeState = open
  -> examine_manhole
* (examine_manhole) {bedState ? up} [!examine!grating]
  {examine_manhole == 1 : {manholeState ? closed : The gutter, covered with a grating.|This shaft looks REALLY tight!}} #player
  ~manholeState += examined
+ {manholeState ? open} [!use!manhole]
  {playerState ? soaped:{Are you serious?? It stinks!|It still smells!|Ewww!}|It's too tight, I'll get stuck!}#player
  + + {playerState ? soaped} [!use!manhole]
      Aah, all right.
      Down the rabbithole! #player
      %WAIT%
  ~moveTo(SHAFT)
  + + [!noaction!manhole] -> back
  - -
-
-> back

=== shaft(->back) ===
#cut
Carefully, you lower your soaked, soaped, slippery body down the hole, head first.
One last push, and off you go!
Holding your breath, you're hoping for the best.
%%
Freedom, at last!
%%
%%
(any key to restart - but why would you?)
%END%
->END

=== shower(->back) ===
{enter():{Take a shower, maybe?|}} #player

* [!examine!button] A big red button. #player
* [!push!button]
  ~ showerState = activated
  Yikes! #player
  Why didn't I undress first?
  ~ playerState = soaked
  * [!examine!soap] Disgusting.#player
    How can SOAP be so dirty?! 
  * [!get!soap] This slimy thing gives me the creeps. #player
  ~ getItem(soap)
+ [!trigger!leaveshower]
  ~moveTo(CELL)
  
-
-> back
  
 === inventory_actions(->back)
 * {inventory ? soap && !(playerState ? soaked)} [!use!soap] It's too dry! #player
 + {inventory ? soap && playerState ? soaked} [!use!soap] Yay, foam bubbles! #player
 Hmmm - lavender?
    ~inventory -= soap
    ~playerState = soaped
 - ->back

(Disclaimer: There may well be better ways to do this – it’s my first time using ink!)

On the Unity side, incoming text is processed by a StoryAdapter wrapper around the ink API. Player choices in the text are mapped to actions in the game environment by custom markup, which I made up in a very ad hoc fashion. E.g.:

  • the choice [!push!button] will be presented as a menu on the “button” object in the scene, when the player approaches it.
  • Location changes are choices, too: a choice with the !trigger! markup will be executed when the player crosses a special invisible object in the scene.
  • The #player tag sends text to a balloon displayed as the player’s thoughts.
  • #cut sends text to a cut scene screen (at the beginning and end of the game)
  • … and so on.

What you end up with, is a sort of MVC architecture of a game: the ink description is the model, being visualized by the game graphics. The player actions in the scene are sent back to the model, which then updates itself to advance the story.

Now, this is really exciting! It means:

  • the game can be played entirely in its text form! You can paste the above text into the Inky editor and play through. It’s not going to be very pretty, due to all the markup, but…
  • … authoring story and game logic is a breeze after the foundation is laid, or at least a lot easier;
  • … the game can be tested without any UI, for instance using automated randomized picking of choices;
  • … games could also be completely or in part procedurally generated.

The one thing I didn’t get around to is dialogue, which is a real shame. But something has to be left for another game, right?

Tiny game #2: Neat-O-Droid

This game took about two weeks from start to finish. “Two games a month” instead of “a game a week” still doesn’t sound too bad, right?

The gameplay is somewhat inspired by the good old Paradroid C64 game, with some Pacman mechanics sprinkled in (gobble up the pellets, avoid and kill the monsters, clear the maze, you name it).

click on the image to open the game in a new window – webgl-enabled browser required!

The most interesting programming challenge was the map generator- I spent the bigger part of the first week on it. The levels in the game are fairly small, but the algorithm can generate arbitrarily big maps, which I hope will come in handy in future games. The general structure is a nested set of rectangular rooms and hallways, such as this:

a bigger maze

The algorithm is based on a simple binary space partitioning scheme, essentially going through these steps:

  • divide an initial rectangle with a few wide separators (the hallways)
  • recursively divide the parts with thinner separators (rooms separated by walls)
  • identify all separator spans shared by two rooms (potential doors)
  • create a random path covering all rooms (this will be the set of required doors)
  • randomly delete a subset of the remaining doors

This way, all rooms are guaranteed to be reachable with an adjustable overall amount of doors.

One major lesson: With the number of moving parts, the parameters that have to be balanced explodes. Even in a simple games as this, tweaking things like

  • size and complexity of the map
  • amount of initially distributed junk
  • player speed and fire rate
  • enemy speed and “litter rate”
  • rate of enemies respawning
  • speed of “junk unloading” at the chute
  • number of junk items the player can carry

do not only make the difference between easy and hard, but also change how the game needs to be played to win.

Tiny game #1: Elsie in the woods

Thinking more about how to unify my interests in programming and art, I’ve been turning to games again – it’s been a while! After spending nearly two months on a prototype platformer game, I had to realize

  • it would take a lot longer to properly finish the game with nice artwork and really smooth gameplay
  • even though developing the game was a lot of fun, I really don’t have that much experience with game design, so ultimately it wouldn’t even have been all that interesting to play.

Inspired by a video on “a game a week”, I decided to abandon the big project and go for way smaller, focused mini games that could actually be implemented in a short time. This is the first of them – situated in what used to be just the intro of my original larger game.

Main main goal here was to have a “complete” game – with a beginning, middle, and end, revolving around a simple core game mechanic.

click on the image to open the game in a new window – webgl-enabled browser required!

Despite its simplicity, there were quite a few interesting technical bits here to experiment with:

  • importing the rigged player model from Blender into Unity
  • an outline shader for the player
  • some postprocess bloom in the background
  • Cinemachine virtual camera controls
  • a particle system for the exploding mushrooms
  • 2d physics