How should I calculate the speed of a Lerp? - c#

I have a Vector2 Lerp statement:
Vector2.Lerp(spawnPos, target, position);
The position variable is a number between 0 and 1, and for all intents and purposes, it increments over time. Think of the way I'm tracking position as a "playhead," like in music or animation, where a cursor moves from left to right as the playback continues.
I want the object to continue moving after it reaches its target, at the same EXACT speed, just infinitely in a direction. I've tried to change the target to the direction multiplied by the distance between the spawn point, but I still can't figure out how to get the correct speed.

A slightly easier answer for you: Instead of using Vector2.Lerp and calculating everything, just use Vector2.LerpUnclamped and throw in any value of t you want!

Thanks to Llama's comment, I was able to calculate the speed of a Lerp.
First, record the start and end time of the interpolation, and whether or not the interpolation has completed (position >= 1). Then, if it hasn't finished, lerp as normal. If it HAS finished, note the end time, calculate the speed and direction in which to move, and increment Transform.position by that value.
bool finished = false;
// Since I'm using AudioSettings.dspTime, I need to use double.
// Most forms of telling time in Unity will use float
double startTime;
double endTime;
void Start()
{
// This is what I am using for my game,
// but you can use any way of telling time that Unity/C# provides.
startTime = AudioSettings.dspTime;
}
void Update()
{
if(!finished)
{
// interpolate
transform.position = Vector2.Lerp(spawnPos, targetPos, position);
if(position >= 1)
{
// Record our end time
endTime = AudioSettings.dspTime;
finished = true
}
} else {
// How long it took to lerp
double lerpTime = endTime - startTime;
// How far we lerped
float distance = Vector2.Distance(spawnPos, targetPos);
// The speed to further move
float speed = distance / (float)lerpTime;
// The direction in which to move
Vector2 direction = (targetPos - spawnPos).normalized;
// Continue moving
transform.position += (Vector3)direction * speed * Time.deltaTime;
}
}

Related

How can I ensure that an object using a cosine equation to determine speed meets its lowest valley at the same time it hits a waypoint?

I'm working on a game for game jam and part of it is to make platforms that move smoothly. They slow down at the ends of their movement before turning around. The platforms simply move side to side or up and down between two waypoints(which are just empty transforms). I have code that uses cosine to determine the speed which works well except it doesn't align with the waypoints, the platforms tend to slow and change direction before ever reaching the waypoints. I need a way to use the distance between the waypoints as a variable in determining how the cosine equation changes speed so that the platforms slow and reverse direction exactly at the waypoints.
Here is what I have so far:
void Side_to_side()
{
if (waypointIndex < horWaypoints.Length)
{
platformSpeed = (1f * (float)Mathf.Cos(2f * (float)Mathf.PI * 1f * totalTime));
Vector3 targetPosition = horWaypoints[waypointIndex].position;
float delta = platformSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, targetPosition, delta);
if (transform.position.x == targetPosition.x && transform.position.y == targetPosition.y)
{
if (waypointIndex + 1 == horWaypoints.Length)
waypointIndex = 0;
else
waypointIndex++;
}
}
else
{
waypointIndex = 0;
}
//Translate platform back and forth between two waypoints
}
As I have said this code moves the platforms in the motions i want but they don't use the waypoints as turn around points. I understand I could do away with the waypoints and just calculate how far I would like each platform to go before turning around individually but that would take time to do it for each platform whereas I'd like to quickly put down waypoint pairs for them to use and the script calculates what the perfect values would be to match the waypoint locations.
If I understand you correctly you want to move an object forth and back between exactly two positions and apply some smoothing to the movement.
I would rather use a combination of Vector2.Lerp with a Mathf.PingPong as factor and you can then apply ease in and out using additionally Mathf.SmoothStep.
This could look like e.g.
public Transform startPoint;
public Transform endPoint;
// Desired duration in seconds to go exactly one loop startPoint -> endPoint -> startPoint
public float duration = 1f;
private void Update ()
{
// This will move forth and back between 0 and 1 within "duration" seconds
var factor = Mathf.PingPong(Time.time / (2f * duration), 1f);
// This adds additional ease-in and -out near to 0 and 1
factor = Mathf.SmoothStep(0, 1, factor);
// This interpolates between the given positions according to the given factor
transform.position = Vector2.Lerp(startPoint, endPoint, factor);
}
you could of course still use cosine if necessary, basically any function that returns a value between 0 and 1. You just have to use the correct multiplier in order to achieve the desired duration in seconds.
Note: Typed on the phone and not 100% sure on the math but I hope the idea gets clear

Unity Vector in If Else Statements

I'm a beginner when it comes to coding so please bear with me. I'm trying to create a boomerang mechanic for a 2d platformer where the boomerang switches directions when it reached at a certain point in which I can set it in the inspector. The way I thought about it was if I subtract the coordinates of the boomerang and the point of destination I will get the distance in between and put it in an if statement and compare to see if the boomerangs position is greater than and equal to the point so it can change its direction. However I receive this error that says
operator cannot be applied to operands to float and vector 2.
below is my code for the boomerang:
public Transform Target;
public float speed; // speed it travels
public Vector2 returnDistance; // The point in where boomerang switches
direction
private bool keepGoing = false; // Update the frame to make it keep going
// Use this for initialization
void Start () {
//rb2d = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
float checkDistance = Vector2.Distance(transform.position,
returnDistance); // check the distance between two points
Debug.DrawLine(Target.position, transform.position, Color.red);
if (Input.GetMouseButton(0) || (keepGoing == true))
{
transform.Translate(Vector2.right * speed * Time.deltaTime); // move
the boomerang to the right
keepGoing = true;
}
if (checkDistance >= returnDistance)
{
transform.Translate(-Vector2.right * speed * Time.deltaTime); // move
the boomerang to the left
keepGoing = true;
}
}
}
So how should I go about this so that I cant take the value I set it up in the inspector for the boomerangs turning point and put it in an if statement?
You should compare checkDistance variable to some treshhold float value (close to zero, but not 0f) — 0.1f or 0.01f etc (you should figure it out by testing).
UPD: I think I should extend my answer to clarify the problem here. You are trying to compare float and [float; float]. Obviously it does not make sense. So you should compare two float values.
If you want boomerang to fly for some fixed distance from who is throwing it, you may campare distance from your character to boomerang with some float value (max distance of throw).
Or if you want boomerang to return after it reaches some point in space you should do as I wrote at the beginning — compare distance from boomerang to that point in space with some float value (close enough to consider it boomerang did hit the target)
You said "I'm trying to create a boomerang mechanic for a 2d platformer where the boomerang switches directions when it reached at a certain point". Then you can use Vector2.sqrMagnitude.
Here is an example of using this vector
public static float SqrMagnitude(Vector2 a)
{
return a.x * a.x + a.y * a.y;
}
So in your case it could be something like this
Vector2 dir = P2 - P1;
float length = dir.magnitude;
dir -= length;
Hope this help.
If ((Checkdistance - returndistance) > 0f)

Using Vector3.slerp

I just want to know if I'm using vector3.slerp correctly as I have some issues.
I have set up a cube in a scene and I want it move smoothly from its current position to one being passed into by the program. Before I was trying to use the slerp I simply had my cube moving from its current point to its new point like this (as I said, the math all works):
cubeFour.transform.localPosition = new Vector3((B2*C1 - B1*C2)/delta,0,(A1*C2 - A2*C1)/delta);
But when I put it in a slerp call, my cube is no longer on the screen. This is how I'm calling it:
Vector3 targetPosition = new Vector3(lerpX, lerpY, lerpZ);
cubeFour.transform.localPosition = Vector3.Slerp(cubeFour.transform.localPosition, targetPosition, Time.deltaTime);
LerpX, LerpY & LerpZ are local variables I've set to contain the X, Y & Z of the first Vector3 I created in my first attempt.
Have I set up the slerp correctly or have I made a kerfuffle somewhere?
Slerp is best for directions, Lerp is best for positions. You should probably be using Lerp. And Time.deltaTime is almost certainly the wrong choice for t. You should be giving it a number that moves from 0 to 1 over the time that you want the cube to move, e.g.
float moveTimeInSeconds = 2;
cubeFour.transform.localPosition = Vector3.Lerp(cubeStartingPosition, targetPosition, (Time.time - startTime) / moveTimeInSeconds);
Or use MoveTowards if it makes more sense to define the speed of motion, and to move towards a target regardless of a starting point, instead of via starting and ending positions and times.
float step = speed * Time.deltaTime;
cubeFour.transform.localPosition = Vector3.MoveTowards(cubeFour.transform.localPosition, targetPosition, step);
Lerp uses a percentage to animate!
This means you must plug in a percentage, not a value near the same thing each frame.
Example Update():
var animationSpeedInSeconds=1;
var animationCounter:float=0;
function Update(){
if(animationCounter>=0 && animationCounter<=1){
animationCounter+=Time.deltaTime/animationSpeedInSeconds;
cubeFour.transform.localPosition = Vector3.MoveTowards(cubeFour.transform.localPosition, targetPosition, animationCounter);
}
}
I hope this helps!

Creating a jump function

I am trying to create a jump method in XNA, but I am facing a lot of problems, it doesn't work for me, I've been trying doing it for like 2 hours long and still no "luck". Can anybody give me a code sample, or at least a direction?
Note: I want the jump to be realistic, using gravity and such.
Thank you!
I erased all my work but here's the latest I tried, I know it shouldn't work for sure, but still..
public void Jump(GameTime gameTime)
{
float currentTime = (float)0.1;
if (position.Y == 200)
{
position.Y += velocity.Y*currentTime -gravity * (float)(Math.Pow(currentTime,2)) / 2;
}
if (position.Y == 200 + jumpHeight)
{
position.Y -= velocity.Y * currentTime - gravity * (float)(Math.Pow(currentTime, 2)) / 2;
}
}
I see that you've learned the equations of motion from your code (which I don't think was in place when I made my comment). However you're a little off, your issue is with your time variable. You're passing in your game time, but then using .1f for your time variable. What you really want for your time variable is the time since you started the jump. Further, position.Y is unlikely to ever be exactly equal to 200 or 200 + jumpHeight. It's a float (I assume), so you can never trust that it'll be a nice round number. If you want to specify an exact maximum jump height, you'll have to perform some equations before and set your velocity.Y accordingly (solve for when velocity equals 0 i.e. the top of your jump).
So, to fix your original code I think something like this will work, albeit totally untested:
public void Jump(GameTime gameTime)
{
if(jumping && position.Y > groundLevelAtPlayer) {
//Get the total time since the jump started
float currentTime = gameTime.totalTime - character.timeofjumpStart;
//gravity should be a negative number, so add it
position.Y += (velocity.Y * currentTime)
+ (gravity * ((float)(Math.Pow(currentTime, 2)) / 2));
}
else
{
jumping = false;
}
}
How exactly do you want to make your player jump? Since you mentioned realism I am assuming that you want your player to jump and move across both the X and Y axis, so that it will move along an arc(parabola if you will).
If that is what you are after, you will need to replicate projectile motion. This previous SO thread contains various methods in which you could implement such motion.
Edit:
You should be able to use the same equations presented. For vertical jumps, the angle will be 90 degrees, or pi/2 if you are working with radians. If you are pressing your direction keys, you will have to use the same equations. The angle at which you want your player to start the jump will have to be selected by yourself. Usually maximum range is obtained at an angle of 45 degrees (pi/4) assuming that the same force is used, so your option is really between 0 and 45 degrees.

How to manage "speed" in a simple racing game?

I'm trying to develop a simple racing 2d game (view top-down) in C#, sdl.net.
Now, I'm trying to manage speed, acceleration and brakes of my car.
My problem is the algorithm.
I have the loop (Events_Tick) executed 50 times per seconds, where the position of my car is processed like the following:
private void Events_Tick(object sender, TickEventArgs e)
{
car.ProcessPosition();
}
and ProcessPosition is something like:
if (throttle)
{
speed += 1;
x += 1;
}
and finally, I draw my sprite on the new X.
The problem is that it is too fast!
So I'm asking you how to maintain 50 FPS (frames per second) and move my sprite (the car) only N pixels per second (based on its speed).
Thank you in advance! Regards!
First of all, because you have 2d not 1d game, you'll need your speed to be
class Vector
{
double x;
double y;
}
with this class, you should maintain position and speed.
Since you are 2d top-down, you'll have to implement some .RotateLeft() and .RotateRight() on the speed vector.
Rotation will be implemented like:
x' = cos(a) * x - sin(a) * y
y' = sin(a) * x + cos(a) * y
And you'll have to implement your .Move() method as follows:
void Move(Vector v)
{
x+=v.x;
y+=v.y;
}
EDIT: please ask away if clarification is needed, or some more in-depth discussion.
Also, you can use timer here, but try to calculate time spent from last timer event, and then multiply speed with that value when adding to the current position, you will get more accurate position that way.
a in sin() and cos() will be an angle in radians, and you will probably want degrees here.
Here is something to get you going regarding degrees and radians.
Use a double value for speed and x, then you can use Math.Round(x) for drawing.
if (throttle == true) {
speed+=0.05;
}
x+=speed; //move car even without acceleration
Follow Henk's advice to make your program more flexible and to be able to show the user something like 50mph instead of 0.05px per 0.02s:
double speedInMph = 50;
double screenSizeInMiles = .1;
double screenSizeInPx = 500;
if(throttle)
{
speedInMph += 1;
}
double speedInMpms = 50/(60*60*1000);//Miles per millisecond
double xInMiles = speedInMpms * 50;
double pxPerMile = screenSizeInPx/screenSizeInMiles;
x+= xInMiles * pxPerMile;
Acceleration will be constant, depending on user input (on a simple level the car is either accelerating, deccelerating or maintaining speed).
Speed will change each frame depending on that acceleration. Each frame, update the car's speed based on both the acceleration and the proportion of a second the frame covers (i.e. 1/50th of a second in your case).
So, if your car is accelerating at 10 units distance per second, in your example the speed will increase by 1/5th of a unit distance per frame.
Just thought it was worth elaborating a little on the other answers, instead of just posting more code :-)
You should add velocity according to current acceleration and handle just acceleration with your controls, limiting speed with a cap and increasing it according to time elapsed between two game loop iterations.
float accel = 10.0f; // units per squared second
float maxSpeed = 50.0f // units per second
float curSpeed = 0.0f;
//game init
while (1) {
long timeBefore = time();
// game loop
long timeElapsed = time() - timeBefore;
if (throttle) {
curSpeed += accel*(timeElapsed / 1000.0);
if (curSpeed > maxSpeed)
curSpeed = maxSpeed;
}
}
In this way you take into account the fact that a longer frame will increase your car speed more than a quicker ore, keeping it consistent with time.
This model implies a constant acceleration while you could hypotetically want a dynamic one, in that case you just move the update to the acceleration (taking into account the frame duration as for speed) and cap it to not go over a fixed threshold.
Henk is right. You will want to create a
Thread
and call its
Sleep(Int32)
method each time in your loop to slow the animation down, where the int parameter is the pause time in milliseconds.
In your case, you will want to move the car N/50 pixels each time looping, and sleep the thread for 20 milliseconds each time.

Categories