Advertisement

Taking a curved path between two points with a constant velocity.

Started by July 21, 2021 10:56 PM
5 comments, last by JoeJ 3 years, 6 months ago

I am making a game for PC that's a clone of the Project Diva games. To put a long story short for those who don't know the game, it's a rhythm game where notes fly from off screen to a receptor placed on screen. I have the notes defined as having a starting position off screen, and a receptor position. To keep in sync, I sync the position to the current position in the song, in beats.

Right now, I determine it will take any note 4 beats to reach the receptor from beginning. I keep track of the beat when the note spawned and the current beat. This basically gives me a lifetime of the note. I normalize it to 0-1.0, but it can go beyond 1.0, if it's lifetime is greater than 4 beats. This allows the player to hit the note a bit late, which is something I definitely want to keep. I plug this normalized lifetime into a simple linear interpolation, and boom. Movement synced to the music.

In code

float progress = (_current_beat - _start_beat) / NOTE_TRAVEL_TIME;
_nx = _start_nx + (_pos.x - _start_nx) * progress;
_ny = _start_ny + (_pos.y - _start_ny) * progress;

It works nice but it's missing something. In Project Diva, the notes don't travel in a straight line, but take a curved path. I guess the best way to show it is with a video:

I've been working through the last few days trying to mimic this behavior. I'd figure the way is to somehow find a parameterized equation that follows a curved path that finds it's way at the receptor at t=1.0. This is way easier said than done, because the speed has to remain constant.

The closest I was able to get is $\left(a\cos(\omega t) + b, a\sin(\omega t) + c)$, which is basically riding the circumference of a circle of radius a, and differentiating the arc length of this function with time gives us a constant $a\omega$, so constant speed. I could somehow calculate the constants that would fit the two points, and adjust and hope it looks good. I am going to try it out, but this feels way too nasty though, and I feel like I'm overdoing it. I'm coming here to see if anyone might have a better solution to this.

krodo said:
The closest I was able to get is $\left(a\cos(\omega t) + b, a\sin(\omega t) + c)$, which is basically riding the circumference of a circle of radius a, and differentiating the arc length of this function with time gives us a constant a\omegaaω, so constant speed.

That's the simplest solution and you're not overdoing.
More complex curves (than an arc) lack an analytical solution, so you'd have to solve for it with iterations. Also it will be less predictable to the player even if done right.
The game in the video also uses such arcs, so you should be fine with it.

Advertisement

The video seems to show the notes always traveling at a constant speed in Y, so all you have to worry about is what to do with X. And the initial X seems to be a given input, sweeping back and forth. So you know at start the ΔT and ΔX.

So, a straight line solution is just Vx = ΔX/ΔT. That's constant velocity.

If you want a curve, try Vx = (ΔX/ΔT) * (1 - K*ΔY). Update this on each frame, so ΔT is the remaining time to hit, ΔY is the remaining distance to hit, ΔX is the remaining X error to hit, and K is a constant you set.

K=0 will give you a straight line. As you increase K, the path will curve, and the moving note will curve to lock onto the target and hit it. K*ΔY should not exceed the Y distance to the top of the screen. Play with values of K until you get what you want.

And a third approach is the quadratic bezier curve: https://en.wikipedia.org/wiki/B%C3%A9zier_curve

A possibly advantage is that your straight line can also be expressed by it (or switch to a linear curve). I am not sure about constant speed, but it seems close enough.

JoeJ said:

That's the simplest solution and you're not overdoing.
More complex curves (than an arc) lack an analytical solution, so you'd have to solve for it with iterations. Also it will be less predictable to the player even if done right.
The game in the video also uses such arcs, so you should be fine with it.

Looks like I have no excuse to not and get my hands dirty with this. Thank you!

Nagle said:

The video seems to show the notes always traveling at a constant speed in Y, so all you have to worry about is what to do with X. And the initial X seems to be a given input, sweeping back and forth. So you know at start the ΔT and ΔX.

So, a straight line solution is just Vx = ΔX/ΔT. That's constant velocity.

If you want a curve, try Vx = (ΔX/ΔT) * (1 - K*ΔY). Update this on each frame, so ΔT is the remaining time to hit, ΔY is the remaining distance to hit, ΔX is the remaining X error to hit, and K is a constant you set.

K=0 will give you a straight line. As you increase K, the path will curve, and the moving note will curve to lock onto the target and hit it. K*ΔY should not exceed the Y distance to the top of the screen. Play with values of K until you get what you want.

I probably picked the worse video to demonstrate the gameplay imo. Most charts have notes fly from all directions.

Alberth said:

And a third approach is the quadratic bezier curve: https://en.wikipedia.org/wiki/B%C3%A9zier_curve

A possibly advantage is that your straight line can also be expressed by it (or switch to a linear curve). I am not sure about constant speed, but it seems close enough.

I've heavily explored bessier curves because it seemed like what I needed. A curve between two points, but working with it, the non constant speed and really no easy way to determine a 3rd point that would guarantee one seemed like I was using the wrong method.

Following a circle arc feels more natural for this. I could use 3 points still to get variety, and get a constant speed.

krodo said:
I've heavily explored bessier curves because it seemed like what I needed. A curve between two points, but working with it, the non constant speed and really no easy way to determine a 3rd point that would guarantee one seemed like I was using the wrong method.

While answering i also thought about quadratic bezier curves (defined from only 3 points). I know they have an analytic solution to compute closest distance, so maybe there also is such thing for constant velocity.
But visually they can not do much more than an arc can do, and if so (e.g. a very narrow U), it's just too hard for the player to predict i thought.
But maybe i'm wrong and it would be interesting to model increasing difficulty. Then you could still combine multiple 3 point beziers to form a S for example. Though, you can do that with arcs too, and it surely is much easier.

This topic is closed to new replies.

Advertisement