An LD48 Rhythm Game?! (Part 2)

Posted by
September 9th, 2014 1:07 pm

Hi all! This was my first game jam ever. I made a RHYTHM GAME! It’s one of the only rhythm games made for this competition. And I’m pretty happy with how it worked out. Even though it may lack some extra polish, I think it makes up by having a really precise rhythm engine. It’s what I spent pretty much all of the first day perfecting, and I tore it down and redid it twice. Anyway: play it here! (And please rate if you have the time, we could still use a lot more)

 

planets2


So, last time round I went through the inception and idea of the game. Now I’d like to give you a quick and dirty technical guide to making a rhythm game,  based on what I learnt from this jam. (I would write a general ‘things I’ve learnt from Ludum Dare’ but there are plenty of good posts on that already, so I think this specific guide would be of more use. And also the world needs more rhythm games.)

If you haven’t played it, either play it or watch this nicely done video analysis of the game, to understand what’s going on. (Thanks KOKOStern!)

I used Unity for this one – but my main project Rhythm Doctor uses Flash / Haxe. The tenets of the programming architecture that I’ve come up with are similar regardless of the language. Ok, here we go! What I’m gonna do is just enumerate a list of ‘rules’ that I follow, in classic BuzzFeed style/excuse of journalism. You Won’t Believe These Three Simple Rules!


A Quick And Dirty Guide To Making A Rhythm Game

First, here are the only objects I have in my Game scene:

Screen Shot 2014-09-04 at 23.13.28

Objects in the Scene

 

There’s the two planets, Red and Blue.

There’s a Controller which takes care of making the level and win/lose sequences.

And then there is the Conductor.


 

1. In a rhythm game, have a class that is used solely for keeping the beat.

In my games I call it the Conductor. It should have an easy function/variable that gives the song position, to be used by everything that needs to be synced to the beat. In this game for example,  the Conductor has a variable called songposition which is pretty much the cornerstone of everything in the game.

Screen Shot 2014-09-04 at 23.21.48

The above are the variables in the Conductor class. Some are specific to my game, but the general ones that I always have are

  • bpm, which gives the bpm of the song
  • crotchet, which gives the time duration of a beat, calculated from the bpm
  • offset, always important due to the fact that MP3s always have a teeny gap at the very beginning, no matter what you do, which is used for metadata (artist name, song name, etc)
  • songposition, a variable that should be set directly from the corresponding variable on the Audio object. This varies from engine to engine, but in Unity for example, the variable to use is AudioSettings.dspTime. What I do is, in the same frame that I play the song, I record the dspTime at that moment, so then my song position variable is set on every frame as follows:

songposition = (float)(AudioSettings.dspTime – dsptimesong) * song.pitch – offset;

Aside: the song.pitch is an inbuilt variable in Unity that gives the speed the song is playing at. By incorporating it into my song position variable, I can change the playback speed and still keep everything in sync. This was used in my game to slow all the songs down 20% because I only realised after composing the music that it was too difficult.

Anyway, now that we have set up our Conductor, time to take care of the objects that need to sync to it!


 

2. Every object that needs to be in sync should do so using only the song position, and NOT anything else.

This means, NO timers, NO tweens. It won’t work consistently!

If you use a timer that increments every frame (e.g. in the Update function), an inconsistent FPS is gonna throw the whole thing off.

If you use some sort of elapsed-time function, it’s still not going to be accurate enough, and if the song skips for whatever reason everything will get thrown off.

So, use only the song position. NO timers.

(Game design tip: have as many things respond to the beat as possible! Preferably everything!)

But even then, there is something more subtle that you need to pay attention to – and this is what I struggled with at first.

You see, even when using the song position variable for all things that sync, there still needs to be a reference point that you want to check the song position with. In the most basic case, all you would check it with would be ground zero: the start of the song.

Say for example you had four lights that you wanted to flash on the first four beats of the song. You’d write, in the Spotlight class script:

int beatnumber = 1; //or 2 or 3 or 4

bool islitup = false;

float bpm = 140;

float crotchet;  //the duration of a crotchet

void Start(){

crotchet = 60 / bpm;

}

void Update(){

if (Conductor.songposition > crotchet * beatnumber)

islitup = true;

}

But other times you might want an action that happens periodically instead of only once. When implementing something like this it can be easy to have a system that makes things inaccurate without you realising it. And so the most important yet simple rule I’ve learnt from this jam is:


 

3. Never update your reference point arbitrarily. Only increment it.

This might be a little subtle so let’s do this by example. Say you want to have a light that Flashes on every beat, instead of once. Here’s a simple way to do it which is… wrong! Can you see why?

float lastbeat; //this is the ‘moving reference point’

float bpm = 140;

void Start(){

lastbeat = 0;

crotchet = 60 / bpm;

}

void Update(){

if (Conductor.songposition > lastbeat + crotchet) {

Flash();

lastbeat = Conductor.songposition;

}

}

Literally five lines of code. Seems like it would work, right? Every time we move on to the next beat, we set the reference to the current time, and wait until another beat has passed.

BUT NO! All you will get is tears. And a flashing light that gets more and more out of sync with the music. Specifically, up to an additional 1/60th of a second more out of sync with each beat. (There’s a hint for you!)

The problem is exactly the rule I wrote above:  Never update your reference point arbitrarily. Only increment it by set amounts.

When we set lastbeat to the current song position, that’s what I mean by arbitrarily updating the reference point. The problem lies in the fact that your game can only work at a specific fps. 60 frames a second, say. So you can only perform a check 60 times a second. And so by the time the if statement returns true, you have already passed the time by up to a 60th of a second. And so what you are setting the lastbeat to is not the actual last beat, but a fraction after!

 

beat

So, what’s the right way to do this? By incrementing, not setting:

float lastbeat; //this is the ‘moving reference point’

float bpm = 140;

void Start(){

lastbeat = 0;

crotchet = 60 / bpm;

}

void Update(){

if (Conductor.songposition > lastbeat + crotchet) {

Flash();

lastbeat += crotchet;

}

}

Simple but important!


 

Applying These Rules To My Game

To be honest, though, I already knew this from developing my first rhythm game last year. But what caught me was a more complex manifestation of this scenario.

You see, in my game the planets orbit around each other following the speed of the song: a half revolution is exactly one beat. When the player presses a button, the orbiting planet and the stationary planet switch roles. Thus if the player presses a button every beat, the planets dance elegantly across the screen in a straight line.

The angle of the planet at any one frame would be given by the following reasoning. If song position is lastbeat at 0 degrees, and it should be lastbeat plus crotchet at 180 degrees, then the angle should be incremented on every frame, by

(deltaTime / crotchet) * 180 degrees

where deltaTime is Unity’s variable that gives the time that has elapsed since the last frame, so that by the time a crotchet had passed, we would have moved 180 degrees. Simple!

Screen Shot 2014-08-25 at 08.49.17

Moving in a straight line.

The problem is when the player doesn’t press EXACTLY on the beat (and to be fair, that’s pretty much impossible). The game had to be grid-based – it sure wouldn’t make for a very fun rhythm game if you had to compensate for a slightly early tap on one beat with a slightly late tap on the next. And so the problem I faced was snapping the planets to a grid while not making the snapping cause everything to be offset.

The first approach which at the time I thought was very clever was, at the time of the key press, to do several things.

Screen Shot 2014-09-10 at 02.37.44

One frame before key press.

  • Record the angle difference between the moving planet’s position and where it should snap to (i.e. 180 degrees in the case of the straight line)
  • Snap the moving planet to the grid, and make it the anchor
  • For the planet that was previously the anchor, offset it by that angle recorded earlier, and make it the moving planet now.
Screen Shot 2014-09-10 at 02.39.04

One frame after.

I thought at the time it was genius – the fact that you pressed it early is now balanced out by the previous planet moving back a bit, so that the next beat would still happen at 180 degrees!

And how did this strategy work out? Terribly!


Everything but the Kitchen Sync

At first it seemed all in sync and dandy, but as the song progressed the syncing got worse and worse. If you’ve been reading so far, you should already know why the game slowly went out of sync.

Yep, it’s because I broke the rule of never using anything other than song position for my calculations. In this case, I compared deltaTime to the song position when updating the angle of the planets. Don’t do this!

But – and yeah it gets a little complicated – even when I replaced deltaTime with a custom timeDifference variable calculated directly from the change in song position between frames, it still didn’t work!

And here’s where the subtlety lies: by incrementing the angle each frame, I was implicitly using the song position at the previous frame as a ‘reference’. Each time I was incrementing this reference by an amount that depended on how much time had passed between frames. And the result of all these calculations, small errors built up that contributed to the game going out of sync.

(Yeah, rhythm games can be tough. In making a rhythm game, it’s absolutely vital that your engine works millisecond-precise, so don’t worry if you take a lot of time making it work perfect, it’s worth it.)

In the end, I fixed everything by going back to the golden rule: only incrementing the reference point by a set amount, an amount that did not depend on frames. Here was the very final solution, which comprised of a few sub solutions working together:

  1. Get rid of the incrementing angle by time difference every frame. Instead, interpolate! Record the song position at the last time the planets switched, call it last hit say. Also record the angle your planet was at, at the time of the last hit. Now, your angle at any frame is just something like this:Screen Shot 2014-09-10 at 03.09.06
  2. To solve the player-not-hitting-exactly-on-beat problem: instead of lasthit being the time at which the key was pressed, it’s the time at which the key would have been pressed, if it was pressed exactly on time. In other words, this last hit variable is ALWAYS only incremented by multiples of the beat! This was what completely eliminated any arbitrary reference points in the calculation, just like the problem of flashing a light on every beat that was discussed earlier. In other words, we are taking away the exact time a player presses the key as a factor in our calculation, and that tidies things up a great deal.

 

(Exactly how to calculate the time the key would have been pressed if it was on time was a mathsy and not particularly interesting problem involving angles and geometry, but it’s in the source code if you’re interested.)


 Conclusion

And that, ladies and gentlemen, is how to make a rhythm game. Hope it illuminates how seemingly small time differences are actually the most important things when developing one. Thanks for reading this far! If you learnt something from this article, I would be grateful if you would check out the game and rate it – it still really needs some more!

Also if you did indeed read the entire thing, it would be safe to say you are somewhat interested in rhythm games. My own game Rhythm Doctor will be out in the distant future, and if this article helped you, you can sign up for the newsletter on the site to get a little email when it the full version is finally out. It will make my day. :)

P.S. Since I’ve written this much already, I might as well complete the postmortem by talking about the music. It was done in about 4 hours in FL Studio, and the main percussion you hear at the start was basically me trying to pay homage to the sick beatz in DJ Snake and Lil Jon’s piece. You know the one that goes

tatatataatattatatatatataaaaaatatatatttatattatattataaaaa

tatattatatatatattaataaaaaaaa rest, 2, 3,

TURN DOWN FOR WHAT?!

Then I added a really nice sounding free Chinese Gu Zheng, which at first made it sound like something from The XX. Though the decision to use it was due to the fact that I had used it before when making this boss fight for Rhythm Doctor, which in turn was very much inspired by the Shanghai Restoration Project – the sickest opening 20 seconds ever!

I don’t usually use FL Studio, and I owe everything to this video by last LD’s runner up DDRKirby(ISQ). Watching someone who’s an expert at something performing it, no matter what that thing is, is always really cool, and you can always learn something from it.


5 Responses to “An LD48 Rhythm Game?! (Part 2)”

  1. Will Edwards says:

    This reawakens my ambitions of having decorative particle effects e.g. chimney smoke puff in time to the music in an RTS I used to mod.

    How might you do that kind of thing in, say, HTML5? There is no midi etc. :(

  2. fizzd says:

    I haven’t used HTML5 myself, but from what I remember reading, music and sound was ridiculously difficult to work with apparently? At least in my experience, every HTML5 game I play in Safari has had hiccups in sound. I wouldn’t make a rhythm game with it just yet. 😛

  3. iambored2006 says:

    It’s always nice reading about other people having similar problems! I’m working on a game which is heavily based on time. There’s a ticking clock, and each playtime is supposed to be exactly 6 minutes. For measuring time, I didn’t want to use the system time, because those functions are always very time-consuming (and you say they weren’t accurate enough for you? that’s weird). Anyway, I went with a tick-timer solution, updating a counter every tick. This worked really well within the code, except for the obvious problem of inconsistent FPS. My solution was checking the system time every 30 ticks, so I get the best out of both methods.

    I’ve never been interested in rhythm games until I played your game and read this article. Now I find it so interesting that I want to make one myself! So thank you very much! [read that in a sarcastic tone; it’s because of people like you that I’ll never be able to make all the games in my todo list…]

  4. klianc09 says:

    Hey, I just checked out your game Rhythm Doctor, and I must say it’s amazing. My expectations were really low, because it’s “just” a rhytm game, but I must admit after that first boss fight, you got me sold.
    Great and simply awesome music.

Leave a Reply

You must be logged in to post a comment.

[cache: storing page]