James Randall Musings on software development, business and technology.
Elite: sound effects and explosion refinement

Refining Explosions

On looking at the explosions this morning I realised that the faces of the ship that I use to form the explosion didn’t start off in the correct position. This resulted in a moderate amount of head scratching before I realisesd what was going on.

Each of the faces has its own normal and I’d used that as the nose orientation of the face so that I can roll and pitch it by a random amount as it flies off into space. Trouble is that the renderer, based on the ship renderer, assumes that the faces are aligned based around the ships nose orientation and so when you come to render them they look all jumbled.

To rotate and pitch the fragments correctly they do need their own nose (and roof) orientations and so the solution was to calculate the (vector) delta between the nose orientation and the face normal when the explosion is initiated and when rendering to use this to translate the nose orientation of the fragment back to “ship nose orientation”. Problem solved! And the explosions look pretty cool now.

Audio

There aren’t many sound effects in the original game but they do help bring things to life. When working with old games you can futz around with old, and often proprietary, sound formats. Or, in the case of even older games like Elite, attempt to recreate the effects based on the programming of the sound chip. Neither are much fun (not to me anyway).

A nice little hack I first used when recreating Wolfenstein 3D was to capture the audio directly from the game while playing it. I use a Mac for development and Rogue Amoeba make a great pair of tools for doing this.

First they have Audio Hijack which will record audio directly from the device and it pairs nicely with Fission which is great for quickly trimming and outputting as MP3. They work great together to capture old game audio in emulators.

And so this afternoon I spent some time playing the original game in BeebEm (there is a Mac version for modern hardware here) and recording the sound effects.

I’ve also taken the same approach as Wolfenstein for playing them back which is to create instances of HTMLAudioElement with the paths to the mp3 files. In order to play overlapping instances of the same sound I create a little wrapper for an array of them for each sound:

function createSingleAudioPlayer(path: string): Promise<HTMLAudioElement> {
  return new Promise((resolve) => {
    const player = new Audio(path)
    player.addEventListener("canplaythrough", () => resolve(player), false)
  })
}

async function createAudioPlayer(path: string) {
  const players: HTMLAudioElement[] = []
  for (let i = 0; i < 4; i++) {
    players.push(await createSingleAudioPlayer(path))
  }
  let currentIndex = 0
  return function (volume: number = 1.0) {
    players[currentIndex].volume = volume
    players[currentIndex].play()
    currentIndex++
    if (currentIndex >= players.length) currentIndex = 0
  }
}

I create these on startup of the game and wait for them to load so as not to have any stuttering when playing back.