Remove delay between lerps - unity3d

I made a simple script that goes to one waypoint and then to the next.
My problem is that it seems to be a delay while going from waypoint1 to waypoint2 and i don't know why:
¿Why is that delay happening and how can i remove it?
using UnityEngine;
using System.Collections;
public class Missile : MonoBehaviour
{
public Vector3 finalTarget;
public Transform forwardObject;
public GameObject impactAreaPrefab;
float smoothingDelay = 0.1f;
bool fired = false;
bool powerPhase = true;
Vector3 currentTarget;
private void OnEnable() {
fire(new Vector3(-25.29f, 0.5f, -10.638f));
}
void fire(Vector3 coords) {
currentTarget = forwardObject.position;
finalTarget = coords;
Instantiate(impactAreaPrefab, finalTarget, Quaternion.identity);
fired = true;
}
void Update() {
if (!fired) {
return;
}
if (powerPhase && transform.position == currentTarget) {
powerPhase = false;
currentTarget = finalTarget;
smoothingDelay = 0.05f;
}
transform.position = Vector3.Lerp(transform.position, currentTarget, Time.deltaTime / smoothingDelay);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, currentTarget, 1, 0.0f)), Time.deltaTime / smoothingDelay);
}
}

That's happening, because you're using lerp not exactly properly. If you want to get linear movement you should cache your first argument (position/rotation on beginning) and provide increasing third parameter. This delay is happening because if your bullet is very close to final position and it's still trying to get there, but your current distance |finalPos - transform.position| is so small that your step Time.deltaTime/smoothingDelay is almost not moving it.
Vector3 startPos;
Vector3 finalPos;
float currentT = 0.0f;
void Update()
{
currentT += Time.deltaTime;
transform.position = Vector3.Lerp(startPos, finalPos, currentT);
}
Checking if Vector3 == Vector3 is also not a good idea. Use pattern from above and check if currentT is larger or equal to 1. If it's true then you're on final position. You get also some control over movement duration by dividing currentT.

So First thing read these post to get better understanding of Lerp function-
https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/
http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8
You should have a better understanding of lerp now.
In summary lerp does a really simple thing. Say u have two values X and Y. For this example let us give them some value, X = 0, Y = 1, Now you want to get a value some percent between them, like u want to get a value which is 50% from X and Y. You can guess the answer is 0.5. The lerp equation for this would be
Mathf.Lerp(0, 1, 0.5f);
So simply- given two values, x and y, Mathf.Lerp returns a value that is t percent between them.
Now to properly use Lerp you need to cache the position before starting the lerp. Most times I use a coroutine to get this effect works pretty well and then u can use animation curve to change the third parameter to create some crazy good effects. For example on using a animation curve just comment i will write it.
For this problem of yours you have two options-
1) Lerp like a pro using Animation curve to manipulate the speed. Remember u can create animation curves in runtime too.
IENumerator Move(Transform toMove, Vector3 end, float duration){
Vector3 startPos = toMove.position;
float elapsed = 0f;
while(elapsed < duration){
elapsed += Time.deltaTime;
toMove.position = Vector3.Lerp(startPos, end, elapsed / duration);//manipulate the last parameter to move the object linearly
yield return null;//basically wait for next frame
}
toMove.position = end;//after lerp ends
}
Now you can instead of duration use speed and then with it you calculate the time required and change the speed to make it faster
float distance = Vector3.Distance(startPos, end);
toMove.position = Vector3.Lerp(startPos, end, elapsed / (distance/(speed * multiplier)));
2) Use Vector3.MoveTowards - This function moves a point to a end point with a given maximum step, requires three paramters, (currentPosition, end, step), u can multiply step with variable to control the speed, both work really good.
Using this is much easier in most cases
Example-
float step = speed * Time.deltaTime;//to make it framerate independent
transform.position = Vector3.MoveTowards(transform.position, end, step * multiplier);
Hope this helps. I am sorry I was unable to format my answer properly, hopefully will get better at answering. Any edits to improve the answer are welcomed :)

I recommend using iTween for smooth movement.
I modified iTween at some point for me to be able to do anything I want. like this:
public static void Rotate (Transform transform, Vector3 target, float transitionTime, Action onEnd = null, bool ignoreTimescale = false, iTween.EaseType ease = iTween.EaseType.easeInOutQuad, float delay = 0)
{
Vector3 from, to;
from = transform.localEulerAngles;
to = target;
Action <object> onUpdateAction = (rotation =>
{
transform.localEulerAngles = (Vector3) rotation;
});
Action <object> onCompleteAction = (data =>
{
if (onEnd != null)
onEnd ();
});
Hashtable hash = new Hashtable ();
hash.Add ("from", from);
hash.Add ("to", to);
hash.Add ("time", transitionTime);
hash.Add ("delay", delay);
hash.Add ("easetype", iTween.EaseType.easeInOutQuad);
hash.Add ("ignoretimescale", ignoreTimescale);
hash.Add ("onupdate", onUpdateAction);
hash.Add ("oncomplete", onCompleteAction);
iTween.ValueTo (transform.gameObject, hash);
}
That gives me full control in a variety of scenarios.
Here is the code if you want to implement it.
https://drive.google.com/open?id=1nLEEYTp-q4Kfh2n3nWQJcMXmPNtVPLLP

Related

Car gets stuck on waypoint

I have an automatic rigidbody car that is supposed to follow a set of waypoints on a mesh collider map but will get caught on any random waypoint and will rotate about that waypoint. Here is my code for the automatic car:
public class FollowThePath : MonoBehaviour
{
public Transform[] target;
public float speed;
public float damping = 6.0f;
public GameObject centerOfMass;
private int current;
void Start()
{
GetComponent<Rigidbody>().centerOfMass = centerOfMass.transform.localPosition;
}
void Update()
{
if (transform.position != target[current].position)
{
Vector3 pos = Vector3.MoveTowards(transform.position, target[current].position, speed * Time.deltaTime);
GetComponent<Rigidbody>().MovePosition(pos);
var rotation = Quaternion.LookRotation(target[current].position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
}
else current = (current + 1) % target.Length;
}
}
I increased the mass, added a center of mass, froze the y axis position and rotation, lowered the center of mass. I noticed that the car gets stuck particularly, but not limited to curves.
Comparing two Vector3s for equality is generally a bad idea when handling moving objects.
Firstly because they use float internally, which always run the risk of being imprecise.
Secondly because Unity knows this and approximates the equality of two vectors internally. The problem is, that you don't have control over the precision being used by unity. When you implement it yourself like the function below it should be easier for you to figure out if that's the problem.
What's most likely the case is, that if (transform.position != target[current].position) never evaluates to false, since transform.position is (0,0,0) and target[current].position is (0.00000000001,0,0) or something. They are almost equal, but not quite equal, at least using the == operator. Then your code in else is never executed.
You can try something like this, where allowedDifference should be in the scope of 0.0001 for ~0.1m precision, or 0.00000001 for ~0.1mm precision:
public bool ApproximatelyEquals(Vector3 a, Vector3 b, float allowedDifference = 0.00000001f){
return Vector3.SqrMagnitude(a - b) < allowedDifference;
}
//Edit:
To clarify, use it like:
if(!ApproximatelyEquals(transform.position, target[current].position), 0.00000001f)

turning towards an object in Unity

Good times. How do I implement the NPC rotation towards the selected object?
public GameObject BufferObject;
public float MoveSpeed = 1f;
void Update()
{
float step = MoveSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, BufferObject.transform.position, step);
}
Here is the script for moving the NPC to the selected object (Buffer Object) and the movement works perfectly, but the implementation of the rotation causes Me difficulties, please advise.
For Unity2D.
Simply get the desired direction
var direction = (BufferObject.transform.position - transform.position).normalized;
and then the desired rotation using Quaternion.LookRotation like e.g.
var targetRotation = Quaternion.LookDirection(direction);
Then either apply it directly if you want it immediately
transform.rotation = targetRotation;
Or if you want it smooth you could use e.g. Quaternion.RotateTowards like
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltatime);
Have in mind though that it might look awkward if the rotation is too slow since your NPC could move back/sidewards already while still not fully looking towards the target ;)
So you might want to wait until
if(Quaternion.Angle(targetRotation, transform.rotation) <= certainThreshold)
{
... your movement code
}
So the answer and the solution from Me, albeit stupid, but working. In order to reflect the sprite, you need to get a variable, either 1 or -1 (For Scale). This code will show the distance from one NPC to the object:
BufferObject.transform.position.x - transform.position.x
And here I get a lot of values as if to the left of the NPC, then -x... , and if to the right, then x... thereby it is possible to determine which side of the object, so also level the value from here (Optional) to 1 or -1 and apply this result to transform.localScale and thereby solve the problem of how to reflect (Rotate the sprite) towards the object. Use it for your health :)
Complete code:
float localPositionAmount = BufferObject.transform.position.x - transform.position.x;
if (localPositionAmount >= 1)
{
gameObject.transform.localScale = new Vector3(1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount <= -1)
{
gameObject.transform.localScale = new Vector3(-1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount == 0)
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
Yes, the code is the simplest and without enumerations and other things, but it works perfectly. I still had to figure it out myself...

Changing the animation values via script in Unity

I've searched different sources but I couldn't find what I directly need. I use a maincamera in my scene and when some conditions are satisfied, I want my camera to animate to the point that I will get from an object which is in the scene. I tried to use Animation Curves but didn't work exactly how I wanted. So I thought of changing the values of the keys of the animation. Is there way that I can change keys and at the same time animation would animate how I want it to animate.
I don't think animations are what you are looking for. I highly suggest you using a Tween asset or writing your own tweening library. Here is an example tweeting code that will move your object:
private void Start()
{
StartCoroutine(Move(5));
}
private IEnumerator Move(float duration)
{
var startPos = gameObject.transform.position;
var targetPos = new Vector3(0, 10, 0);
var timer = 0f;
while (timer < duration)
{
transform.position = Mathf.Lerp(startPos, targetPos, timer / duration);
timer += Time.deltaTime;
yield return null;
}
transform.position = targetPos;
}
This piece of code will take your gameObject and move it to the targetPos in duration seconds. What we do here is essentially called linear interpolation, which means that we change a value linearly from the start value to end value, and timer / duration is called interpolation value. The interpolation value must be between 0 and 1, where inputting 0 will give you the start value and 1 will give you the end value. You can smooth this interpolation value to get smoother movements.

Rotate object to face a direction in a duration in Unity?

So, in my scene I have my main game object, and a second game object which serves as a reference. I want the main game object to rotate so that it will face the same way as the second game object. And this rotating needs to happen in a set time. Let's presume the duration is 3 seconds. If the second game object has a world rotation of 0, 90, 0, the main game object starts to rotate so that after three seconds it's rotation is 0, 90, 0.
So, I have the Transforms of the main game object and the second game object, now I need to calculate the amount to rotate per frame, so that the rotation happens in the correct duration.
I would very much appreciate it if someone could help me! Thank you in advance.
Whenever you want something moving within a specified time I would recommend to not do it in Update but instead use a Coroutine.
It works like a little temporary Update method and is executed each frame until the next yield. this is better to maintain and control than the Update method.
Then do your rotation using Quaternion.Lerp or also Quaternion.Slerp depending a bit on your needs
private IEnumerator RotateOverTime(Transform transformToRotate, Quaternion targetRotation, float duration)
{
var startRotation = transformToRotate.rotation;
var timePassed = 0f;
while(timePassed < duration)
{
var factor = timePassed / duration;
// optional add ease-in and -out
//factor = Mathf.SmoothStep(0, 1, factor);
transformToRotate.rotation = Quaternion.Lerp(startRotation, targetRotation, factor);
// or
//transformToRotate.rotation = Quaternion.Slerp(startRotation, targetRotation, factor);
// increae by the time passed since last frame
timePassed += time.deltaTime;
// important! This tells Unity to interrupt here, render this frame
// and continue from here in the next frame
yield return null;
}
// to be sure to end with exact values set the target rotation fix when done
transformToRotate.rotation = targetRotation;
}
Now in order to start this routine you would call it via StartCoroutine like e.g.
// simply stop any other running routine so you don't get concurrent ones
StopAllCoroutines();
StartCoroutine(transformToRotate, otherTransform.rotation, 3f);
You can use Slerp method for this. So, it should be something like this in Update:
transform.rotation = Quaternion.Slerp(transform.rotation, secondTransform.rotation, rotationSpeed * Time.deltaTime);
https://answers.unity.com/questions/765197/how-do-i-rotate-an-object-over-a-set-time.html
The answer for short:
float totalRotationTime = 5.0f;
float y_degreesPerSecond = go.transform.eulerAngles.y / totalRotationTime;
float x_degs = go.transform.x/ totalRotationTime;
//etc...
go.transform.Rotate(new Vector3(x_degs * Time.deltaTime, degreesPerSecond * Time.deltaTime, 0));
I have updated the answer given by derHugo, with this update you can define the time and the angle that object will rotate in the given time.
private IEnumerator RotateOverTime(float duration)
{
var timePassed = 0f;
while (timePassed < duration)
{
var factor = timePassed / duration;
factor = (Mathf.PI / 2) * (Time.deltaTime / duration);
print("factor: " + factor);
hedefAraYon = Vector3.RotateTowards(transform.forward, hedefYon, factor, 0.0f);
transform.rotation = Quaternion.LookRotation(hedefAraYon, Vector3.back);
// increae by the time passed since last frame
timePassed += Time.deltaTime;
// important! This tells Unity to interrupt here, render this frame
// and continue from here in the next frame
yield return null;
}
// donme takılı kalabiliyor, o yüzden 0,5f olarak ayarladım
if (Quaternion.Angle(transform.rotation, Quaternion.LookRotation(hedefYon, Vector3.back)) <= 0.5f)
{
Vector3 tempRotation = transform.rotation.eulerAngles;
tempRotation.x = Mathf.RoundToInt(tempRotation.x);
tempRotation.y = Mathf.RoundToInt(tempRotation.y);
tempRotation.z = Mathf.RoundToInt(tempRotation.z);
transform.rotation = Quaternion.Euler(tempRotation);
}
hareketDurumu = HAREKET_DURUMU.DURUYOR;
print("Time Passed-Döner OK: " + timePassedOverall);
}

GameObject is vibrating throughout the movement

I have a GameObject that takes position coordinates from a file. This GameObject moves with vibration rather than smoothly. (as if moving back and forth.)
Here is the code that provides the movement:
int counter = 0;
void Update()
{
var maxDistance = speed_* Time.deltaTime;
var oldPosition = transform.position;
var newPosition = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
var actualDistance = Vector3.Distance(newPosition, oldPosition);
var distanceRemainder = maxDistance - actualDistance;
if (distanceRemainder > 0.0001)
{
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder);
counter++;
}
transform.localPosition = newPosition;
}
NOTE: The data read from the file is in the "positions" array (x,y,z coordinates).
When I lower the 300f value in the variable maxDistance, the vibration stops and the motion becomes more fluid. However, Gameobject speed is also slowing down. How can I ensure a fast and smooth movement?
SOLUTION:
While looking for a solution of the problem, I came across the this topic. It helped me learn the source of the problem.
I have observed that the GameObject is not vibrating in Scene view, it was moving smoothly. But the object seemed to be vibrating in GameView. The problem is not the moving object, it's the camera function I write to follow it.
The camera function that was written to follow the object has been updated and the problem has disappeared.
One can see more of the solution by going to Post #13 in the link above.
Thanks to everyone trying to help.
This is caused cause you are using your distanceRemainder as your maxDistanceDelta, which I think is incorrect, if you want a smooth movement, you should multiply it for the Time.deltaTime, try:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder*Time.deltaTime);
Or simply declare speed variable and do:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], speed*Time.deltaTime);
I assume what you want is your object moving with a constant speed to the first position. Once it reaches it, move to the next one.
I would do it in a simple Coroutine which is better to understand and maintain than doing stuff in Update:
private void Start()
{
// todo assign positions
StartCoroutine(RunPositions());
}
private IEnumerator RunPositions()
{
foreach(var position in positions)
{
while(!Mathf.Approximately(Vector3.Distance(transform.position, position), 0))
{
var maxDistance = speed_* Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
// render and continue in the next frame
yield return null;
}
}
}
If you are fine with a precision of 0.00001 you can also simply use
while(transform.position != position)
instead.