I had the urge to make a Halloween game. Taking the weekend, which amounts to 16 hours over three days in my schedule, I embarked on a personal game jam. The result is ghostly riddle fun.
This is a quick look at what I did, with some challenges and what’s new in my game engine, including better animation support, and things I’d like to have done better.
You can play the game for free. It’s a point-and-click riddle game. Figure out the cryptic riddles and you can discover where the ghost is hiding.
Concept
I wanted the player to find a hiding ghost, that’s all I knew initially. It wasn’t clear to me how I’d build a puzzle out of this. Using riddles was something that randomly popped into my head, which is odd, since none of my other games are riddle based. I actually shied away from riddles as they are heavily language dependent, but people like riddles, and I guess not everything needs to be universal.
My concept hinged on finding the right graphics. For such games, I look at stock vector graphics — I l wish I had an illustration partner, we could do so much more together. I found a suitable witch’s laboratory image. It had several obvious places and things in which a ghost could hide.
I heavily tied the riddles to the graphics of the game. You can’t figure out the answer to a riddle from the riddle alone. Only in combination with the graphics can the player find an answer. This made it much easier to write the riddles. At this point in the game, I only had some riddle ideas in mind. Most of the final text was written after the main implementation was done.
Let’s go to that development process.
Development Process
I have a custom engine I use for the games at Edaqa’s Room. It’s obviously what I’d use for this game. It has a few limitations, a few of which I fixed for this game and I few I lived with.
The first thing I do is the graphics work. Yes, the graphics were already drawn, but the pipeline needs to be fed. This means conversion to SVG files for editing in Inkscape. As browsers can’t handle complex files, I break them down into objects and a background, emitting WEBP files for them.
I don’t extract bitmaps manually. I give the items names in the file and have a tool that extracts and converts each. The tool runs in the background and does this as I make changes. Alas, I have yet to find a way to make this process go all the way back to the source file. After the initial conversion, all work needs to be done in the SVG files.
I’m getting better at this part of the process. There are still some questions about file management, but it works.
I have a pending project the requires working from source bitmaps, not vectors, which will be interesting to solve, where interesting probably means "a pain".
Animations
I wanted more animations in this game in the one scene than I’d had before. There were these candles that I felt should wave around. Plus, that cauldron in the middle should be properly bubbling!
This is a weakness in my game engine. It’s mostly based on CSS animations, where I can trigger animations from the game. Unfortunately, the HTML animations system was designed by people whose entire experience with animation is the HTML animations system. It was having nothing of my desire to stack two animations.
A sizeable chunk of the time spent on this Halloween game was changing where my animations are specified. I pulled the keyframes out of CSS and put them in my own game files. Now I used the animations system directly to play them. It is much better, but still quite limited. It would, however, be good enough for the animations I needed in this game.
Here’s an example for the candle flame, it’s half of its animation. If you’re familiar with CSS animations, you can probably understand what this is doing.
flame-1: duration: 1.35 repeat: 10000 direction: alternate keyframes: - offset: 0 transform: rotate(0deg) - offset: 0.5 transform: rotate(5deg) - offset: 1 transform: rotate(-7deg)
There’s a second flame-2
animation that does the opacity and scaling. By combining two independent animations I’m able to avoid the fixed visual repetition, as each cycle has its own timing. Each candle can also adjust the speeds, so the two candles also have some variety. Each candle actually has three animations, but oh my, I’ll get to that reason in another article.
The weird bumpy motion of the ghost hiding is not intended. I couldn’t get the transition easings to do what I wanted. The headache inducing CSS easing nonsense forced me to give up and accept a bumpy ghost, which doesn’t look that bad.
Going forward, I’m going to do some tests to see how much of the animation I can move into JavaScript directly, bypassing the HTML animations nonsense. It’s strictly a matter of performance whether I keep any of the old system. I’ll talk about this more when I get back to it.
Dialog and riddle text
I build my engine primarily for multiplayer escape rooms. This has been the dominant guiding principle for the way interactions in the game should behave. Unlike an adventure game, it meant that text prompts should never get in the way. All the players should be allowed to keep interacting even if there is active dialog on the screen.
I never had, and still don’t, have a reference game for the best UX here. I keep altering timings and adjusting the behaviour. In playtesting of my escape games, it seems to work well, but none of those had riddles. Indeed, puzzles where text was key usually had an additional text element on the screen.
In this game, the text isn’t on the screen long enough. Unlike my other games, the riddles require closely reading and deciphering, which entirely throws my timing estimates out of whack. Sure, the player can click on the mirror to read it again, but many players skim the intro text and aren’t aware of that.
This is a part of the system I’d like to revisit again, but I didn’t have time for in this game — I needed to finish it on the weekend. Here are some ideas that would have helped this game, some new, and some lingering from before:
- Endless prompts: require the user to click the close prompt to dismiss the text, as opposed to dismissing after a period. It’s a challenge to integrate this with the open flow in the general sense.
- Side panel: have an extra graphic item off to the side that the user can move around, that contains the text of the riddle. It gets in the way, though.
- Text log: a history of all the text that was shown. This is something I’ve wanted to do for a while now, but I’m not sure how to persist that text in the longer games.
- Modal sub-scenes: like the game items, provide a clean way to bring up a modal as a sub-scene
On top of this, I want to add better text formatting. It’s just all plain text now, which means I can’t even do line-breaks for the riddles, which makes it harder to see their verse form and rhymes.
Other Ideas
A few other random ideas for the engine came up while working on the game. Some of these have actually come up before, it’s probably those I should do first.
- Random text: Whenever the player picks a location, the ghost replies differently if they answered correctly of not. However, this text is always the same. I’d like to build in a system where the game can cycle through a list of text options.
- Chained events: I copied and pasted several bits of game responses since I don’t have a good way to combine actions. Keep in mind, the game code isn’t imperative, but a reactive language, which needs to work multiplayer over a network.
- Shared code: Each item in the game defines its own responses and actions. While in the source files I can make multiple items each behave the same, the compiler merely copies the entire set of responses to each game item. So far this is okay, but as I increase the response complexity, it becomes wasteful.
- Animations: Even after the animation upgrade I plan to abandon this entirely in favour of a new way, assuming it’ll be performant enough.
That rounds out my personal game jam experience. I’m happy with the result. Though there are things I’d like to improve, but it’s a fun little game, and it’s ready in time for Halloween.