How can I create a Rhythm-Based Input?

Hello guys, I am creating a rhythm-based fighting game, like “Sifu” but the player needs to attack on the beat like in “Metal: Hellsinger”.

I am using Quartz Subsystem for BPM determination and creating “on beat” events for the input. But currently I use “Timeline” to determine the input window. It has several downsides.

First, the game forgives the player when he presses a bit later than the bar, but I do not know how to “forgive” EARLY input.
Second, I do not know how to define the accuracy of the input. Like “Perfect”, “Good” and “Ok” input. I hope that this makes sense.

I use blueprints and have no experience in C++ whatsoever. Really hope that someone knows how to help me. Thanks)

Hello @Adalbala

I’m not super familiar with the Quartz system but logically speaking, if you had two information: your beat timestamp and an input timestamp you’d be able to calculate the early and late ease for the player with simple math.

I’d suggest to start from a quick prototype if you’re able to get these two timestamps in two separate events: input and beat. Quartz probably delivers you the beat events, and on an input event try to reach your clock handle and query the GetCurrentClockTimestamp function, save everything in the variables.
Then on the game tick calculate and inform the player on the result.

Be aware that in such games any delays (blueprint execution for instance) may introduce uneven experience for the player. Syncing audio is a complete different topic. Nevertheless, I’d say that it doesn’t have to be perfect to be a fun game.

Good luck!

Kind regards,
Taberu

I’ve done a rhythm game in godot, not sure how much of that will translate but the concepts should be the same.

The way I did it was I generated a song in a DAW, exported it as a midi, then wrote a python script to convert the timings of all the notes into milliseconds from the start of the song. Midi’s generally use ticks, and converting ticks to ms was not too hard.

Made a json structure for my notes at ms timing and read them into my game via a file.

Engine logic should go as follows, use a system clock and track the MS separate to that of your file. You know when the note is coming due to the file you generated, so see if the ms lines up or not and draw your own line as far as good, perfect, miss, etc. goes.

At the end of the day, there will be some issues keeping the audio in sync with the game state, but if you do what I suggested it should be minimal and you’d just need an offset setting to tweak the timing… most rhythm games have this since it just varies from hardware to hardware.

Good luck

Thanks for the idea)

I tried the GetCurrentClockTimestamp function but it does not want to compile unless I specify a certain name? I have no idea how to use “name” variables. And when I promote it to a variable it still refuses to print the beats. Kinda confused, maybe this function is not the answer or I am doing something wrong :confused:

No worries, you will get more intuition in it soon.

Look at the screenshot:


It’s actually quite simple. When you use the CreateNewClock function you can feed literal name or a variable into the ClockName parameter. Then you save a reference to the clock object into a variable. If you do that, you can do option no. 2. If you don’t have a reference, then QuartzSubsystem will have to look for your clock by name (which is implemented like a lookup table in the engine so comparisons are cheap). In option no. 1 you need to feed the same literal name into the function, which is a downside when you decide to change the name in the future: you will have to remember to change this name everywhere it is used in the whole project (and there might be same names but used in different functions with different goal).

Make literal is what you were missing here. :slight_smile:

Good luck!

P.S.
Name: ‘None’ is a special reserved name to indicate that it was never set or it was an invalid name. With pointers in C/C++ is the same story: there’s a nullptr which is truly a pointer of value 0.

It worked! :tada:

Thank you so much! All I had to do is to get GetCurrentClockTimestamp function from the variable I created :man_facepalming: