Це відео не доступне.
Перепрошуємо.
Math for Game Developers - Smooth Move(ment) (Linear Interpolation)
Вставка
- Опубліковано 18 сер 2024
- Smooth out character movement by interpolating the velocity vector.
Find the source code here: github.com/BSV...
Question? Leave a comment below, or ask me on Twitter: / vinobs
2020, Still the best tutorial in math for videogames
always has been
Thats the best thing with math, basics dont really develop much..
10 Years later, this was the best tutorial I could find for the maths behind making a custom movement system with braking.
hell yea
You did everyone a solid putting this playlist together. This is the bedrock of GD and a huge help! High praise good sir!
LERP = Linear Interpolation. :)
lol)
LERP = Linearpolation
This is honestly probably the best video I've ever seen on UA-cam. You explain everything super clearly. I wish I could like it more then once :D
TameTheGameGaming even better than liking it twice: you can tell a friend about it :)
+Jorge “Vino” Rodriguez I want to. But I have no frends. are you friend? can I tell you about it?
+TheWieriekenshin I don't have any friends either. We can be friends with each other. What were you going to tell me about?
+Jorge “Vino” Rodriguez I was going to tell a friend about this video as you had suggested earlier. :-)
Yeah. There's lots of things that contribute to circle jumping making you a bit faster and this is probably the biggest one. You add the forward movement to the right movement and you get a vector that's longer than either forward or long, so you can move forward/right faster than only forward or right. It's not the lerping that causes it I would say but just adding vectors without normalizing them properly. Good thinking!
There are just no other tutorials on these topics, espacially of this quality. So thanks for helping out like 100 times now
Thank you very much for the complete series. Now i am understanding how actually the gaming works.
Excellent series.
Straightforward style that is connected to the industry......as opposed to purely academic
This is an underrated series! I subscribed immediately because of it!
Lerp = Linear Interpolation, awesome video dude! Thanks!
If anyone is confused with what's going on with the dt*50
then considered using a variable. For ex: ( float step = dt*50 )
or more simply, dt (time in seconds) and 50 (speed in m/s), therefore dt*50 is (s * m/s = m)
It's just the step value.
If you want to go from 0 -> 5, then the function goes like 0 -> 0.5 -> 1 -> 1.5 ....... -> 5 (in increments of 0.5, so step = 0.5)
so, it's nothing but distance coverage in small chunks.
5 years later on a different account and going back through olde vids, here's another Unity/C# version I came up with (still took me embarrassingly long to figure it out, but oh well :P) :
public class Maths : MonoBehaviour
{
public static float Approach(float goal, float current, float dt)
{
//how far left to we have to go before we get to our goal
float difference = goal - current;
Debug.Log($"Goal: {goal}, Current: {current}, Diff: {difference}, DT: {dt}");
//--- interpolate up or down
//if dt is not enough to take us to our goal
if (difference > dt)
{
Debug.Log($"diff > dt. return {current + dt}");
return current + dt; //return one step further to our goal
}
/*edge case: if goal is lower than current (interpolating in the downward direction / value is getting lower),
then difference will be negative*/
if (difference < -dt)
{
Debug.Log($"diff < -dt. return {current - dt}");
return current - dt;
}
//otherwise, if in range of goal, return goal
return goal;
}
}
public class Game : MonoBehaviour
{
[SerializeField] float maxSpeedAbs;
float minSpeedAbs = 0;
[SerializeField] Rigidbody rbPlayer;
public VectorNew veloGoal;/
public VectorNew veloCurrent;
Vector3 rbVelo;
public void KeyRelease(float minSpeed)
{
if (Input.GetKeyUp("w"))
veloGoal.z = minSpeed;
if (Input.GetKeyUp("s"))
veloGoal.z = minSpeed;
if (Input.GetKeyUp("d"))
veloGoal.x = minSpeed;
if (Input.GetKeyUp("a"))
veloGoal.x = minSpeed;
}
public void KeyPress(float maxSpeed)
{
if (Input.GetKey("w"))
veloGoal.z = maxSpeed;
if (Input.GetKey("s"))
veloGoal.z = -maxSpeed;
if (Input.GetKey("d"))
veloGoal.x = maxSpeed;
if (Input.GetKey("a"))
veloGoal.x = -maxSpeed;
}
public void Update()
{
KeyPress(maxSpeedAbs);
KeyRelease(minSpeedAbs);
}
public void FixedUpdate()
{
veloCurrent.z = Maths.Approach(veloGoal.z, veloCurrent.z, Time.deltaTime * 40);
veloCurrent.x = Maths.Approach(veloGoal.x, veloCurrent.x, Time.deltaTime * 40);
if (veloCurrent.x > 0)
{
Debug.Log($"VeloCurrent.x: {veloCurrent.x}");
Debug.Log($"VeloCurrent.x * dt: {veloCurrent.x * Time.deltaTime}");
}
//(set the VectorNew into a Vector3)
rbVelo = new Vector3(veloCurrent.x, 0, veloCurrent.z);
rbPlayer.position += rbVelo * Time.deltaTime;
}
}
Usually, you have a direction vector (normalized one) which is controlled by the input - while the speed is then used to stretch the direction vector. So simple: (note: .... represents other code)
....
object.setDirectionAccordingToInput( incomingInput );
....
.... Inside Update
object.position(object.position() + object.dir() * object.velocity());
....
where dir, velocity and positon are math-vector-N structs
this mostly prevents stuff like x,z-movement faster than x-movement
to make the project compile in VS 2013
it just require to add
#include
in source files/renderer/shaders.cpp
(line 21 for example)
thank you!
+UTube2K6 Awesome, thanks for that! Worked like a charm.
Thank you so much, you make me feel more comfortable with this algorithms, thanks my friend.
You have a great teaching style. I find these very enjoyable to listen to for a refresher.
Super helpful tutorials. Keep up the good work mate
btw Lerp stands for linear interpolation :)
wonderful :) I understand this now! (implemented in a smooth movement on grid based system) thank you :]
It's the edit and continue feature in Visual Studio, the keyboard shortcut is Alt-F10. :)
It is called lerp because it is Linear intERPolation
Holie shit this is the gold mine I have been searching for. Imma dig this so hard.
you're a great teacher hope to learn more
wow, the way i've been doing it all this time is something like
velocity += (goal-velocity) / 20;
nice to learn that there's a better way to do these things
I think this has a small problem at around 5:40.
Extreme example to illustrate:
If flGoal is 10 and flCurrent is 9.5 and dt = 1
then flDifference is 0.5
So (flDifference > dT) fails
(flDifference < dT) passes, and returns flCurrent - dt = 8.5
Next pass (flDifference > dT), so flCurrent + dt = 9.5
Thus it will oscillate between the two values and never reach the goal.
e.g. with starting values of flGoal = 10, flCurrent = 4.5, and dT = 0.1,
the code oscillates between 9.99999999999998 and 9.89999999999998
if (flDifference < -dt) gives 1.0 < -.5 which fails and so 10 is returned as is correct.
@@JorgeVinoRodriguez Thank you. Typo in my code - I missed the minus sign in front of dT
Ah yes. I knew that but had forgotten it. I've added an annotation, thanks.
it works beautifully! Thanks
thank you very much !!!
"Lerp" comes from "Linear IntERPolation"
Great stuff Jorge! Just subscribed, I'm a novice programmer trying to make some videos for the Free Godot Game engine and your videos will be extremely helpful!
Heck yes! Godot is aliiiivveee!
Good stuff as always, great videos. :) I was able to create this in Unity/C# with a kinematic rigidbody. I don't fully comprehend it (pretty standard though :P), but I got it working all right I think.
*keyboardControls.cs*
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class keyboardControls : MonoBehaviour {
//the velocity goal
public static Vector2 vecVelocityGoal;
//the fastest the player can move
public static float topSpeedX = 3;
public static float topSpeedY = 3;
// Update is called once per frame
void Update()
{
//if moving left
if (Input.GetAxisRaw("Horizontal") < 0)
{
//set the fastest possible speed as negative
vecVelocityGoal.x = topSpeedX * -1;
}
//else if moving right
else if (Input.GetAxisRaw("Horizontal") > 0)
{
//set the fastest possible speed as positive
vecVelocityGoal.x = topSpeedX;
}
//if moving down
if (Input.GetAxisRaw("Vertical") < 0)
{
//set the fastest possible speed as negative (down)
vecVelocityGoal.y = topSpeedY * -1;
}
//if moving up
else if (Input.GetAxisRaw("Vertical") > 0)
{
//set the fastest possible speed as positive (up)
vecVelocityGoal.y = topSpeedY;
}
//if not moving left or right
if (Input.GetAxisRaw("Horizontal") == 0)
{
//set the fastest possible speed as unmoving
vecVelocityGoal.x = 0;
}
//if not moving up or down
if (Input.GetAxisRaw("Vertical") == 0)
{
//set the fastest possible speed as unmoving
vecVelocityGoal.y = 0;
}
}
}
*smoothMovementInterpolation.cs*
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class smoothMovementInterpolation : MonoBehaviour {
keyboardControls kc;
Rigidbody2D rb;
//scale the deltaTime by this (to speed or slow interpolation)
float dtScaler = 16;
// Use this for initialization
void Start () {
rb = GetComponent();
kc = GetComponent();
}
//interpolate up or down to the goal speed
float Approach(float goal, float current, float dt)
{
//how much left do we have to go before we reach our goal
float diff = goal - current;
//if dt is not enough to take us to our goal
if (diff > dt)
//return one step farther to our goal
return current + dt;
//if interpolating in the downward (on the graph) direction (ie, player is slowing down) and the difference is not enough for us to get to our goal
if (diff < -dt)
//return one step farther to our goal, in the downward/slowing direction
return current - dt;
//return goal if in range (previous returns will stop this return if need be
return goal;
}
// Update is called once per frame
void FixedUpdate()
{
//interpolate the amount of speed change (speed up or slow down over time)
float dtScaled = Time.deltaTime * dtScaler;
//store velocity in a variable to access
Vector2 rbVel = rb.velocity;
//apply interpolation method and return x and y velocities and store in vars
rbVel.x = Approach(keyboardControls.vecVelocityGoal.x, rb.velocity.x, dtScaled);
rbVel.y = Approach(keyboardControls.vecVelocityGoal.y, rb.velocity.y, dtScaled);
//change position based on previous position + amount supposed to change * dt (to slow down)
transform.position = new Vector2(transform.position.x + rbVel.x * Time.deltaTime, transform.position.y + rbVel.y * Time.deltaTime);
//change velocity based on the previous velocity + gravity * dt (to slow down)
rb.velocity = new Vector2(rbVel.x + rb.gravityScale * Time.deltaTime, rbVel.y + rb.gravityScale * Time.deltaTime);
Debug.Log(rb.velocity.x);
//update position and velocity
/*transform.position = new Vector2(transform.position.x + rbVel.x * Time.deltaTime, transform.position.y);
rb.velocity = new Vector2(rbVel.x + rb.gravityScale * Time.deltaTime, rb.velocity.y);*/
}
}
Cool
Unhandled exception at 0x00c8db1c in MFGD.exe: 0xC0000005: Access violation writing location 0x00000010.
The program '[2396] MFGD.exe: Native' has exited with code -1073741819 (0xc0000005).
Greatly appreciate for your response sir, again thanks for the wonderful tutorials. Cheers!
Hmmmm well if I understand correctly, you'd need to zero out the acceleration once the character gets to full velocity, and you'd probably have to cap velocity as well. Without looking at the code it's hard to tell. In the later videos there are links to the source code I'm using in GitHub, maybe try messing around with that :)
I was thinking the lerping would play a part because if you normalize the vector that you're setting as the goal state the inbetween state getting there might still be of greater magnitude than the normalized vector because the interpolation is being done on components rather than the vector as a whole
I'm not so sure about that, if I understand you correctly. The set of all normalized vectors makes a unit circle. If you draw a line between any two points on that circle then all points on that line will be inside the circle. So, lerping between normalized vectors won't carry you outside of the unit circle, thus you won't go any faster than normal. But it's certainly confusing to think about and you can see why bugs like this slip through.
Great video series! :D
I'm glad they slip through, infact the reason I wanted to understand better was to make sure any games I create can have such fun mechanics in them! haha, anyway I found a couple of articles that specifically explain circle jumping and strafing in q3a
funender dot com/quake/articles/cj_theory dot html
Man, u are gold! Thankss
Great Channel. Subscribed.
Thank you man, helped a lot
Aaaaaand you just got a new subscriber...
ultra top!! Cool!
acceleration = acceleration * ( 1 - currentVelocity / maximumVelocity) as the character gets closer to maximumVelocity, they will get less acceleration from their acceleration. That's one way at least.
thank you sir
Maybe, you made a little mistake at 3:28
You should add (Goal - Current) * Delta_{t} to Current instead of just Delta_{t}
A formula like this will work both on incrementation and decrementation, and it is also aware of the magnitude of change.
btw. The way you associate Math and Game Dev is awesome.
A very good point. It's an intentional simplification for pedagogical purposes.
Jorge, please tell us how do you live reload the code in C++ without having to rebuild the project?
Thank you very much for the tutorial bro. I tried to run in in VS 2010 express but I am getting this error after pressing F5 :
'MFGD.exe': Loaded 'C:\Windows\System32\ig4icd32.dll', Binary was not built with debug information.
'MFGD.exe': Loaded 'C:\Windows\System32\ig4dev32.dll', Binary was not built with debug information.
'MFGD.exe': Unloaded 'C:\Windows\System32\winmm.dll'
First-chance exception at 0x00c8db1c in MFGD.exe: 0xC0000005: Access violation writing location 0x00000010.
Dang, 6 years and no reply....
Hey
I really like your videos ; you are very different to everyone else because you show us visually how things are done which helps people better understand things. I have a few questions. I downloaded the source and followed instructions but when I hit F5 it appears then exits with code 0x1 , why won't it work properly for me? I'm using VS2010 Ultimate. Did you write all the OpenGL code? Thanks!
If anyone has a problem running and compiling the project on Visual Studio 2017, Put this code in the main.cpp and compile , it should work :)
//_____________
extern "C" {
FILE* _iob = NULL;
}
//_____________
I was wondering is this lerping what makes circle jumping/running allow you to move faster in many games? because you have changing goal values of x,y,z so the combined vector can be stretched past it's speed limit?
Can't thank you enough for putting together all these videos - thoroughly enjoying them. However, I'm trying to get the code to compile on VS 2015 Community, and i'm stuck with a linker error 'libglfw.lib(win32_window.o) unresolved external symbol __imp___iob referenced in function _createWindow'. Any suggestions? Unfortunately, my C++ is a bit rusty. Thanks!
+Michael Ruth I compiled libglfw with VS 2012 and I haven't had time to go make the MFGD project work with 2015, sorry. You could give a shot at compiling glfw 2015 yourself, or try it with 2012?
Argh! Same problem!
Is it possible to have a reimplementation of all of the videos in SDL2. I really loved the lessons :)
btw LERP is short for Linear intERPolation :)
Great videos! I'm interested what framework have you used in your code? And if it is possible to give me some advices what framework to use with C++ for 2D.
If I'm trying to build a mass-based control system, where acceleration is applied to velocity, which is applied to position... how do I stop the character from constantly accelerating? I want to have my character accelerate when I press a key, but that means I can just speed up infinitely. How do I make sure you can't go faster than a certain speed (other than straight-up capping velocity)? I don't need to specify how fast; just make sure the rate of slows when running. Does that make any sense?
I need to create a servo-like movement for a simulated two axis camera gimballing system. Think: pitch and yaw rotations but done smoothly. Right now our simulation is jerky with respect to the movement endpoints. Once it reaches the desired angle in either axis it just stop very abruptly. I want to add some realism by simulating "s-curve" type movement. Is this done similarly except with angular velocities rather than linear? So I could use Slerp function from, say, GLM math library?
MrGameengineer Yes, that's what slerp does - but also check out my videos of quaternions which cover how to do this.
Jorge Rodriguez I will. Thanks Jorge. Really enjoy your videos.
Hi Jorge Rodriguez, how do you display those 3D graphics with C++? Did you use any external libraries/packages/game engine?
5mwa It is rendered in opengl. Try open.gl for a tutorial.
can we get by by simply divide the distance I want to travel by the delta? in other word divide it to multiple steps instead of having to check if I exceeded the goalDistance or not?
+raed tabani Sure! You'll need a slightly different formula though, lerp(a, b, x) = b * x + a * (1-x). I think I'm going to be making a video on that formula as part of the numerical analysis series.
Call it "acceleration"
Ye, I was expecting some serious maths while it's just acceleration
Can you explain how you did the method to keep your sprite within the borders? Do you reverse gravity or something?
ltskullcrusha There's no magic here, when the character is below the floor, the position is snapped to the floor. You can see the code for it here: github.com/BSVino/MathForGameDevelopers/blob/lerping/game/main.cpp#L107
Could you not achieve the same thing by creating an acceleration vector, coding its relation to velocity (vf = vi + dt*a), making a velocity limit, and having the input set the acceleration?
+bboy02701 I suppose you could. I don't think you would get a terribly different result.
Honestly I don't really like this video much anymore, because it doesn't have the x = at + b(1-t) with t in [0, 1] idea of lerp, which is the primary way of thinking about lerp. I may redo it.
Well when you have 173 likes and no dislikes you're more likely than not doing something right.
Wow thanks for the explaination.
I do have one doubt. How come you didnt have to debug again after altering the factor fro 20 to 50. Shouldnt it be built again.
Thanks
I used the "Edit and Continue" feature of Visual Studio which recompiles the application in place and patches the image in memory. I think the shortcut for this is alt-F10.
thanks
What exactly is the DT variable? I understand that it is the change in time, but if your game loop is running at 30/60fps wouldnt the change in time be constant and you always interpolate to the same position? How does it become one?
+Unknown a Some games don't fix their timestep (this example code is one such game) and in such a case, dt may vary from frame to frame. It won't be exactly 1/30 or 1/60 every frame. In this case it's useful to have a variable that represents how long this frame is.
Like I mentioned in my other response, the dt spreads the contribution over the whole frame instead of applying it all every frame. Position is in meters, velocity is in meters per second, and dt is in seconds. So the units of velocity * dt are m/s * s = m, which matches the units of position.
Thanks again for the vids, just want to see if I got something: In this case, delta t is how much speed to add to the current speed of the object, every GL cycle. Is this correct? If so, why is it called delta t? Why not delta s? (Speed)
delta t is delta time. You need to multiply it by the velocity before you add it into the position. For a better explanation watch the jumping and gravity video.
Jorge Rodriguez Okay, so the dt that is being used in the approach function, was already multiplied by the velocity 'before the vid started'? Because in this video, the only use for dt is to add it to the existing speed, every frame. (I have seen your jumping and gravity tutorial, there dt does seem to me associated with time. But in this tutorial, I don't understand the connection between dt and time, apart from the fact that the bigger dt is, the shorter will be the period of time it takes to get from the starting speed to the goal speed).
Aviv Cohn using dt as the 3rd argument will make all of your transitions happen in one second. Jorge Rodriguez I feel like this multiplier should be represented properly somewhere else in the form of an acceleration value or something so we can understand what it's actually doing rather than just "increase that value so it's snappier".
It may help to think of it in terms of a car. When you start driving you are at 0 miles per hour and you have a goal of 60 miles an hour but it takes time to get to that goal. So your current is 0. your goal is 60 at every dt(usually the time between frames.) dt is really small and when he multiplies it by various values later in the video is when you see the changes is the rate of change. The bigger the number he multiplies dt by the faster speed reaches the goal. If you had a car that could go from 0 to 60mph in 10 seconds on average you would at 10mph to your speed every second for 10 seconds.
I think what you're getting at is acceleration. If you go from 0 to 60 miles per hour in 10 seconds then each second you add 6 miles an hour to your speed. So second 0 you're going 0 miles per hour, then after 1 second you're going 6 mph, then 2s 12mph, etc. You're accelerating 6mph per second.
Hi! is there anybody that could teach me how to run this in visual studio 2017? What are the things i need to setup in the properties? Thanks
What software do you use to draw these videos?
Photoshop, Camtasia to record the screen, Visual Studio for the code.
The thing I do not get is why are you increasing box.vecVelocity each call to Update.
+fun Tertain The point is to smoothly transition from slow to fast. Increasing or decreasing the velocity slowly each frame gets us that.
Jorge Rodriguez I see thank you, but i still don't quite get it. I take it as that we use interpolation in order to be able to control the speed of an object while maintaining smoothness, but on the opposite hand where you are not utilizing interpolation you are increasing speed but disregarding smoothness. Am i correct?interpolation allows us to slowly move up to a max speed in return giving us smooth gameplay instead of the "grid-locking" each frame.
+fun Tertain The gradual increase in velocity is simply acceleration. A shorter Δt and a framerate of 60 fps will ensure smoothness.
+fun Tertain Speaking of, it's important that your time steps are independent of the frame rate, so that if for whatever reason the frame rate slows down or lags, the motion will still be correct, but you'll just skip some frames.
I am not getting The calculation for Approach Method .Why you are comparing difference with dt ?Earlier you told that if Current < Goal.It should be with 0 .Also the edge case is not clear :|
If the amount we're going to go this frame (dt) is more than the total remaining distance then we cover all remaining distance and arrive this frame. The comparison checks whether that's true.
Thanks for the reply.Hopefully I understand better now.
Is there any name for this way of movement: pastebin.com/7n2VEGf7? works same as Lerp
2019 o/ !!!
on which engine do you test your code
ryan mccarthy je uses his own engine
Hi. Im almost finish doing my pong project. problem is I want the ball bounce a little bit realistic base on Gamedev.net its about interpolation and angle of reflection. Can you make a video about angle of reflection?
Trax Morth That's a great idea I can't believe I haven't done one yet.
Pls do pls do..
Trax Morth Although in the case of pong it's going to be very simple, just take the negative of the x coordinate. But to do arbitrary walls you have to use some vector math.
Yeah its simple but if I could just understand the reflection i could apply it to anywhere. Uhm are you going to make a vid for that? :D
Yes I've added it to my list. Do you know about how to do vector projections?
what engine does he use?
Oops, something went wrong sorry. Send me an email at bs.vino@gmail.com and I'll help you work to the bottom of it.
Send me an email and I'll try to help you. bs.vino@gmail.com