Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

PizzaLovers007

78
Posts
1
Topics
2
Followers
A member registered Sep 07, 2018 · View creator page →

Creator of

Recent community posts

Pretty neat! Love me some rhythm games :D

I think the platforming overall was a pretty good difficulty, and seeing the 2nd half change one beats 1 and 2 made it an interesting challenge. I struggled with the camera a bit, and some of the perspectives made jumps difficult (especially the one right after the platforms in the wall), but otherwise this is a solid game!

Super cool concept and really solid execution! It's impressive how you managed to get all the physics working without feeling janky at all (I had ~60 fps the whole time, which probably helped). I would have loved to see more of the object blending incorporated into the gameplay somehow, though I'm sure you had plenty of ideas that you didn't have time to implement.

Haha, a well earned triple digit score! The red ants are definitely the trickiest part, but super fun to implement. Glad you had so much fun!

What wild ride, you really nailed the psychological horror genre with everything! The lighting, the music, the overall feeling as you slowly descend into madness as you try to keep your tamagotchi alive. I like the multiple end states too. Seriously great work here!

Glad to hear, it was definitely the kind of gameplay I was going for!

Thanks! And nice work on the 96 :D

Pretty neat shadow effect! I think because of it though, the enemies needed some other sort of tell since otherwise you turn the corner and immediately get shot. Maybe like a footstep or other sound? Something that let you know something was there, but not exactly where.

Well, a cactus hiding around the corner ended my run anyway and they don't make sounds lol

Thanks for checking it out!

I liked how in sync everything was with the music! Awesome style too. I think it would have been cool to see more variation in the platforming, but it's nice to see another rhythm-based game.

Great beatboxing at the end :)

Really interesting mechanic! I definitely had a lot of trouble understanding what color it would switch to at the beginning, and even after seeing the compass flash I think this could be communicated better. Maybe if the blurred section itself was colored to what it would be after rotating?

I did complete the main levels, but once I saw a green zone I noped out haha. Great puzzle design overall, and I enjoyed it!

This was a really cool idea! Definitely one of the more unique games in the jam so far.

Grats on the 79! I think I could have tuned the difficulty some more but alas the time limit. Glad you had fun with it!

Thanks for checking it out!

No worries, appreciate you checking it out!

(1 edit)

The shaders were super cool! And the puzzling was quite nice too, though I think I ran into a bug on level 20 where it won't solve even though all the tiles are black. Maybe I found an alternate solution that the game won't take? It won't let me retry either.

Dang getting sick during a jam really sucks :( hope you're feeling better now!

Pretty fun! I liked roaming around after beating the game, though I think the game froze after using too many bombs haha

Interesting game! I definitely felt lost a lot of the time thinking "is this where I'm supposed to be going?", but that's pretty much how I feel in dreams too so nice job.

Congrats on your first (?) game! Pretty cool art and visual effects. I think the dash sometimes felt like it was missing enemies, and it was a bit difficult to figure out where you would land. Still, this definitely has some potential if the mechanics are fleshed out some more!

Thanks for playing! Happy you had fun with it :)

Thanks for playing it!

Glad you enjoyed it! I agree the tutorial is a bit weak, but I just needed to get something in there to at least introduce the mechanics a bit before the actual gameplay. It's good you were still able to get the hang of it in the end!

Thanks for playing! Those mechanics were fun to make :D

Glad you liked the animation! Art is definitely not one of my strong suits, but I'm pretty happy with how everything turned out in the end. I actually did consider limiting movements to be strictly on the beat, but I think that would have led to more frustrating gameplay that would feel like dropped inputs rather than incorrect timing. Congrats on the 80!

(6 edits)

Hey everyone! I wanted to share my experience working with Godot to make a rhythm game and the challenges I encountered with it. Hopefully this devlog passes some useful information and implementation in case you want to make your own rhythm game in Godot.

(Code formatting on itch is unfortunately not very readable, so if you want a better experience you can read it on GitHub here!)

Why rhythm games are difficult (to make)

Besides being difficult to play, rhythm games are also difficult to make. For most game engines, there are two important computing threads for rhythm games: the main thread and the audio thread. The main thread contains all of the game logic like inputs, physics, animations, etc., but most importantly it signals to the audio thread what sounds to play. Unfortunately this communication has some few milliseconds of delay, which for rhythm games can be problematic.

To make matters worse, the internal clocks between the main thread and the audio thread are not synced with each other. For shorter durations, this is not a problem. However, if you start two stopwatches in both threads at the same time, they will eventually go out of sync with each other.

Godot has a great writeup on how you can address these issues as well as going into more detail on Godot's specific audio implementations. This was instrumental knowledge that I needed to program my game.

Goals for my game

With the above in mind, what did I actually need for my game?

  1. Infinite gameplay
  2. Obstacles (ants) that move one square forward/backward on the beat
  3. Sounds that play on the beat

Once I came up with these ideas, it was time to get to work!

Determining when a beat happens

The 1st goal actually dictated which method of syncing I used. While not completely accurate, you can get the approximate timestamp of where you are in a song and use that to get what beat you're on:

# First, get the playback position
var time_seconds = $Player.get_playback_position()
# Playback position only updates when an audio mix happens, so we can
# add the time that has passsed since then
time_seconds += AudioServer.get_time_since_last_mix()
# The he audio thread is slightly ahead of the actual sound coming out
# of the speakers/headphones because of latency, so subtract that
time_seconds -= AudioServer.get_output_latency()
# Finally, just a bit of math to get the beat
var beat = time_seconds / 60 * bpm

Now that I had the beat value, I could tell when the next beat happens by keeping track of the previous time and comparing it to the current time after truncating the decimal. Once the values changed, I could then signal that a beat happened. All this was centralized in a Conductor class:

class_name Conductor
var _prev_time_seconds: float
# Other scripts can connect to this signal to know when the beat happens
# and what beat it was
signal quarter_passed(beat: int)
func _process(delta: float) -> void:
    var time_seconds = $Player.get_playback_position() + AudioServer.get_time_since_last_mix() - AudioServer.get_output_latency()
    # Validation
    if not _is_valid_update(time_seconds):
        return
    var beat = time_seconds / 60 * bpm
    var prev_beat = _prev_time_seconds / 60 * bpm
    if floor(beat) > floor(prev_beat):
        # Beat happened this frame!
        quarter_passed.emit(floor(beat))
    # Keep track of the previous frame's time
    _prev_time_seconds = time_seconds

With this done, I could connect my ant movement logic to the quarter_passed signal!

You may be asking, why do you need to check if an update is valid? Due to how threading works, the current time can sometimes go backwards, so you need to account for this. There was also some weird bug on web builds that caused time_seconds to be extremely big. So, I didn't process frames where these oddities occurred:

func _is_valid_update(time_seconds: float) -> bool:
    return (
        # No weird web issue
        time_seconds < 1000 and
        # Time moved forward
        time_seconds > _prev_time_seconds)

It's important to note that the quarter_passed signal doesn't happen exactly on the beat. Signals can only be emitted on frame updates, so some amount of deviation is expected. Having a high framerate makes this better. For my game, this deviation was ok, but if you are writing a game where even this deviation needs to be known, you can adjust the quarter_passed signal to take the non-truncated beat.

Dealing with song loops

With infinite gameplay, I had no choice but to have a looping song. This is actually a valid reason for why the current time goes backwards, so I added some logic to account for this properly.

var _loops: int = 0
var _num_beats_in_song: int = round($Player.stream.get_length() / 60 * bpm)
func _process(delta: float) -> void:
    # ...calculate/validate time_seconds
    if time_seconds - _prev_time_seconds < -5:
        # Loop happened!
        _loops += 1
        # Make prev time on the same "loop" as the curr time. It's not
        # recommended to use song length directly as there can be small
        # inaccuracies with audio looping and the song itself
        _prev_time_seconds -= num_beats_in_song / bpm * 60
    var beat = time_seconds / 60 * bpm
    var prev_beat = _prev_time_seconds / 60 * bpm
    # Now add additional beats from previous loops
    beat += _loops * num_beats_in_song
    prev_beat += _loops * num_beats_in_song
    # And do the rest as usual...
# Validation needs to change as well
func _is_valid_update(time_seconds: float) -> bool:
    return (
        # No weird web issue
        time_seconds < 1000 and (
            # Time moved forward
            time_seconds > _prev_time_seconds or
            # Loop happened
            time_seconds - _prev_time_seconds < -5))

"Scheduling" sounds

Unfortunately, Godot does not support scheduling sounds, so this needs to be done in a more manual way.

Instead of checking if a beat happens *now*, I checked if a beat happens in the *near future*. This can be done by re-adding the output latency back to the current time.

# Similar to quarter_passed, but with audio latency accounted for. Use
# this to schedule sounds on the beat.
signal quarter_will_pass(beat: int)
func _process(delta: float) -> void:
    # ...calculate/validate time_seconds
    # ...emit quarter_passed signal
    # Now adjust the time to be in the future
    var latency_in_beats = AudioServer.get_output_latency() / 60 * bpm
    beat += latency_in_beats
    prev_beat += latency_in_beats
    if floor(beat) > floor(prev_beat):
        # Beat will happen soon!
        quarter_will_pass.emit(floor(beat))
    # Keep track of the previous frame's time
    _prev_time_seconds = time_seconds

I used this to schedule both the backing metronome as well as the other tick sounds indicating how the ants will move/are moving. Here's a small snippet of the backing metronome:

@onready var conductor: Conductor = get_tree().get_first_node_in_group("conductor")
@onready var hi_tick_player: AudioStreamPlayer = $HiTick
@onready var lo_tick_player: AudioStreamPlayer = $LoTick
func _ready() -> void:
    conductor.quarter_will_pass.connect(_on_beat_passed)
func _on_beat_passed(beat: int) -> void:
    if beat % 4 == 0:
        hi_tick_player.play()
    else:
        lo_tick_player.play()

Again, having a higher framerate will result in more accurate sounds, but 100% accuracy will always be impossible. Due to the nature of Godot's audio chunking, sounds can only be played at 15ms intervals (by default), and calling Player.play() schedules the sound to be played at the next mix. My approach schedules the sound to be played on the mix just after the true beat time. However, you can technically look further into the future with AudioServer.get_time_to_next_mix() to ensure sounds start on the mix just before the true beat time.

Summary

So, can you make a rhythm game in Godot? Yes! With some limitations:

  • Framerate matters! Higher framerates result in more accurate beat signals, so game performance needs to be top notch.
  • Scheduling sounds is not yet possible, but this can be somewhat worked around using Godot's audio APIs. Some amount of variance is unavoidable due to audio chunking and a lack of a DSP time/scheduling implementation.

That's all!

If you're curious, the full source code for my Conductor code can be found here on my GitHub.

Also, check out my game here! I'll be happy to check out yours as well :)

Pretty fun, I definitely wouldn't call this a dumpster fire! I think you had a pretty interesting concept going on, and the gameplay was engaging albeit a bit slow. I have to agree that the rendering and music were great.

Graphics and animation work was really well done! The mutation choice was also a nice touch. I thought the laser charge felt long, but after getting 5 eyes I could see why haha. Maybe a square aspect ratio would have worked better? Some enemies aggro'd offscreen, which was unexpected.

Tense atmosphere, and I really liked the dual camera view concept! A shame that it didn't have much to it.

I think it would have been nice to have some way to keep track of everyone's individual schedules, even if it were manual. As far as I could tell I couldn't mark dates on the calendar, and I could only select my answer. The concept was interesting though!

Interesting concept! I like the low fidelity style as well. Couldn't figure out the right way back since I swear the maze changed haha

pizza

Fun chill game! Definitely a nice break after playing some other submissions.

Thanks for checking it out!

Visual representation was honestly the biggest struggle the entire development time haha, I definitely agree even now it's hard to tell visually that you can fit in the diagonal gap. I probably could have figured something out, but alas I was running out of time and needed to work on other mechanics. Thanks for the feedback, and I'm glad you liked it!

(2 edits)

Really simple but really fun! Some of the aberration variations were quite clever. Out of curiosity, how many were there?

(1 edit)

Props for writing this in your own engine! I think the map design was really well done, and it was very clear when I needed to return to an area. The actual spike/enemy placement was a bit unfair at time (e.g. leaving the final boss room forces you to take damage), but overall I had fun playing this. The first boss was so rough with the wide spikes haha

Pretty fun! I think it would have been nice to have some sort of scoring system (maybe time?) so that there's some replay value and strategy.

Great core mechanic and style! Would have loved to see more variation in enemies.

Thanks for playing! Glad you figured it out in the end, and yeah definitely agree having a sound signaling when the new beat starts/ends would have been a great addition.