Friday, January 11, 2013

Rocks! Update #2 - it's a game

It's an actual game now!

So, first things first - here's the current version of Rocks!


New features include:

  • updated graphics - random rock shapes, and a progression of sizes
  • on-screen instructions
  • better sounds
  • proper collision detection
  • particle effects when things are destroyed
  • more than one level
  • a "shield" that will prevent rocks from running into you

It's looking a lot more like a real game now.

Sound design is hard

Oddly enough, the hardest thing for me so far has been making those decidedly "retro", simple sound effects. The Web Audio API is very powerful, but it's also very much focused on doing sophisticated manipulation of sampled sound. I certainly could have grabbed appropriate sampled sounds, or built some in Audacity, but I wanted to push the "classic" feel of the thing, and I thought - "I've done this sort of thing before, how hard can it be"? Besides, attaching a couple of huge sample files to a game that's currently under 20kb total in size felt a bit like the tail wagging the dog.

Of course, the last time I tried to create synthesized sounds from scratch was probably 30 years ago, on an 8-bit home computer with a fixed-voice synthesizer chip. There's something to be said for the existence of fewer choices helping to focus your efforts. When you're faced with an API that supports multi-channel surround sound, arbitrary frequency- and time-domain manipulation, 3-D positional audio, dynamics compression, and all the rest, it's a little difficult to figure out how to just make a simple "beep".

Here's what I've learned so far about using the Web Audio API:

Web Audio is based on a connected graph of nodes, leading from one or more sources through the graph to the ultimate audio output
This is enormously-flexible, and each of the individual node types is jut about as simple as it can be to do the thing it's designed for. There's a "gain" node that just multiplies the input by a constant and feeds it to the output, for instance. The source nodes don't have individual volume controls (because there's the gain node for that).

There's one weird quirk to my old-school sensibilities, which is that every note requires making another source node and connecting it to the node graph. When a note stops playing, the source node is automatically removed and garbage collected. If you want to play the same sound over and over, you're continuously creating and destroying nodes and connecting them to the graph.

There's a simple oscillator source node that's very flexible
You can easily create an oscillator that uses an arbitrary waveform (square, triangle, sine, on user-defined), plays at a specific frequency, and starts and stops at a specific time. This is about 80% of what you need to make a "beep", but:

Oddly, there's no built-in ADSR envelope support
Back in the day, we'd set up ADSR (attack, decay, sustain, release) parameters for a sound, which would control how quickly it came up to full volume, how loud it was as the note progressed, and how quickly it faded. There are probably about 10 different ways to do the same thing in Web Audio, but nothing with the same simplicity.

There's no simple white-noise source
This is a bit of a weird omission, in that noise sources are the basic building blocks of a lot of useful effects, including explosions, hissing, and roaring noises. And again, there's probably 10 different ways to solve this with the existing building blocks, each with their own limitations and quirks. I ended up using Javascript to create a buffer of random samples, which I could then feed through filters to get the appropriate noises for thrust and explosions.

The API is very much a work in progress
Despite the fact I wasn't trying to anything particularly sophisticated, I ran into a few bugs in both Safari and Chrome. I imagine a certain amount of this is to be expected with an in-development API that hasn't been standardized yet.

Next Up: Enemies!

The next big feature for Rocks! is to have some enemies to chase you around and shoot at you.

Saturday, January 05, 2013

One Game a Month, One Blog a Month?

A New Year Brings a Fresh Start

I swear, I'm not going to start this post out with how disappointed I am at my lack of writing output over the last year. Oops...

The Problem

No matter how much I promise myself I'm going to update my blog more often, it tends to languish. I have a bunch of half-written articles waiting to be published, but in the absence of any compelling deadline, I can continue to look at them as "not quite ready for public view" for forever.

A possible solution

Something I've seen work really well for other people who struggle with producing consistent output are what I think of as "creative challenges". Things like the "take a picture every day for a year" challenge that a lot of people are doing to improve their photography.

I just can't face the idea of a "blog a day" challenge, though - I like the idea of something a little more long-form, and a daily deadline would force me to cut corners to an extent I'm not ready for yet.

So instead, I signed up for the OneGameAMonth challenge. Game design is one of my non-programming passions, so I feel like I'll be able to stay motivated and really try to see this through. A month is a long-enough deadline that I feel like I can produce something worth examining, and the practical problems and "stuff I learned along the way" should provide ample material for *at least* one blog entry a month.

The Plan

I haven't planned the whole 12 months out yet, but here's what I do know my plans:
  • I will create a variety of games in different formats, including video games, board games, and card games
  • I will explore different genres in each format
  • Everything I do will be open-source on my Github account
  • I will write at least one blog entry every month, about the current game
  • If I don't finish a game in a particular month, I will not give up - I'll just do something less ambitious for the next month

The Proof

And to prove that I'm not completely full of it, here's the in-progress game for January, after two days of after-hours hacking:

It's named Rocks!

And here's the GitHub repository for it.

This is an HTML5 Canvas & WebAudio version of the old Asteroids arcade game. Because it uses some cutting-edge web features, it only runs properly in recent WebKit-based browsers. That's Google Chrome and Safari. Future games will likely be more cross-platform, but I wanted to learn a bit about the Web Audio API.

What I've learned on this project so far

This first version is very limited, and frankly pretty buggy:
  • There's no proper collision detection - it's hard to die, unless you try to hit a rock with the ship
  • The asteroids don't start larger and break up into smaller ones
  • There's no level progression, and no game-over when you die 3 times
  • No enemy UFOs yet
  • There are missing sound & visual effects
And the code is, frankly, a mess. But on the other side, there's a lot I've learned over the last two days:
  • All of the rendering is done using the Canvas line-drawing primitives
  • The sounds are synthesized on-the-fly using Web Audio units instead of sampled sounds
  • The animation is driven using requestAnimationFrame, so it should throttle back when in the background
  • The whole thing is less than 11k in size, and there's about 400 lines of Javascript in the main game file. That's smaller than a typical iOS app icon...