Movement Code Affected by Fps in Unity 2D - c#

My current player movement code provides 0.32 translation on each key click.
If it is clicked 2 times very quickly, it should be 0.64.
I provide this with coroutine, but it is affected by fps.
How do I ensure that it is not affected by fps?
I know Time.deltaTime, but how do I get exactly 0.32 if I multiply by Time.deltaTime?
My current move code:
public float move = 0.32f; //movement limitation
public float repeat = 4; // I perform 0.32 in 4 parts to be more smooth.
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow) //Exapmle key. I have 4 ways to move
{
StartCoroutine("Left");
}
}
IEnumerator Left()
{
for (int i = 1; i <= repeat; i++)// as I said, I perform 0.32 in 4 parts to be more smooth.
{
transform.position = transform.position + new Vector3(-move / repeat, 0, 0);
yield return null;
}
}

If you wanted to be smooth rather use a fixed speed and do e.g.
// distance to move
public float move = 0.32f;
// speed in Unity units per second
public float speed = 1f;
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)
{
StartCoroutine(Left);
}
}
IEnumerator Left()
{
// Pre-calculate the target position
// This is the huge advantage of IEnumerators -> you can store local variables over multiple frames
var targetPosition = transform.position + Vector3.left * move;
// track how far you have already moved
var moved = 0f;
// Loop until you moved enough
while(moved < move)
{
// the step is now frame-rate independent and moves the object with a fixed value per second
var step = speed * Time.deltaTime;
// every frame move one such step towards the target without overshooting
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);
// keep track of the movement
moved += step;
yield return null;
}
// and to end up with a clean value in the end
transform.position = targetPosition;
}
However
If it is clicked 2 times very quickly, it should be 0.64
this wouldn't be covered. In order to do this I would actually use a completely different approach without Coroutines at all and rather simply use e.g.
// distance to move
public float move = 0.32f;
// speed in Unity units per second
public float speed = 1f;
Vector3 targetPosition;
void Start()
{
targetPosition = transform.position;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)
{
targetPosition += Vector3.left;
}
...
transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
}
so now you can smash your move keys in any direction as much as you like and it will simply sum them all up into the one targetPosition and always keep moving towards it smoothly but with a fixed velocity.
or another alternative would be to interpolate and rather make the object move faster if it is further away from the target but slower the closer it comes
// 5 here is an arbitrary number based on experience -> tweak it according to your needs
public float interpolation = 5f;
...
transform.position = Vector3.Lerp(transform.position, targetPosition, interpolation * Time.deltaTime);

Related

Camera approaching and distancing loop in Unity3D

i can't understand how can i make it so that my camera will approach an object and after reaching a certain distance from it,it will go back to the initial position, and so on.
Here is my code:
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
float speed = 10f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 distance = (transform.position - cameraLook.transform.position);
Debug.Log(distance);
transform.LookAt(cameraLook.transform.position);
if (distance.x > 30)
{
go();
}
if (distance.x < 0)
{
goBack();
}
}
void go()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
void goBack()
{
transform.position-= transform.forward * speed * Time.deltaTime;
}
}
Update is called once every frame.
What currently happens in your code is
Frame 1
You reach a distance of x > 30
you call go once
Frame 2
Now your distance might be < 30 but still > 0 due to the movement last frame
You do nothing at all anymore
or the same with
Frame 1
You reach a distance of < 0
you call go once
Frame 2
Now your distance might be > 0 but still < 30 due to the movement last frame
You do nothing at all anymore
Then also it is very "dangerous" / unreliable to simply check the global X axis difference between your objects. This assumes that your camera is definitely moving along the X axis.
Instead I would rather use something like e.g.
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
[Toolbox("The minimum distance the camera should stay away from the object when moving closer")]
[Min(0f)] public float minDistance = 0f;
[Toolbox("The maximum distance the camera should move away from the object")]
[Min(0f)] public float maxDistance = 30f;
[Toolbox("How fast shall the camera travel. If your target objects moves have in mind that this needs to be bigger then the movement speed of your target object ;)")]
[Min(0f)] public float moveUnitsPerSecond = 10f;
[Toolbox("If true starts moving towards otherwise starts moving away")]
public bool startMovingTowards = true;
private bool isMovingTowards;
private void Start()
{
isMovingTowards = startMovingTowards;
}
void Update()
{
// first look at the target object
transform.LookAt(cameraLook.transform.position);
// get the desired final distance based on the move direction flag
var targetDistance = isMovingTowards ? minDistance : maxDistance;
// get the desired final position based on the desired distance
// simply moving the distance backwards away from the object
var targetPosition = cameraLook.transform.position - transform.forward * targetDistance;
// Move smoothly with the given moveUnitsPerSecond to the desired final position
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond * Time.deltaTime);
// is the desired final position reached (within a precision of 0.00001)
if(transform.position == targetPosition)
{
// then simply invert the move direction
isMovingTowards = !isMovingTowards;
}
}
}

Accelerate rotation from 0 to x angle in x seconds in Unity

This is more of a math question than a coding question. I would like to reach for example an rotation angle of 90 in 1 second while speed is accelerating at constant value. My current version takes 1.4 seconds to reach the desired rotation angle, and it should reach it in 1 second. I believe that the reason for that is that it currently accelerates to speed of 90 in 1 second and not to rotation angle of 90. Since I am not that good in math, I have no idea how I need to adjust the acceleration calculation. I am unable to find any solution to this.
NOTE: I need to adjust the rotation angles manually, I am not able to use any existing functions, like for example transform.Rotate(), since in my complete version the rotation direction can change at any time and the rotation also has deceleration value.
This is a very simplified version of what I have (it only rotates the z axis to one direction and runs once on start):
private float accelerationInSeconds = 1;
private float targetAngle = 90f;
private float speed = 0;
private float axis = 1;
private bool rotate = true;
private float acceleration;
void Start() {
// Calculate acceleration (this calculation should be changed)
acceleration = targetAngle / accelerationInSeconds;
}
void Update() {
if (rotate) {
// Accelerate
speed += axis * (acceleration * Time.deltaTime);
// Calculate next rotation position
Vector3 rotationVector = transform.rotation.eulerAngles;
rotationVector.z += speed * Time.deltaTime;
// Rotate object
transform.rotation = Quaternion.Euler(rotationVector);
// Check if rotation has gone over the target angle
if (rotationVector.z >= targetAngle) {
rotationVector.z = targetAngle;
speed = 0;
rotate = false;
}
}
}
Thanks in advance for anyone who can help!
EDIT: Modified code to be more efficient. I can't use RotateTowards() since in my complete code I need to clamp the rotation between targetAngle and negative targetAngle. Hopefully this code is more efficient and performance friendly. But I still have not found a solution for my original math related question, which was the whole point of this question.
private float accelerationInSeconds = 1;
private float targetAngle = 90f;
private float speed = 0;
private float angle = 0;
private float axis = 1;
private bool rotate = true;
private float acceleration;
void Start() {
// Calculate acceleration (this calculation should be changed)
acceleration = targetAngle / accelerationInSeconds;
}
void Update() {
if (rotate) {
// Accelerate
speed += axis * (acceleration * Time.deltaTime);
// Calculate next rotation position
angle += speed * Time.deltaTime;
// Check if rotation has gone over the target angle
if (angle >= targetAngle) {
angle = targetAngle;
speed = 0;
rotate = false;
}
// Rotate object
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
}
I finally figured it out, thanks to Math section in StackExchange.
So the simple answer is this:
acceleration = 2 * targetAngle / Mathf.Pow(accelerationInSeconds, 2);
As was suggested before I would use a Coroutine. Coroutines are like temporary Update methods and often easier to control and maintain than doing stuff directly in Update.
// Flag to avoid concurrent routines
private bool isRotating;
public void Rotate(float targetAngle, float duration)
{
if(! isRotating) StartCoroutine (RotateRoutine(targetAngle, duration));
}
private IEnumerator RotateRoutine (float targetAngle, float duration)
{
// Just to be sure
if(isRotating) yield break;
// block concurrent routines
isRotating = true;
// Pre-calculate the start and end rotation
var start = transform.rotation;
var end = Quaternion.Euler(0, 0, targetAngle);
var timePassed = 0f;
while(timePassed < duration)
{
// This value will grow linear from 0 to 1 in exactly "duration" seconds
var x = timePassed / duration;
// TODO!
var y = MAGIC;
// Interpolate between the start and end rotation using given factor "y"
transform.rotation = Quaternion.Lerp(start, end, y);
// "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
// Increase by the time passed since last frame
timePassed += Time.deltaTime;
}
// To be sure to end with clean values
transform.rotation = end;
// Allow next routine
isRotating = false;
}
So what do we have to fill in for MAGIC?
Basically it can be any mathematical function that maps given input 0 to 1 to 0 to 1.
There are multiple possibilities.
What you currently ask for is a linear growing speed. That means the resulting movement shall be quadratic! So we already know the Formular
var y = a * x * x + b;
We further know from your code that speed always starts from 0 -> b = 0. And the last step is pretty straight forward:
What value do we have to fill in so y goes from 0 to 1 at the same time that x goes from 0 to 1?
1 = a * 1 * 1 + 0;
=> a = 1!
So in your case it is simply
var y = x * x;
If you also want ease-out you could also simply use Mathf.Smoothstep which automatically adds ease-in and ease-out
var y = Mathf.SmoothStep(0, 1, x);
To make it even easier to control you could use an AnimationCurve and adjust the movement curve exactly to your needs in the Inspector
[SerializeField] private AnimationCurve curve;
The curve editor already comes with some preset curves like e.g. linear, logarithmic, exponential and eased-in/-out grow from 0 to 1!
And then use AnimationCurve.Evaluate to get the value (y) in the routine for a given input time (x).
var y = curve.Evaluate(x);

How to make the character move a certain distance when a button is pressed or tapped

So I'm working on a 2D game right now just to learn different stuff or codes related to making 2D games.
So I've come to a trouble where I got curious on how to make a character move a certain distance like per say 1 block per tap of button. I will give an example here. So take imagine the grid as a land.
Move a certain distance.
This one is the one my character is doing with my current code
and here's my movement code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class keycontrol : MonoBehaviour
{
private float moveSpeed;
private Rigidbody2D rb2d;
private Vector2 change;
private Animator animator;
bool isXMoving;
bool isYMoving;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
}
void Update()
{
change.x = Input.GetAxisRaw("Horizontal");
change.y = Input.GetAxisRaw("Vertical");
if (Mathf.Abs(change.x) > Mathf.Abs(change.y))
{
change.y = 0;
}
else
{
change.x = 0;
}
animator.SetFloat("walk_right", change.x);
animator.SetFloat("walk_left", -change.x);
animator.SetFloat("walk_down", -change.y);
animator.SetFloat("walk_up", change.y);
}
void FixedUpdate()
{
rb2d.MovePosition(rb2d.position + change * moveSpeed * Time.fixedDeltaTime);
if(Input.GetKey("left shift"))
{
moveSpeed = 150;
animator.speed = 1.5f;
}
else
{
moveSpeed = 70;
animator.speed = 1f;
}
}
}
Thanks a lot for help
I think the problem is just your movement speed. You multiply your speed with Time.fixedDeltaTime but that isn't necessary because that value will be constant. Instead, try just setting the speed to 1 and remove the Time.fixedDeltaTime.
Note: Time.deltaTime is used to make the character move a certain amount per frame because the shorter the time between frames, the less the character will move. Time.fixedDeltaTime stays constant because it is the time between each physics frame.
If I'm understanding correctly, the reason it keeps moving is because the input stays greater than zero. You could add a variable to check if the keys are already down to stop the movement.
There are two options here I think,
Make the user click everytime then want to move (press key, lift finger, press key, etc)
Add a timer to make it move every Nth second
For (1) we could just add an variable to say we are moving already.
//Changes IsXMoving and IsYMoving to a single boolean
bool isMoving = false;
///Start, Update, etc
void FixedUpdate()
{
//Check if we are already moving - if we are not, update movement
if (!isMoving)
{
rb2d.MovePosition(rb2d.position + change * moveSpeed * Time.fixedDeltaTime);
if(Input.GetKey("left shift"))
{
moveSpeed = 150;
animator.speed = 1.5f;
}
else
{
moveSpeed = 70;
animator.speed = 1f;
}
}
//Check we are moving by getting the magnitude - if it zero, we are still
isMoving = (change.magnitude != 0);
}
The second option (2) would basically replace this with a basic timer.
//Removed IsXMoving and IsYMoving
//lastMove will store the time we last allowed movement and delay is
//the minimum time between movements in seconds
float lastMove;
float delay = 1; //1 second
void FixedUpdate()
{
//Check if enough time has passed to be allowed to move
if (lastMove + delay < Time.time)
{
rb2d.MovePosition(rb2d.position + change * moveSpeed * Time.fixedDeltaTime);
if(Input.GetKey("left shift"))
{
moveSpeed = 150;
animator.speed = 1.5f;
}
else
{
moveSpeed = 70;
animator.speed = 1f;
}
//Only update the time if we are moving
if (change.magnitude > 0)
lastMove = Time.time;
}
}

How do i add a line which tells the code that i want the platforms to speed up once a loop of the level has been completed/

public class PlatformMoving : MonoBehaviour
{
public float speed = 1.5f; //How fast the platforms are moving
// use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
// Every frame we look at the position of the ground and it is moved to the left
transform.position = transform.position - (Vector3.right * speed * Time.deltaTime);
// If the position of the ground is off the left of the screen
if (transform.position.x <= -13.05f)
{
// Move it to the far right of the screen
transform.position = transform.position + (Vector3.right * 53.3f);
}
}
}
Assuming the loop you're talking about is the lines following the comment // If the position of the ground is off the left of the screen. The increase of the speed would be specified within the if statement that follows it, since that's where the cycle is occuring.
I'd refrain from calling it a loop btw, simply because it makes one search for a for, while or foreach loop when skimming the code, leading to confusion when one isn't there.
I've commented in your code where it would be located.
public class PlatformMoving : MonoBehaviour
{
public float speed = 1.5f; //How fast the platforms are moving
// use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
// Every frame we look at the position of the ground and it is moved to the left
transform.position = transform.position - (Vector3.right * speed * Time.deltaTime);
// If the position of the ground is off the left of the screen
if (transform.position.x <= -13.05f)
{
// Move it to the far right of the screen
transform.position = transform.position + (Vector3.right * 53.3f);
// Increase speed here
// speed += x;
}
}
}

Why the camera is not moving at all if the rotation speed value is for example set to 0.01?

If I set the rotation speed to 5 for example it will rotate facing the next target waypoint and then will move to it. But the camera rotation will be too fast.
Changing the speed to 0.01 make it rotating in a good slowly smooth speed. But then at 0.01 the camera rotate facing the next waypoint but never move to it. It stay on place.
This is the waypoints script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
private GameObject[] waypoints;
private Transform currentWaypoint;
private enum CameraState
{
StartRotating,
Rotating,
Moving,
Waiting
}
private CameraState cameraState;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
void Start()
{
cameraState = CameraState.StartRotating;
}
void Update()
{
switch (cameraState)
{
// This state is used as a trigger to set the camera target and start rotation
case CameraState.StartRotating:
{
// Sanity check in case the waypoint array was set to length == 0 between states
if (waypoints.Length == 0)
break;
// Tell the camera to start rotating
currentWaypoint = waypoints[UnityEngine.Random.Range(0, waypoints.Length)].transform;
lookAtCam.target = currentWaypoint;
lookAtCam.setTime(0.0f);
cameraState = CameraState.Rotating;
break;
}
// This state only needs to detect when the camera has completed rotation to start movement
case CameraState.Rotating:
{
if (lookAtCam.IsRotationFinished)
cameraState = CameraState.Moving;
break;
}
case CameraState.Moving:
{
// Move
transform.position = Vector3.MoveTowards(transform.position, currentWaypoint.position, Time.deltaTime * speed);
// Check for the Waiting state
if (Vector3.Distance(currentWaypoint.position, transform.position) < WPradius)
{
// Set to waiting state
cameraState = CameraState.Waiting;
// Call the coroutine to wait once and not in CameraState.Waiting
// Coroutine will set the next state
StartCoroutine(WaitForTimer(3));
}
break;
}
case CameraState.Waiting:
// Do nothing. Timer has already started
break;
}
}
IEnumerator WaitForTimer(float timer)
{
yield return new WaitForSeconds(timer);
cameraState = CameraState.StartRotating;
}
public void RefreshWaypoints()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
}
}
And the look at camera script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LookAtCamera : MonoBehaviour
{
// Values that will be set in the Inspector
public Transform target;
public float RotationSpeed;
private float timer = 0.0f;
public bool IsRotationFinished
{
get { return timer > 0.99f; }
}
// Update is called once per frame
void Update()
{
if (target != null && timer < 0.99f)
{
// Rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation((target.position - transform.position).normalized),
timer);
timer += Time.deltaTime * RotationSpeed;
}
}
public void setTime(float time)
{
timer = time;
}
}
Problem
Your script basically works! The problem is in
private void Update()
{
if (target != null && timer < 0.99f)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation((target.position - transform.position).normalized), timer);
timer += Time.deltaTime * RotationSpeed;
}
}
there are two issues with that:
You add Time.deltaTime * RotationSpeed so the time it takes to reach the 1 or in your case 0.99 simply takes 1/RotationSpeed = 100 times longer than usual. So your camera will stay in the Rotating state for about 100 seconds - after that it moves just fine!
(This one might be intentional but see below for a Better Solution) Quaternion.Slerp interpolates between the first and second rotation. But you always use the current rotation as startpoint so since the timer never reaches 1 you get a very fast rotation at the beginning but a very slow (in fact never ending) rotation in the end since the distance between the current rotation and the target rotation gets smaller over time.
Quick-Fixes
Those fixes repair your current solution but you should checkout the section Better Solution below ;)
In general for comparing both float values you should rather use Mathf.Approximately and than use the actual target value 1.
if (target != null && !Mathf.Approximately(timer, 1.0f))
{
//...
timer += Time.deltaTime * RotationSpeed;
// clamps the value between 0 and 1
timer = Mathf.Clamp01(timer);
}
and
public bool IsRotationFinished
{
get { return Mathf.Approximately(timer, 1.0f); }
}
You should either use Quaternion.Slerp storing the original rotation and use it as first parameter (than you will see that you need a way bigger RotationSpeed)
private Quaternion lastRotation;
private void Update()
{
if (target != null && !Mathf.Approximately(timer, 1.0f))
{
transform.rotation = Quaternion.Slerp(lastRotation, Quaternion.LookRotation((target.position - transform.position).normalized), timer);
timer += Time.deltaTime * RotationSpeed;
}
else
{
lastRotation = transform.rotation;
}
}
Or instead of Quaternion.Slerp use Quaternion.RotateTowards like
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation((target.position - transform.position).normalized), RotationSpeed * Time.deltaTime);
Better Solution
I would strongly suggest to use the Coroutines for everything instead of handling this kind of stuff in Update. They are way easier to control and makes your code very clean.
Look how your scripts would shrink and you wouldn't need all the properties, fields and comparing floats anymore. You could do most things you are currently getting and setting to wait for a certain thing to happen in only a few single lines.
In case you didn't know: You can actually simply yield return another IEnumerator on order to wait for it to finish:
Waypoints
public class Waypoints : MonoBehaviour
{
private GameObject[] waypoints;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
private Transform currentWaypoint;
private void Start()
{
// maybe refresh here?
//RefreshWaypoints();
StartCoroutine(RunWaypoints());
}
private IEnumerator RunWaypoints()
{
// Sanity check in case the waypoint array has length == 0
if (waypoints.Length == 0)
{
Debug.Log("No Waypoints!", this);
yield break;
}
// this looks dnagerous but as long as you yield somewhere it's fine ;)
while (true)
{
// maybe refresh here?
//RefreshWaypoints();
// Sanity check in case the waypoint array was set to length == 0 between states
if (waypoints.Length == 0)
{
Debug.Log("No Waypoints!", this);
yield break;
}
// first select the next waypoint
// Note that you might get the exact same waypoint again you currently had
// this will throw two errors in Unity:
// - Look rotation viewing vector is zero
// - and transform.position assign attempt for 'Main Camera' is not valid. Input position is { NaN, NaN, NaN }.
//
// so to avoid that rather use this (not optimal) while loop
// ofcourse while is never good but the odds that you will
// always get the same value over a longer time are quite low
//
// in case of doubt you could still add a yield return null
// than your camera just waits some frames longer until it gets a new waypoint
Transform newWaypoint = waypoints[Random.Range(0, waypoints.Length)].transform;
while(newWaypoint == currentWaypoint)
{
newWaypoint = waypoints[Random.Range(0, waypoints.Length)].transform;
}
currentWaypoint = newWaypoint;
// tell camera to rotate and wait until it is finished in one line!
yield return lookAtCam.RotateToTarget(currentWaypoint);
// move and wait until in correct position in one line!
yield return MoveToTarget(currentWaypoint);
//once waypoint reached wait 3 seconds than start over
yield return new WaitForSeconds(3);
}
}
private IEnumerator MoveToTarget(Transform currentWaypoint)
{
var currentPosition = transform.position;
var duration = Vector3.Distance(currentWaypoint.position, transform.position) / speed;
var passedTime = 0.0f;
do
{
// for easing see last section below
var lerpFactor = passedTime / duration;
transform.position = Vector3.Lerp(currentPosition, currentWaypoint.position, lerpFactor);
passedTime += Time.deltaTime;
yield return null;
} while (passedTime <= duration);
// to be sure to have the exact position in the end set it fixed
transform.position = currentWaypoint.position;
}
public void RefreshWaypoints()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
}
}
LookAtCamera
public class LookAtCamera : MonoBehaviour
{
// Values that will be set in the Inspector
public float RotationSpeed;
public IEnumerator RotateToTarget(Transform target)
{
var timePassed = 0f;
var targetDirection = (target.position - transform.position).normalized;
var targetRotation = Quaternion.LookRotation(targetDirection);
var currentRotation = transform.rotation;
var duration = Vector3.Angle(targetDirection, transform.forward) / RotationSpeed;
do
{
// for easing see last section below
var lerpFactor = timePassed / duration;
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, lerpFactor);
timePassed += Time.deltaTime;
yield return null;
} while (timePassed <= duration);
// to be sure you have the corrcet rotation in the end set it fixed
transform.rotation = targetRotation;
}
}
Note
Again instead of Quaternion.Slerp and currentRotation you could also simply use Quaternion.RotateTowards like
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, RotationSpeed * Time.deltaTime);
And for the movement you can also still use Vector3.MoveTowards if you want
while (Vector3.Distance(currentWaypoint.position, transform.position) < WPradius)
{
transform.position = Vector3.MoveTowards(transform.position, currentWaypoint.position, Time.deltaTime * speed);
yield return null;
}
but I would prefer to use the Lerp solutions. Why I suggest to rather use Lerp?
You can very easy controll now whether you want to move/rotate by a certain speed or rather give it fixed duration in which the move/rotation shall be finished regardless how big the differenc is - or even have some additional checks in order to decide for one of those options!
You can ease-in and -out the movement/rotation! See below ;)
Hint for easing Lerp movements
For still maintaining an eased-in and/or eased-out movement and rotation I found this block How to Lerp like a pro very helpfull! (adopted to my examples)
For example, we could “ease out” with sinerp:
var lerpFactor = Mathf.Sin(passedTime / duration * Mathf.PI * 0.5f);
Or we could “ease in” with coserp:
var lerpFactor = 1f - Mathf.Cos(passedTime / duration * Mathf.PI * 0.5f);
We could even create exponential movement:
var lerpFactor = Mathf.Pow(passedTime / duration, 2);
The multiplication property mentioned above is the core concept behind some interpolation methods which ease in and ease out, such as the famous “smoothstep” formula:
var lerpFactor = Mathf.Pow(passedTime / duration, 2) * (3f - 2f * passedTime / duration);
Or my personal favorite, “smootherstep”:
var lerpFactor = Mathf.Pow(passedTime / duration, 3) * (6f * (passedTime / duration) - 15f) + 10f);

Categories