Ludum Dare 29: Lost Cat

Download Lost Cat v0.1 (Ludum Dare version).

Requirements: Python 2.7, a terminal emulator.

Summary

Lost Cat is a game I made with my kids for Ludum Dare 29, a game jam that took place during April 25-28, 2014.

We developed this compliant with the "jam" rules, because we were (technically) a team of more than one person, we needed the extra 24 hours, and we had to blur the rule about pre-existing libraries. I especially don't mind bending the library rule in this case, since most of the actual game development time (foolishly) went toward building an engine and editing tools from scratch.

This version of the game was developed using Python, and runs in a terminal window. If you don't know what that is, you might just want to wait until we make a web version. :)

Version History

Version 0.1: official Ludum Dare entry

The first submitted version. This version is very nearly playable. :)

Known issues:

  • The level design is boring and easy.
  • Art for the barn (the upgrade store) is missing.
  • We planned for sound but didn't implement it.
  • Nothing happens when you actually reach the cat. Consider yourself victorious when you get there. :)

Development Journal

Friday 4:00pm

I'm doing the Ludum Dare game jam this weekend (LD29), and will be posting development notes to this page. See also Twitter.

From my declaration to enter:

First time LD’er, generally a hobby gamedev newb, using LD to kick myself in the butt and actually make something. I’ve always wanted to make games with my kids, but our first few attempts were hampered by elevated expectations—mine more than theirs. We’ve had the most success making games in highly constrained environments, such as for the Meggy Jr handheld, which I thoroughly recommend.

So for LD29, I’m going with Python and curses. As in, terminal ASCII art. This was actually more of a practical choice than anything else: the most time I’ve had for this recently was on an airplane, and I wanted a language and library I (personally) could use without Internet resources. I’m pleased to say that on modern hardware and a local terminal window (not 300 baud), curses is blazingly fast, and I managed to cobble together a dirt-simple 2D engine with plenty of head room (dozens of objects, hundreds of FPS, with a naive drawing technique). Also, curses makes for great nostalgia-based dopamine boosts, like playing Ladder on my dad’s Kaypro.

There’ll be an optional audio layer based on PyGame, so you can play quiet with just Python, or install PyGame for sounds. One of the kids was most looking forward to doing sound effects, so expect some “pew pew swish” vocals. The other kid wanted to draw art, and now I’m wondering if I could throw together a color ASCII art editor in an evening.

I realize the tech choice limits the compo appeal (I’d do a web game if I were more familiar with a framework), but we’re going with jam rules and just planning on having a good time. If the game engine amounts to anything, I’ll post it. But if you’ve never built an engine before, I recommend it. I’m happy to finally have the opportunity to implement techniques I’ve only read about, in a small and forgiving medium.

I'm pretty sure the decision to use a relatively obscure technology with a game engine built from scratch is going to bite me, but I had early success with a rudimentary graphics and game engine, including timed ticks, individually-controlled figures with z-ordering and transparency, sound and music (via PyGame, which is obnoxious but since it's only for sound I can make sound and PyGame optional), and keyboard input. And it's too late to learn something new now. The biggest risk is the kids will find ASCII art unsatisfying, but we can always do a JavaScript remake later.

Here's a simple colored snake demo using the engine:

I had a nice Game of Life implementation also and I'd post a video but there's a small bug now and I don't have time to fix it because OMG we've started!!

Friday 7:27pm

The theme for the jam was posted at 6pm, and I wrote it up on the whiteboard in the office:

I left the kids alone in a room for a bit, and they came out with something involving digging to the center of the earth, and kittens. They're currently doing concept art while I desperately code up an image editor, so we can divide up the remaining work. Shooting for a digging mechanic and a simple cut-scene engine by the end of the evening (4 hours).

Friday 8:37pm

Proving that my attempt to make an ASCII art editor on short notice is both pointless and futile, here's my daughter rocking Emacs in artist-mode:

The boy saw my M-Audio Ozone ready and waiting, and started jamming game music in GarageBand:

They've already got concept art and even the controls written out. I'm still struggling with my game loop, and spending time taking photos of their progress.

Friday 11:43pm

Some gratuitous debugging, the evening ritual, and an hour of a movie later, and I'm pretty sure I'm better off sleeping than trying to stay up. We're nowhere close to gameplay, and I think the kids are already souring on the premise. But I think I have a pretty good idea of what we're doing, and once something is playable they'll probably perk up again.

The boy dug out a book for inspiration: Leo Geo and his Miraculous Journey Through the Center of the Earth, by Jon Chad. Each page is a 25" x 4" spread, oriented longways for tall pictures depicting Leonid's descent.

"I'm sorry sis, this is something I have to do — for science!"

Saturday 12:15am

Unfortunately I'm starting to notice some issues with flickering with large overlapping objects in my junky little display engine. I haven't given up entirely yet, but this late in the evening this is not a fun thing. Shown here are two overlapping rectangles with minimal update logic, and I'm only getting 200 fps and flickering.

Maybe sleep will help.

Saturday 11:54am

I purposefully did not ask for "leave me alone" terms from my family during LD, so I've spent the morning grocery shopping, feeding and eating, and other family duties. Meanwhile I'm still chasing bugs in my display logic. If I can get rudimentary figure editing working by 1pm (one hour), I'll be very happy, and can start working on actual game logic.

Frame rates are getting worse and worse as I add logic, but that's to be expected. I assume there's some kind of logarithmic drop-off of framerate, so it's disappointing to see it fall by huge chunks, though I know it'll settle down soon. I'm also calculating framerate by overall average and not a running average (boo), but that won't bite me until later so I'm not going to worry about it.

Re: flickering, I'm wondering if I'm refreshing the curses display incorrectly. It's not related to figure count or color variety. I accidentally scrolled up in my iTerm window and noticed previous frames in the scrollback, so maybe I'm refreshing the display incorrectly. This is despite the fact that I'm drawing to fixed coordinates into the curses buffer via addch(), then calling refresh() at the end of drawing. scrollok(False) doesn't make a difference. Other curses-based apps (like less) don't fill the scrollback like this. Both iTerm and Terminal exhibit the flicker, though only iTerm is putting frames into the scrollback buffer...

Saturday 3:08pm

The image editor is working! I already have a list of features I want to add, but it makes color ASCII art and saves it in a format my graphics engine can load into a game. Sweet!

Saturday 6:37pm

24.75 hours elapsed, 47.25 hours remain. Much time spent on non-LD things today. No regrets or self-punishing thoughts this go around, but I still haven't made satisfying progress. Looking for an easy early target...

A possible lead on why iTerm2 fills its scrollback buffer when using curses "alternate screen mode", though not necessarily a fix for flicker. But I'm not going to fix the flicker issue until I see it being a problem in an actual game.

Your Work Ethic Depends on Dopamine Levels Across the Brain. WikiHow: How to Increase Dopamine. Fruits, vegetables, exercise, sleep.

Sunday 1:32am

Sleep.

Still no gameplay. I'm learning a bunch from the image editor tool I've built, especially since I've built the tool on top of the game engine. I have a more solid plan for implementing the gameplay and cutscene systems now than I did earlier today, but I haven't actually implemented them, so this is going to be tricky. I've also managed to spend some time improving the editor in ways that may have been unnecessary, but I want it to work, especially if the kids are going to help out by using it.

Probably the biggest recent change to the editor is support for multiple frames. These frames will be used for either animation or for changing views of the same object, such as the player facing multiple directions. That way, a game object controlling a figure only needs to change which frame is displayed when the state of the object changes, instead of swapping out the object entirely.

My crazy curses display engine drives the fan on my laptop something fierce. Maybe re-displaying every character and switching the curses buffer as fast as possible isn't the best idea. I use timed ticks for game system updates, maybe I should also use timed ticks for refresh calls as well, fixed at 60 fps...

Anyway, sleep. We'll see what happens.

Sunday 11:19am

Went out for brunch, and while there wrote out a list of assets that need producing. I have one bug to fix on the image editor then I can hand that off to the kids. We'll do some sound recording this afternoon.

Meanwhile, I've got the gameplay and visual style worked out in my head. There are several things we want to accomplish, but they're additive, so we can start with the first one, and see how far we get. Screenshot soon.

I also have a couple of things I want to try to improve the display engine. Not worth a ton of time, but I think I'm doing some stupid things in a couple of places that could be made smarter. It's annoying that my laptop fan goes so hard when a simple game is running.

Brunch is... making me... sleepy...

Sunday 1:33pm

Ugh, still hacking on the image editor. Gotta stop this. But look, I can make an animated boat!

Previously, the screen drawing routine was iterating over every location in the frame and calculating the character to draw from the z-index-ordered figure list. This avoids excess calls to addch() (at most one per location) at the expense of a significant amount of figure churning. I switched to a simpler technique that simply iterates over the figure list and paints each figure into the frame, allowing overlapping figures to re-draw locations.

Better? I'm actually not sure yet. The other method had no problems hitting 60 fps and beyond. As above, I was seeing some nasty flickering issues in both iTerm and Terminal. This new drawing method doesn't improve that case. That may be understandable: curses doesn't draw directly on to the screen (in this mode), but instead draws into a buffer, which I copy onto the screen with refresh(). The flickering could only be explained by calling refresh() too frequently, or something like that. So it's no surprise that even if the new buffer drawing method were more efficient, it wouldn't affect the flickering problem.

I also noticed I was performing this drawing sequence as often as possible. I use timed ticks to advance the simulation, and—for now anyway—I don't change the figures being displayed when the simulation isn't advancing. So the frame draws in between ticks are a complete waste. I changed it to only draw once per tick. This probably keeps the CPU cool, so no more fan noise. But this doesn't fix the flickering issue. 60 refreshes a second doesn't seem like that much considering how capable it is otherwise (I can do many hundreds per second without flicker under other circumstances).

So now I'm starting to suspect the refresh itself being difficult for curses, again possibly having to do with using too many colors at once. I assume curses is issuing ANSI terminal sequences for clear, cursor mode change, and write events for everything on the screen with each refresh. It would make sense that more mode changes (colors) mean more commands to send to the terminal, and the frequency of refreshes is making slight delays more noticable.

I think I've done more than I should to fix this during a game jam, so the real solution is probably to port to another graphics platform after the jam is over. I can write chars directly into a PyGame window, or more likely I can just port the whole thing to JavaScript in a browser.

I also fixed file logging by simplifying how I'm using the logging library. There's supposedly a way to give each module its own logger, but my logging config wasn't propagating to each individual logger. So now I just use the root logger provided by the logging functions.

Oh! And I changed the UX in the image editor a bit. It used to be modal, with a menu mode and an edit mode. Now there are control keys available during edit mode, and menu mode is simply Control-X followed by an action key, such as Control-X S for save. Frame switching and color switching have control keys, so you don't need to leave editing mode for those. This leads to another surprise with curses, which is that control key sequences are a pain in the butt, and many of the good ones have special meanings in a terminal that I can't override. Also the escape key gets a delay. There's a way around that one, but I don't have time to worry about it.

Sunday 4:54pm

Text animation for cutscenes and dialogs:

Shown here is a demo of several features, including variable-speed variable-formatting text and, in the second run, the ability to accelerate text with a keypress. If I have enough time to do sound, I hope to add Nintendo-style blips to the write events. I'm pretty happy with this, and with the versatility of my display system.

Lesson learned: 60 frames per second seems like a lot, but when animating text, one character per frame is actually pretty slow.

I also shored up the display system a bit to accommodate terminal resizing. If you resize to smaller than the game, it'll ask you to fix it, instead of erroring out.

The game now has a tentative title: Lost Cat.

Monday 2:02am

The rest of Sunday went to a modal architecture for the start screen, cut scenes, and the game board. We also riffed some sound effects and vocals into GarageBand, though we're pretty far from a point where we can actually use them. Some post-processing will be required, to say the least. And I still haven't implemented the game board itself, which is a pretty huge set-back for a 6pm Monday deadline. And we're behind on art assets due to poor time management.

One of the cats got curious about the studio set-up, and I managed to get a bit of actual cat sound. As you can hear, this is hardly an ideal studio set-up, where computer fan noises and echoes dominate our tracks. Someday I'll set up a real booth for this sort of thing.

Monday 2:00pm

I have simplified the mode system, added a menu widget and a game mode. The game mode is still empty which doesn't bode well, with only four hours remaining. I'm clearly working on all of the wrong stuff.

But there's no giving up now. And whatever I don't finish before 6pm I can do after 6pm and beyond.

Monday 5:22pm

38 minutes left! I almost have a game!

Monday 6:55pm

We made it. Well, we made something, at least. But we submitted a playable early start of a game to Ludum Dare 29 just in time for the Monday 6pm deadline (using the jam rules).

As mentioned in the version notes at the top of this page, I'm missing art for the barn scene, and a reaction to the win condition of actually touching the cat. Simple versions of these shouldn't take more than another hour, but we were too close to 6pm to get it in, so I left it.

More important to making it fun is better level design. The engine supports an arbitrary pattern of dirt types and coin placement, so there's a ton of potential for making a real maze-like level that opens up Katamari-style as you upgrade your shovel. That was the intent, anyway. With real level design, even a one-cat game should be reasonably fun.

The full vision for the game includes multiple cats and cinematics, telling a little story the deeper you go. We also had big plans for sound and music. I'm mostly just charmed by the idea of having full sound and music for an ASCII-art game that runs in a terminal window. But before we get anywhere near that far, if we do want to invest in this game, I'll want to port the engine to the web, so more people can play it.

Meanwhile: I've included the ASCII art editor (imgedit.py) and a simple level generator (lvledit.py) with the game. I won't be providing instructions for these, but if the game is too boring and you're willing to dig deep, you can finish the barn art yourself. :)

If you've ever wanted to make a video game, give Ludum Dare a shot. It's great motivation, with a supportive community, and just great fun all around. I look forward to the next one.

P.S. A list of neat engine features I put effort into when I should have been doing basic gameplay:

  • Game object and display object system. Many elements are independent objects that can be iterated and animated independently. Game objects contain figures. The last minute rush kind of ruined the object structure, but in general this has proven satisfyingly flexible, and makes it easy to animate multiple elements in separate ways.
  • The art editor. This tool makes it easy to hand-draw colored ASCII art, with animation frames. Combined with the engine, it's easy to produce animated figures that live in the game world.
  • Animated text panels. These are currently used on the opening preamble and in the barn. Press space to speed up the text, Nintendo-style. In a later version, these will be wired for sound, and can be used for Animal Crossing-style dialog.
  • Pause menu. Press escape at any time to open it, and use cursor keys and space to navigate the menu.