Skip to content

“Adding Some Spice to Your Marriage”

I’ve participated in two game jams this year and released two games!

The first jam was during Awesome Games Done Quick 2017, and for it I made Composition. Which is… weird. We’ll go with weird.

The second was a horny game jam during February, and for it I made Adding Some Spice to Your Marriage, which is an “instructional DVD” (rhythm game). It has a couple of risqué photos in it but is otherwise abstract enough that it’s probably safe for work.

Spice is something I felt like talking about a little, so here’s a bunch of words! This post is just a collection of random things I wanted to say; there’s no arc through it or grand summary at the end that fits everything together.


Conception

Horniness is not something I’ve ever really felt a need to explore creatively. That said, most of the things I make don’t really explore anything that isn’t a part of the mechanical design, so it’s far from the only topic that I don’t have anything to say about. (And, in the end, I didn’t really end up saying anything!)

But, I ended up thinking about the jam anyway.

“What would I even do. Rhythm game where you do sexes? I have no idea how to make rhythm games, that sounds like a great plan.”

“Okay but I can’t compose. Well, for a rhythm game you don’t really need notes or chords probably, just a rhythm track. Rip off Birdman1 and make every sound in the game drums? Sure.”

“But what about graphics though, I can’t draw either. Well that’s not a problem, I don’t need to go full BEMANI and produce movies; all I need is an arrow that shows what directions to press. Simple vector graphics, maybe animate the prompt a bit, done.”

“How to make this horny though. Maybe animate the arrow to suggest it’s a dong? Ooh, instructional DVD: the arrow will show you sex movements, and then you have to do them yourself. To explain the graphics, we’ll go with the story that there’s a contract dispute with the actors and we don’t have permission to use any footage, so we had to make some placeholder graphics.”

“What if the instructions get increasingly elaborate and nonsensical, and then when you do them in real life the levels will end with you failing in some slapsticky way. Like, one of them is that you keep backing up, taking step after step until you’ve moved like 100 feet, and then you mash the right arrow key and take a running start to insertion. But when you try it for real, you end up tripping and flying offscreen. Yes. That’s it. I think I have to do this now.”

1: I don’t know how drums work and mean no disrespect to Antonio Sánchez in comparing my short, bad loops to his excellent work for Birdman. ^


Okay, But How Do I Make a Rhythm Game Though

Before fully committing to doing this thing, I gave myself one more hurdle: I needed to slap together a test of the basic systems to see if it was actually possible for me to write a rhythm game. I’ve never done anything that required such precise synchronization between sound, graphics, and input before, and also never put any thought into how rhythm games work, so that was exciting.

It took a few days before I hammered everything out and got a basic architecture going with all the components, and I continued to change chunks of it over the course of the project because I didn’t and still don’t have any idea what I’m doing.

(Uh oh, I started early. Don’t tell anyone.)

So if you want to know a bad way to make a rhythm game, here’s an outline of how Spice works:

  1. The game loop tells a metronome object what the current frame time is.
  2. The metronome provides a few events (game loop ticking, pausing, etc.) that objects can sign up to listen to and beat objects that can be used for synchronization.
  3. The systems of the game are handled by a library of graphics objects and a simple animation manager that lets you tween their properties, a drum machine you can load patterns into, and a chart manager you send key events to. All three synchronize themselves to beats and events from the metronome.
  4. And then the game controller is responsible for loading data into all three of those systems and making sure everything starts at the right time.

The Metronome, Beats, and Changing the Tempo

The game is fairly fragile and strongly depends on the game loop ticking the metronome a whole bunch (sixty times a second is the browser default and works fine) to keep everything running correctly, because the systems do maintenance every time it’s ticked (sending the callbacks for finished animations, checking if you missed a keypress, etc.) and the whole thing falls apart if they don’t do that regularly. There’s a lot of room to make everything more robust.

One of the things I decided at the very beginning, before I started coding, was that I absolutely, positively had to have an accelerando in one level. This is a big problem, because if we want to do that dynamically, then all of the systems have to be able to reschedule events so animations slow down if the tempo goes down and the next drum hit plays sooner if the tempo goes up. All without discontinuities, because we can’t have the game skip and cause the player to miss a beat.

My solution was to abstract time into the beat object. The first draft of the metronome gave out raw timestamps, so if you asked it for when the next whole note starts, it would hand you a number that you could then directly compare with the frame time provided by the game loop. The new beat object was just a simple struct containing the timestamp the metronome was started (let’s call it startTime), the duration of a thirty-second note at the tempo the metronome was set to when the beat was created (thirtysecondDuration), and the number of thirty-second notes from the starting time of the metronome that the beat represents (thirtyseconds). To explain that last one a bit more: the beat when the metronome starts would be 0, a quarter note after that would be 0 + 8 = 8, an eighth note after that would be 8 + 4 = 12, a whole note after that would be 12 + 32 = 44, and so on.

Calculating the raw timestamp that a beat object represents is really easy: it’s just startTime + thirtysecondDuration * thirtyseconds. The fun part is that this set of data, along with the current time, provides enough information to “convert” the beat and intervals to a different tempo, but first we have to define what “conversion” means.

The conversion process should take a beat, a new tempo, and a pivot timestamp (which will be the current time) and return a new beat with the following two properties:

  1. If the old beat is x beats ahead of the pivot time in the old tempo, then the new beat should be x beats ahead of the pivot time in the new tempo.
  2. If we have two beats in the old tempo and the pivot time is y% of the way between them, then the pivot time should also be y% of the way between the new beats.

The first property is just a formalization of the idea that, when you change the tempo at a specific time, all the notes after that time get faster or slower. The second property is more interesting: it says that if we’re in the middle of an interval of two beats and convert the endpoints to a different tempo, then we should remain at the same position relative to the endpoints. This is how we’ll ensure that animations don’t skip when we change the tempo; the animation system works in a typical fashion by tweening a property based on what percentage of the way through an interval the pivot time is, so if we make sure the pivot time is also the same percentage of the way through the converted interval, then there won’t be a discontinuity when the tempo changes.

The easiest conversion process that meets the first property is to calculate the number of thirty-second notes remaining between the beat and the pivot time: thirtysecondsRemaining = (timestamp(beat) - pivotTime) / beat.thirtysecondDuration. Then, you just make a new beat starting at the pivot time that lasts the remaining number of beats at the new tempo: {startTime: pivotTime, thirtysecondDuration: newThirtysecondDuration, thirtyseconds: thirtysecondsRemaining}.

It might not be obvious at first glance, but this conversion also meets the second property! The intuitive explanation is that the first property also holds if the beat you’re converting is before the pivot time—if it’s x beats behind the pivot time in the old tempo, then the converted beat is x beats behind the pivot time in the new tempo—and since there are the same number of beats before and after the pivot time in the converted interval, the ratio remains the same.

If you don’t find that argument completely convincing, I also did the full math for that one for my own benefit.

So with this conversion process, we can change the tempo whenever we want and keep everything running smoothly without any discontinuities: when the metronome changes the tempo, it sends out a notice to all the systems that depend on it, and they just change all the beats they’re using with new ones and keep on trucking. The music will proceed at the new speed, animations will continue from the exact spot of the tempo change, and the chart manager will keep accepting inputs on time.

The level with the accelerando in it uses no trickery at all like precalculating the tempo changes ahead of time; the animation and music and chart data are all defined in the exact same way as every other level, and all it does is bump up the tempo a couple of notches every so often. I’ve spent so much time talking about this because I’m really, really happy that I was able to get it to work exactly like that.


Experimentation, Failure, and Change

As I mentioned earlier without much detail, the original concept of the game was that an arrow would move around and, in its animations, give you the inputs you need to play every stage: when the arrow moves, that’s when you’d hit an arrow key, and the direction it moves is the direction you’d hit. I also wrote every drum loop so that every movement would also be timed with a drum hit and there was never a drum hit when you shouldn’t press an arrow (although I do use hi-hats in each loop to keep the rhythm going).

I was experimenting to see if this provided enough information to play the game. If you tapped along with the practice screen, it gave you a short text log of feedback on if you got a note wrong or right or missed it entirely, but otherwise you were on your own to figure out when to push a key and what key to push.

The result of the experiment was that literally no one could figure out how to play! So I added a scrolling chart that explicitly tells you the inputs and that seemed to work out a lot better for everyone. I wrote all the stage animations before I added the chart, so most of them end up going underneath the chart at some point and that’s unfortunate. The TV screen when you’re playing a level for real also overlaps the top of the chart a little bit. If I had planned the game from the beginning to have the chart, I definitely would have given it its own dedicated space.

Another bummer is that it’s now possible to just play the whole game while looking at the chart and completely ignore the animation and the silly sight gags. No real way around that one, unfortunately. I originally only had the chart on the practice screen so that you’d have to look at the animation during the execution phase, but if you can’t read the animation, that means you have to memorize the whole input stream and that’s not cool.

The other major change that happened over the course of development was that I never ended up doing that slapstick thing where you’d try to do a complex routine from the video and it would end in failure. There were two reasons for this.

The first was that I didn’t want to have to write two sets of animations for a bunch of levels. Heh.

But the second, real reason was that I changed the tone/premise of the game a little. One of the things I was struggling with was how to end the game; it didn’t have a story and all it consisted of was switching back and forth between watching instructions from a DVD and then doing them, and I didn’t know what to do once the player finishes the last level. Just rolling credits felt unsatisfactory, but there wasn’t a reason to play a cutscene or anything.

Eventually, I came up with a new premise so ludicrous that I knew I had to use it:

“What if the reason there’s no footage isn’t because of legal issues, but because the series of sexes in the game is actually some sort of demonic ritual and all the cameras were destroyed? And when you do them for real the graphics slowly start to distort, and at the end of the game you complete the ritual yourself and everything explodes and that’s the end. We can change the slapstick levels to ones that are physically impossible, like one where the arrow (dong) splits into two directions and then you have to move it two ways at once. YEP.”

A side effect of the change was that, now that the animations were going to be completely the same in the video and real life, it made less sense to split the game modes between screens in the way that it does; the whole reason that split was there was because the scenes were going to play out differently. I ultimately left the split in, because changing it would’ve taken effort (heh heh), but also because it does still make thematic sense that you get the instructions from the video and then carry out those instructions in a different space. But if I’d had the ritual concept from the beginning, I may have designed the flow of the game differently.


Stock Photo Library License Agreements

For the main menu, some DVDs use a montage of clips from the movie. For Spice I decided it’d be funny if the “plan” for the DVD was to do the same, but because of the whole no-footage problem, I instead had to hastily grab a bunch of watermarked stock photos from a photo library.

But, did you know: stock photo libraries forbid you from using their photos with watermarks on them, even if you purchase a license? It’s true!

Displaying and/or distributing to the public any watermarked or unlicensed Shutterstock Content (whether incorporated into a derivative work or alone) constitutes copyright infringement.

Shutterstock Terms of Use

Watermarked content cannot be used in any final materials or any publicly available materials and may only be used for the 30 days following download.

iStock Content License Agreement

So, what I did was find some photos online under modifiable CC/etc. licenses and apply a fake watermark from a 100% completely fake company to make them look like stock photos.

logo for the fictitious company Stuttershock

… please don’t sue.


Seeking and Diegesis

I don’t know if this actually bothered or confused anyone, but when you switch between different DVD screens, there’s a pause of about 1.5 seconds and the selection arrow goes away. That’s an intentional delay to emulate what happens with actual DVDs: the highlight layer of the selected button goes away and there’s a pause as the drive seeks to whatever part of the DVD it needs to load. The same thing happens when the main menu loops; I even made it loop slightly before the crash cymbal finishes sounding so it cuts off, just to be blunt about it.

It’s kind of annoying, haha. It even used to be 2 seconds, but I reduced it because I was tired of waiting that long.

It took me a bit to figure out how the practice screen should behave when seeking back to the main menu, because it has a few elements that live in the UI layer, not the DVD video.

screenshot of the level Mix-Up

The level name, main animation, and scrolling chart are all obviously a looping video and so directly part of the DVD2, so if the player backs out to the main menu then they should pause but remain onscreen. But what about the grading arrows that pop up when you press keys and the instructions in the bottom right corner? The arrows appear directly because of your input in a way that could never happen with a normal DVD, and the instructions make no sense as something that would appear onscreen.

They’re really just UI elements where I’m telling you how the game works, and I had no idea what method of disposing of them made the most sense. I ended up clearing them out entirely when you go back to the main menu so the only thing onscreen while it’s seeking is the DVD video, but that was fairly arbitrary. So if you were wondering why those things randomly vanish from existence on that one screen, that’s why.

The playing screen also has the same UI elements, but I don’t do anything special with them during the zoom transitions. During the shatter transition when you finish the last level, though, I did strongly consider clearing them out like the practice screen does when seeking; since it’s “reality” that’s breaking apart, it makes sense that the only things there should be the “real” things: the main arrow (you), the TV screen, and the pentagram. In the end, I left them in because that transition felt more satisfying to me with more visual elements in it. Hopefully the thematic inconsistency there doesn’t bother anyone too much!

2: Technically, at some point the practice screen should also have a pause to seek backward and loop, because DVDs have a limited capacity and can’t just show a sequence forever. But it’d be a horrible idea to interrupt the player like that, so I didn’t. ^


Firsts

Besides being the first rhythm game I’ve ever made, Spice is also the first action game of any sort that I’ve released (as long as you don’t count my VVVVVV level, “DSS Ideal X”). Every game idea I have always ends up being for a grid-based puzzle game; in fact, Spice‘s predecessor Composition was the first thing I’ve ever released that wasn’t a grid-based puzzle game! This game jam thing has got me trying out new things, so that’s really cool, even if I find them super stressful because I’ve never been a fast coder.

It’s also the first game I’ve ever made with a fully composed soundtrack. I don’t really have the ability to do any better than simple drums, but it still counts!

It’s also the first game I’ve ever made with a credits sequence!

So even though the game kinda looks like a shitpost, and maybe is a little bit, it’s a real, sincere thing with real effort behind it and I’m proud to have made it.

Post a Comment

You must be logged in to post a comment.