Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411274 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 03:44:05 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsA Dance of Fire and Ice - tough one-button rhythm game! [Demo inside]
Pages: [1]
Print
Author Topic: A Dance of Fire and Ice - tough one-button rhythm game! [Demo inside]  (Read 9514 times)
fizzd
Level 0
**


View Profile
« on: July 09, 2015, 03:07:10 AM »

Hi guys,




(2nd August) Check our new trailer for next weeks Dare build

!


Play original version here!
Or here on Kongregate.
Or here on mobile for $1, with free updates as this game progresses!

A Dance of Fire and Ice is a tough one-button rhythm game about turning geometry into music.

You might know me from Rhythm Doctor, which won an IGF Student award last year. This is the second project I'm working on, which is also a tough-as-nails one-button rhythm game, and this time I'm working on it in a team. Our artist Kyle is an artist behind webcomic Soul Symphony, and our composer Jade is a music student at Berklee College of Music.

We recently got selected for the Dare to be Digital 2015 competition, which is a student competition for game developers. We're representing the University of Cambridge, California Institute of the Arts, and Berklee College of Music. And we have five weeks from today to get a revamped, high quality version of this game out for the Dare exhibition in Dundee, Scotland. We're competing with 15 other student teams in this competition, who are all probably spending their limited time more wisely than writing a devlog heh. But anyways..

This devlog serves to chart my struggles and lessons learnt from the programming side of things. We have a tumblr that serves more of a visual progress indicator with concept art and so on, but this will be a more thorough writeup. Willy Chyr's Relativity devlog has been a joy to read, and served as the main inspiration in deciding to do this. Hopefully you will find this devlog interesting too!

I'll start with going into detail about rhythm game design and the programming challenges in going from prototype art to full blown HD. Stay tuned!
« Last Edit: August 02, 2015, 11:05:23 PM by fizzd » Logged
Cunnah
Level 1
*



View Profile
« Reply #1 on: July 09, 2015, 03:10:39 AM »

Isn't that the name of the book series on which game of thrones is based upon?

EDIT: no just close the books were called a song of fire and ice.
Logged
jctwood
Level 10
*****



View Profile WWW
« Reply #2 on: July 09, 2015, 05:13:30 AM »

Really, really nice to see a Dare participant in the devlog thread. Good luck! The game looks visually stunning certainly. I have been at the past few Dare events to play the games made by students and I have to say my favourites are almost always local multiplayer, everyone is there with friends and it sucks to have to watch someone else play a cool, innovative game. Still rhythm games need to be revamped and this seems like a genuinely fun approach! Looking forward to the devlog.

p.s. I agree that Dance of Fire and Ice is eerily similar to the Game of Thrones series/book title. Maybe something like Visions of Flame and Frost? If you really want to stick with that theme. Smiley
Logged

fizzd
Level 0
**


View Profile
« Reply #3 on: July 09, 2015, 12:12:12 PM »

So let's start from the very beginning. How do you make a rhythm game? Well..

Devlog #0 - How to Dismantle an Atomic Bomb
(Breaking down the Rhythm Game)

I've done rhythm games a few times, and in fact they're the only kinds of games I've seriously worked on and ever want to do. And when I first started I found there wasn't much documentation around for the general architecture of rhythm games. So, this is meant to be a quick and dirty technical guide to how I approach my game architecture.

First, you might want to see this video for an explanation of the main mechanic of this game.

--

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.



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:

Code:
    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:



Code:
    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?


Code:
    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!

IMPORTANT ILLUSTRATION:




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

Code:
    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 by

Code:
(deltaTime / crotchet) * 180 degrees

so that by the time a crotchet had passed, we would have moved 180 degrees. Simple!

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.


  • 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.


One frame before key press:



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:


  • 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:

  • 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 you can ask me 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.

Special thanks: Tom Voros, creator of http://microngame.com, who helped me loads along the way with hints on getting the rhythm synced in AS3.
Logged
fizzd
Level 0
**


View Profile
« Reply #4 on: July 09, 2015, 12:18:29 PM »

Really, really nice to see a Dare participant in the devlog thread. Good luck! The game looks visually stunning certainly. I have been at the past few Dare events to play the games made by students and I have to say my favourites are almost always local multiplayer, everyone is there with friends and it sucks to have to watch someone else play a cool, innovative game. Still rhythm games need to be revamped and this seems like a genuinely fun approach! Looking forward to the devlog.

p.s. I agree that Dance of Fire and Ice is eerily similar to the Game of Thrones series/book title. Maybe something like Visions of Flame and Frost? If you really want to stick with that theme. Smiley

Oh cool, and thanks! Haha there's no reason you can't split up with your friends though, and besides sessions in this game are ideal for hotseat play because turns are very short, at most 60 seconds. Hopefully we can get some peripherals for true crowd-drawing arcade style action. For Rhythm Doctor at the IGFs we made a giant controller and just the sound of it clacking attracted a huge crowd!

The name is most likely gonna stick though, seeing it's already out there in the market now.
Logged
fizzd
Level 0
**


View Profile
« Reply #5 on: July 15, 2015, 02:05:17 AM »

Devlog #1: It Feels Like There's Oceans, Between You and Me, Once Again.
(Breaking Apart Levels into Individual Scenes)

First, a little bonus I wrote: an analysis of Rhythm Game Design in Orbit Or-Beat!

So, a lot of the past week has been implementing Kyle's assets. We've gone from this:



To this:



Quite an improvement! With a rhythm game though the most important thing is how it looks in movement (note how screens for 140 look absolutely mspaint but the videos are positively

).

I wish I could capture the movement at something above 5fps but my laptop can't handle it Sad One day we will find a solution! (..i.e. begging youtubers who've recorded footage of our games in the past, to record new builds for us.. hey if it works it works ok shh)
---

So let's talk Unity. From what I've learned, there are two ways to make a multi level game.

1) Have one scene per level.
2) Have one scene for all the levels, and every time you load that one scene, also pass in an argument that gives a level layout in a string, like "RRRUDDDLLLLLDLL".

Before all this, i was using method (2), i.e., every level in my game was shared in a single Scene. And I made a little interpreter that could read a string and convert it to a level. And all the levels were just hardcoded in the main Controller class. Prepare your eyes.

Code:
string[] leveldata = new string[]{"RRRRRRRRR",
     "RRRRRRRDRRRRRRRRR",
     "RRRRRRRURRRRRRRRR",
     "RRRRRRRURRRRRRRDR",
     "RRRRRURURRRRRURUR",
     "RRRRRDRDRRRRRDRDR",
     "RRRRRRRRRRRRRRRR" +
     "RRRRRRRRRRRRRRRD" +
     "RRRRRRRRRRRRRRRD" +
     "RRRRRRRRRRRRRRRD" +
     "RRRRRRRRRRRRRRRU" +
     "RRRRRRRRRRRRRRRU"..

So what would happen when the player won a level, is a currentlevel variable would increase, and the Scene would be loaded again. Nice.

The problem with this

..is that everything unique to a level now has to be done in code. The scene itself in Unity's editor is just completely blank. And so when Kyle gave me a layout like this:



..well, I wasn't sure how to even begin doing individual assets and placing stuff per level. One way would be to have assets be represented in that string as well, like

"RRRRDDDLLLL//ASSETS//CHAIN1/(230,300)/3.2x"

..yeah oh my god tweaking that stuff would be a nightmare. I could code up something that lets you take assets, alter them, and export results to XML or something but ohhh goddd noo think of all the numbers. Colors, position, scale, attaching individual scripts, the parameters of those scripts, nononono

Change

Sure, if I could switch to a scene-by-scene layout that would be cool because placing Kyle's assets would be a lot easier, BUT, the actual tiles themselves are a lot easier to write out in "RRLDLLD" form, than to painstakingly place in Unity's editor.

I just happened to be meeting up with a fellow gamedev in Cambridge, just after graduation (whoop whoop MEng Cantab!!) and two days before going back home to Malaysia. I told this little thing to him and he said

"Yeah why not just break it into individual scenes?"
"But I can't just place my tiles everywhere!"
"Then why not just extend the editor?"
"What like NGUI does? Isn't that like ridiculously complicated to do?!"
"Not really.."

And so..



Woohoo!!

Yeah, turns out extending the editor is actually pretty simple and well documented! Actually that's a lie. There were a few catches. But I had it up and running in a day or two, thanks to some great online tutorials. Now we get the benefit of

  • Visually placing assets using the editor, and
  • Still creating the level with strings.

Of course, the one problem with this is that now, instead of one instance of a GUI, a Controller, etc etc, now every single scene has their own instances of these things. And I think this multi-scene setup might actually cause some problems in the future. What if I added a new Menu object? Would I have to go to every single scene and add it 30 times? Can we not have like Master Slides the way they do in Powerpoint? Is there anything like this?!

If you have answers please let me know. Otherwise hope you enjoyed reading this and till next time.

Final reflections: writing one of these posts seems to take about an hour, including recording gifs and uploading them and all. Is it worth it? Is this time better spent working on current problems? Who knows! I think one mistake I did during my degree was spending too long trying to make perfect notes, when I should've done more practice questions. Maybe that applies here too.

(today's headline is brought to you by Seafret - Oceans.)
« Last Edit: July 15, 2015, 02:43:01 AM by fizzd » Logged
jctwood
Level 10
*****



View Profile WWW
« Reply #6 on: July 17, 2015, 03:32:19 AM »

This is looking great. It is nice to get some insight into rhythm game mechanics. The giant controller sounds fun too I hope you find time to make one for this game!
Logged

fall_ark
Level 2
**



View Profile
« Reply #7 on: July 17, 2015, 10:45:56 PM »

Great read fizzd. Hope you have a blast writing these & looking forward to more. Definitely an interesting insight into the insides of rythm games. (The thing about frame rate is super cool!) Grin
Logged

Chinese localizer and influencer. Translated Dead Cells, Slay the Spire, The Count Lucanor, Katana Zero, Dicey Dungeons, and involved in the localization of Reigns, The Curious Expedition, Desktop Dungeons, etc.
If you have questions about Chinese loc and publishing etc., find me at Twitter @SoM_lo
fizzd
Level 0
**


View Profile
« Reply #8 on: August 01, 2015, 07:49:56 AM »

Devlog #2: It Ain't the Speakers That Bump Hearts It's Our Hearts That Make the Beat
(Building a Snare Drum Controller)

Today is gonna be a little different: it's all about real life. Physically I mean, not like, student loans and society pressures. We made a snare drum controller for our exhibition!



(click for vid)



If you're interested, in how to do it: I bought a piezo transducer, which is this metal ring that comes enclosed in a black plastic. Wired it to an Arduino Leonardo (The Uno doesn't have keyboard input capabilities, beware, I had to go back to change it!). And the following was the code I ended up with, including a delay to avoid debouncing:

Code:
==
const int threshold = 600;
const int delaytime = 160;

void setup()
{
pinMode(A0,INPUT_PULLUP);
Keyboard.begin();
}

void loop()
{
//if the button is pressed

if (analogRead(A0) Less Than threshold) Keyboard.write(65); delay(delaytime);
}

===

Had a good friend help me with placements. The arduino was stuck to the side of the snare, and the piezo duct taped to the top. We tried several configurations including having it at the bottom of the snare, but it resonated even more down there causing multiple inputs per hit. So the top of the snare it is.

And best part is that it feels realllly good, especially with fast rhythms! If you're in Dundee on the 13th to 16th come try it!


(today's headline is brought to you by Twenty One Pilots - Holding On To You.)
Logged
fizzd
Level 0
**


View Profile
« Reply #9 on: August 01, 2015, 07:52:02 AM »

This is looking great. It is nice to get some insight into rhythm game mechanics. The giant controller sounds fun too I hope you find time to make one for this game!

Thanks! The drum controller is indeed working!!!

Great read fizzd. Hope you have a blast writing these & looking forward to more. Definitely an interesting insight into the insides of rythm games. (The thing about frame rate is super cool!) Grin

Hey, nice to talk to you again! Glad you enjoyed the post, and sorry for the lack of updates recently, we're entering final crunch to really get the game polished haha. Hope to have something nice for you to stream again soon!
Logged
fizzd
Level 0
**


View Profile
« Reply #10 on: August 02, 2015, 10:38:43 PM »

Finished our trailer!





Getting really smooth footage was a headache, but it was done. Change the resolution to 1080p60 for some silky smooth 60 fps action.
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic