• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM Trying to make a Rhythm game

Q

Quelandoris

Guest
So I'm trying to make a rhythm game, and for this I need my obstacle objects(Whose only code is y-=4) to spawn at particular times in the song. I have a devoted object that keeps track of when it needs to create these obstacles.

Right now its code is as follows:
Create:
Code:
///Play music
//set sound
audio_play_sound(song,100,false);
//Set sound offset
off = ((room_width-player_ob.x)/4)/room_speed;
Step:
Code:
///Spawn objects at correct times
//At 5 seconds in
if(audio_sound_get_track_position(song)==5-off){
    instance_create(1025,130,high_ob);
}
So some explanation:

The "off" variable is the offset between when an object spawns and when it will reach the player. The variable is there mostly to make it easier for me to set my game to the music.

The "high_ob" is one kind of obstacle.

After using show_debug_message, The game never gets into the if statement at all.

Any help would be appreciated, thank you in advance!
 
D

Dudeidu

Guest
Not sure if that will do it, but since audio_sound_get_track_position returns a number in seconds, maybe you should use round(5-off) instead and see if it works.
 
C

CedSharp

Guest
Let's say your room is 1024x720.
Let's say player is at position 200 on the x axis.
Let's say room_speed is 30

off = (( 1024 - 200 ) / 4) / 30
off = ( 824 / 4 ) / 30
off = 206 / 30
off = 6.866666666666666666667

So it's literally impossible for your song position to == 5-6.8666666666666667.
Either use floor() to round the value, or use '>=' to accomodate for imperfect matching.

CedSharp
 
Q

Quelandoris

Guest
Let's say your room is 1024x720.
Let's say player is at position 200 on the x axis.
Let's say room_speed is 30

off = (( 1024 - 200 ) / 4) / 30
off = ( 824 / 4 ) / 30
off = 206 / 30
off = 6.866666666666666666667

So it's literally impossible for your song position to == 5-6.8666666666666667.
Either use floor() to round the value, or use '>=' to accomodate for imperfect matching.

CedSharp
You're right that i forgot to round the off value, but its still not working. Even if I remove the off variable from the if altogether, it doesn't spawn.
 
C

CedSharp

Guest
You're right that i forgot to round the off value, but its still not working. Even if I remove the off variable from the if altogether, it doesn't spawn.
Did you try the '>=' ?
Maybe it's never perfectly on the value.
 
Q

Quelandoris

Guest
Did you try the '>=' ?
Maybe it's never perfectly on the value.
Didn't work either.

I think i'm just going to create a timer variable that will track how many steps have passed since the objects create event.
 

Mr Magnus

Viking King
try printing out what values audio_sound_position is cycling trough. it might just be that it is counting incorrectly, in an unexpected format, or whatnot.
 
I

Ingemann

Guest
The problem is that using a timeline won't stay in sync with the music.
If, for whatever reason, the music stutters for 50ms ( loading, cpu, ram, whatever ) then the whole timeline will be off-sync.
Also if you place the audio event inside the timeline?
 
I

Ingemann

Guest
I just tried it with a timeline. It's quite doable, I also calculated the BPM against the FPS of the room, just set a "playing" variable to make sure the music event only executes once. Or use isPlaying.

Edit: It goes out of sync after a while for some reason I'm trying to figure out right now. Lookng into the delta time.
 
Last edited by a moderator:
C

CedSharp

Guest
I just tried it with a timeline. It's quite doable, I also calculated the BPM against the FPS of the room, just set a "playing" variable to make sure the music event only executes once. Or use isPlaying.

Edit: It goes out of sync after a while for some reason I'm trying to figure out right now. Lookng into the delta time.
What I meant is not being able to start the music at the same time, that's easy.
What I meant is that the "music player" can "lag", you can compare that to when you watch a video and the video pauses for 2 seconds because it has to load, because randomly
it wasn't able to keep up.

The music player in gamemaker isn't any different than any other music players, so it will have those stutters.
Maybe not on the dev's pc, which often has better performance than the "usual every day folk's PC", but
on less powerful pc it will definitly happen.

In a rythm game, the most important feature to make sure to optimize and make perfectly working is the sync between
the music and the interactivity. This is why I don't recommend, and by far, the use of timeline to handle the game.

The usage of "audio_sound_get_track_position" is a very good initiative, so we just need to make it work.
It returns the position, in seconds, that the music is currently.

IMPORTANT: audio_play_sound returns a special index for the playing sound. You should use this index with any other sound-related scripts to get/set info
Maybe I'm wrong, but you don't seem to actually do this, the manual mentions it.


The logic I would follow, if you want to create an instance every 5 seconds, is as follow:

// -- Create Event
timer_delay = 5;
timer_last = 0;
music = audio_play_sound( song, 100, false );

// -- Step Event
var time = audio_sound_get_track_position( music );
if( time - timer_last >= timer_delay ) {

timer_last += timer_delay;
instance_create( ... );
}

Hopefully this will fix your problem ~

Regards,
CedSharp
 

Mick

Member
In a rythm game, the most important feature to make sure to optimize and make perfectly working is the sync between
the music and the interactivity. This is why I don't recommend, and by far, the use of timeline to handle the game.

The usage of "audio_sound_get_track_position" is a very good initiative, so we just need to make it work.
It returns the position, in seconds, that the music is currently.
I totally agree with @CedSharp . His solution should work. You should know that audio_sound_get_track_position() will return the current position in seconds, but it's much more precise than returning a whole second, it is a decimal number and can be like 10.234674.

You might want to trigger objects at irregular intervals in the game I guess, like sometimes every beat, then just every bar, every second bar etc. For this you can create a list of triggers containing the spawn times. When track position is greater than or equal to the trigger time you do your stuff and increase the list position.

Create event:
Code:
triggers = ds_list_create();

ds_list_add(triggers, 5);
ds_list_add(triggers, 7.5);
ds_list_add(triggers, 8.75);
ds_list_add(triggers, 10);

trigger_count = ds_list_size(triggers);
trigger_current = 0;
Step event (borrowing code from CedSharp):
Code:
var time = audio_sound_get_track_position( music );
if(trigger_current < trigger_count)
  if( time >= triggers[| trigger_current++] ) {
    instance_create( ... );
  }
}
 

Mick

Member
Also, to ease the work of creating a list of triggers you can create markers visually in an audio editor like SoundForge (markers are saved with the wave file in certain audio editors). You can then use a tool like this one: http://pixelsiege.net/markers/ to export the markers to a text file and import that into your game and do some automatic processing or just manually edit the exported text file to get your trigger positions.
 
I

Ingemann

Guest
@CedSharp and @WitchMaster, I think you're looking for delta_time, as I wrote in my last post. Delta time gives you the actual microseconds between frames, and then you can adjust accordingly on each step to avoid lag between the music and the graphics. Otherwise the music will usually outrun the graphics sooner or later.

There's an example in there.

https://docs.yoyogames.com/source/dadiospice/002_reference/date and time/delta_time.html

Edit: it works perfect for timelines as a windows export, but experience so far has shown me that you have to use delta_time for other exports - trying to implement it now.
 
Last edited by a moderator:
I

Ingemann

Guest
Edit: So basically the idea is to adjust the room_speed after the delta_time difference, but I have some more testing to do. But..

Code:
global.diffr = round(global.ot-delta_time)/6666;
room_speed = 30 + global.diffr;
And the diffr is next to stable in a windows export as you can see here:

 
Last edited by a moderator:
C

CedSharp

Guest
Edit: So basically the idea is to adjust the room_speed after the delta_time difference, but I have some more testing to do. But..

Code:
global.diffr = round(global.ot-delta_time)/6666;
room_speed = 30 + global.diffr;
And the diffr is next to stable in a windows export as you can see here:

I think this is an interesting idea. I've never tried to change the room_speed.
But I also think this solution is completly beside the point of the op.

The only thing he wants to do is spawn objects when the music reaches a certain position.
What you are doing is overkill for this simple request.
 
I

Ingemann

Guest
The solution we suggested don't need delta_time at all, it relies on the audio track position alone.
Yea, and that's fine in theory, but the graphics will fall behind the audio with time in, say, a browser export. That's why something like delta_time was made in the first place, to compensate for the lag.
I think this is an interesting idea. I've never tried to change the room_speed.
But I also think this solution is completly beside the point of the op.

The only thing he wants to do is spawn objects when the music reaches a certain position.
What you are doing is overkill for this simple request.
I don't find that 5 mins of drag n drop programming and 2 lines of code, is any sort of "overkill" compared to your code tho? I'm not even too sure that your code won't let the graphics fall behind, have you tried it?
 

Mick

Member
Yea, and that's fine in theory, but the graphics will fall behind the audio with time in, say, a browser export. That's why something like delta_time was made in the first place, to compensate for the lag.
The graphics should not fall behind because the track position is checked every step if it has reached a certain point, so it doesn't matter if the previous step was slower. The code I posted entirely relies on the position of the audio track so the framerate can vary, also if the audio is paused and resumed it should work ok.
 
I

Ingemann

Guest
The graphics should not fall behind because the track position is checked every step if it has reached a certain point, so it doesn't matter if the previous step was slower. The code I posted entirely relies on the position of the audio track so the framerate can vary, also if the audio is paused and resumed it should work ok.
True, spawning of objects should not fall behind, but if they contain any animations that follow the rhythm or just does some stuff on cues, or if any animations are supposed to follow the rythm at all, then that will fall behind if the cpu has to choose between graphics/image_speed/fps and sound. It can be resolved with delta_time, also in your example. In that case you would have to adjust the image_speed or the room_speed after the delta_time difference. So yea, it would work ok, for nothing but spawning of objects tho, while my timeline example with delta_time also works for animations alongside and is resolved with far less code. It's a fair mention, in my mind.
 
C

CedSharp

Guest
Yea, and that's fine in theory, but the graphics will fall behind the audio with time in, say, a browser export. That's why something like delta_time was made in the first place, to compensate for the lag.

I don't find that 5 mins of drag n drop programming and 2 lines of code, is any sort of "overkill" compared to your code tho? I'm not even too sure that your code won't let the graphics fall behind, have you tried it?
It's not the code that you write that is overkill, it's the processing that the game will do and the use of the processor that will be overkill.
Sometimes, 100 lines of code is a hell lot better ( and optimized ) than 2 lines of code. Actually, most of the time it is :p

True, spawning of objects should not fall behind, but if they contain any animations that follow the rhythm or just does some stuff on cues, or if any animations are supposed to follow the rythm at all, then that will fall behind if the cpu has to choose between graphics/image_speed/fps and sound. It can be resolved with delta_time, also in your example. In that case you would have to adjust the image_speed or the room_speed after the delta_time difference. So yea, it would work ok, for nothing but spawning of objects tho, while my timeline example with delta_time also works for animations alongside and is resolved with far less code. It's a fair mention, in my mind.
The timeline will not work, you will always fall short. Even if you change the room speed, the timeline doesn't know about the music position.
The animations of each object should, of course, be based on the music position. You don't just sync instance creation, that's dumb.
Everything in your game should listen to a "master" 'tick' or 'step'. You can't use GameMaker's step logic to listen to if obviously because
the gamemaker step events aren't in sync with the music. So changing the speed of the game all the time will just make it overly complex to handle.

Checking the music position to create an instance is the best way to do it right now, without any overkill setup.
The game runs normally, without any fps change, without room_speed changes, without nothing.

And if the animation should be in sync with the music ( which op didn't mention ) then the same logic as creating the instances should be applied.
The timeline is good if you want to base everything on GameMaker's step event.

This is not the case here, he wants to base what he does on the current music position.
So timeline is the wrong way to go about it ( but I never said it wouldn't work )

Regards,
CedSharp
 
I

Ingemann

Guest
The timeline will not work, you will always fall short. Even if you change the room speed, the timeline doesn't know about the music position.
You place the audio_play event inside moment 0 of the timeline of course, and then make sure it never returns to moment 0 again, and it's nothing like a regular mp3 player or such like you said, because winamp or media player or alike loads the music when you execute the media file, hence stutter, game maker preloads the mp3/ogg/wave unless you tell it otherwise, so it's nothing like what you say, and I have just showed you a video of it where it works flawlessly + I have tested it several times today. Your method will go out of sync once the cpu can't handle any possible animations, not with instance creates as in your example, but with animations. And what kind of a rhythm game is completely void of animations? Few I take it.
 
C

CedSharp

Guest
Um, by default, if I'm not mistaken, musics files are streamed, to leverage load time.
Even if the music is all loaded in memory, gamemaker still has a music player, which doesn't run in sync with the game itself.
I also mentionned that on a good pc ( like yours ) the delay will be small and probably not noticable.
On less powerful machines it will be quite evident.

Anyhow, I'm done arguing with you, if you think timelines are better suited for the task, fine.

Your method will go out of sync once the cpu can't handle any possible animations
I wonder why? I don't think so. If I use the position of the music, it's simple:

var time = audio_sound_get_track_position( music );
var beat_count = time / BPM; // BPM is a constant that should be defined in the macros
var beat_per_second = beat_count/60;
var current_beat = mod( beat_per_second, 4 ); // returns if on beat 0, 1, 2 or 3
image_index = floor( sprite_get_number( sprite_index ) * 4 / current_beat );


the above code assumes your image animates over the span of 4 beat in the music.
ofc the code can easily be modified.

I don't see how this will overload the cpu in animation?
You're only working with numbers, it's super easy for cpu to handle.
 
Last edited:
I

Ingemann

Guest
Um, by default, if I'm not mistaken, musics files are streamed, to leverage load time.
Even if the music is all loaded in memory, gamemaker still has a music player, which doesn't run in sync with the game itself.
I also mentionned that on a good pc ( like yours ) the delay will be small and probably not noticable.
On less powerful machines it will be quite evident.

Anyhow, I'm done arguing with you, if you think timelines are better suited for the task, fine.


I wonder why? I don't think so. If I use the position of the music, it's simple:

var time = audio_sound_get_track_position( music );
var beat_count = time / BPM; // BPM is a constant that should be defined in the macros
var beat_per_second = beat_count/60;
var current_beat = mod( beat_per_second, 4 ); // returns if on beat 0, 1, 2 or 3
image_index = floor( sprite_get_number( sprite_index ) * 4 / current_beat );


the above code assumes your image animates over the span of 4 beat in the music.
ofc the code can easily be modified.

I don't see how this will overload the cpu in animation?
You're only working with numbers, it's super easy for cpu to handle.
How clever is it to have to code every animation? Give me a break. Delta_time my friend, that's why it's there.
 

Micah_DS

Member
Just wanted to chime in and say that I've accomplished this using delta_time to keep track of timing and it worked beautifully. It's the most solid way I personally know of to do this.

I used it for an odd music video project; I made a program that did various light flashes and such on time with the music and I captured it with OBS to make it a video. I also made a beat counter project a while back to test the timing sync and it maintained timing for many minutes on end (until I stopped it, so I believe it would work indefinitely).

It really isn't going to hurt CPU either. You'd have to be on a pretty sad potato.
 
C

CedSharp

Guest
Just wanted to chime in and say that I've accomplished this using delta_time to keep track of timing and it worked beautifully. It's the most solid way I personally know of to do this.

I used it for an odd music video project; I made a program that did various light flashes and such on time with the music and I captured it with OBS to make it a video. I also made a beat counter project a while back to test the timing sync and it maintained timing for many minutes on end (until I stopped it, so I believe it would work indefinitely).

It really isn't going to hurt CPU either. You'd have to be on a pretty sad potato.
I don't mind the use of delta_time at all. My biggest problem is changing the room speed every step.
It doesn't make sense to me, it's like changing the speed of your hearth beat every second. Yes, SOMETIMES it accelerate or slows down,
based on circumstances, but mainly your hearth beats at the same speed most of the time.

How clever is it to have to code every animation? Give me a break. Delta_time my friend, that's why it's there.
Coding every animation is quite normal. Actually, it's not even related to this subject. For instance, if you want your game to have a player that has an animation the
"accelerates" and "decelerate" at the speed you are running at, might you want it or not, you'll have "to code it" and you won't use room_speed.
I don't see how doing a simple mathematic formula can be bad compared to changing the timing system of the whole game.

Just saying
 
I

Ingemann

Guest
That's why I said I would implement it with image_speed instead, in your example. R-e-a-d. Besides it hardly changes the room_speed at all if it's made properly.

And whatever, go ahead and make a rhythm game where every subimage for every animation has to be coded in, if you think that's so clever.
 
Last edited by a moderator:

Micah_DS

Member
I don't mind the use of delta_time at all. My biggest problem is changing the room speed every step.
It doesn't make sense to me, it's like changing the speed of your hearth beat every second. Yes, SOMETIMES it accelerate or slows down,
based on circumstances, but mainly your hearth beats at the same speed most of the time.
Ah! I must've been a bit too tired to read it all properly or something, because I didn't see where changing room speed was suggested until I read it over again. No no no, I wasn't suggesting that at all.

Sorry @Ingemann, but the suggestion to change room_speed is not good, nor is it necessary. delta_time measures the time taken between the last step and the current step, and that's all that is needed for this.

Furthermore, when you change room speed, you will likely break some of your game's logic, unless you're also multiplying movements and all physics by delta time (but it's utter hell if you try this...).

In explaining, if your room speed is set to 60, and some movement takes place, say 2 pixels per step, this means that thing moves 120 pixels in 1 second (2x60). But if your room speed changes, that thing will move more or less than it's supposed to in a second! This means your game becomes inconsistent and that's really really bad. There are other reasons to avoid changing room speed all the time, but this is the major one.

(EDIT: Actually, I didn't explain it all that properly here, because the main reason why it becomes inaccurate is due to divisions that are not clean [ e.g. "not clean" 1 / 3 being .3333, and .3333 x 3 = .9999 when it should be 1 ] and a bunch of very small numbers that have an element of randomness. It becomes inaccurate. Sorry I didn't explain properly in the first place. This may not even be a perfect explanation, but I hope it's said well enough to show the issue.)


All you need is delta_time and measure the time passed and do stuff when enough time accumulates. No need for any image speed or room speed or anything.
 
Last edited:
I

Ingemann

Guest
Ah! I must've been a bit too tired to read it all properly or something, because I didn't see where changing room speed was suggested until I read it over again. No no no, I wasn't suggesting that at all.

Sorry @Ingemann, but the suggestion to change room_speed is not good, nor is it necessary. delta_time measures the time taken between the last step and the current step, and that's all that is needed for this.

Furthermore, when you change room speed, you will likely break some of your game's logic, unless you're also multiplying movements and all physics by delta time (but it's utter hell if you try this...).

In explaining, if your room speed is set to 60, and some movement takes place, say 2 pixels per step, this means that thing moves 120 pixels in 1 second (2x60). But if your room speed changes, that thing will move more or less than it's supposed to in a second! This means your game becomes inconsistent and that's really really bad. There are other reasons to avoid changing room speed all the time, but this is the major one.

All you need is delta_time and measure the time passed and do stuff when enough time accumulates. No need for any image speed or room speed or anything.
Yea, still, it works fine for the purpose I made a video of as you can see. It is however not an elegant solution, but something with image_speed might be more convenient for a game of larger proportions, but as you can see in the video, then it changes like 1-2 fps every 200th step or so, so no, it's not hard or chunky actually, but I would of course rather to suggest an implant where you work on speed or image_speed or alike as I also suggested later on. But changing room_speed is not that hard on anything actually as evident by the video. Not when we're talking a single fps here and there.
 
I

Ingemann

Guest
The optimal solution is probably some sort of synthesis of all the things we've been over so far.
 

Micah_DS

Member
Yeah, your idea to change room speed according to time passed should work good in theory, and it will work for the most part (as seen in your video), but the the problem is inaccuracy arises due to decimal precision. Numbers get too small, delta time introduces randomness, and sometimes divisions aren't perfectly clean.

But even if it was only a tiny difference that didn't matter for certain games, the main point is that it should still be avoided because it's entirely unnecessary to do anything other than measure time. No corrective timing needs to be done. Doing anything else is just like making a simple fully-functional machine into a more complex machine that does the exact same thing yet has more gears to spin. So it's not helping and it's only adding in more chance of something to go wrong and with more effort (CPU power).
 
Top