Move Object outside of Update() Function without teleporting [duplicate] - c#

I want to move an object slowly from his original position to a little higher position but this code moves the object instantly to the highest position even when i use really slow speed like 0.0001f. I call LiftObj() inside another code 1 time only and i tell it run until it reaches the liftOffset. What is wrong with this code?
void LiftObj(GameObject Obj) {
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset) {
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp (newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
}

but this code moves the object instantly to the highest position even
when i use really slow speed like 0.0001f.
You are not waiting at-all. The while loop will execute as fast as possible no matter how low your variable is.You should be doing this in a coroutine function then wait with the yield keyword yield return null.
IEnumerator LiftObj(GameObject Obj)
{
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset)
{
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp(newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
yield return null;
}
Debug.Log("Done Moving Up!");
}
then move it with:
StartCoroutine(LiftObj(myGameObject));
Not even sure that would work as expected because you are missing Time.delta so the movement may not be smooth. If all you want to do is move from one position to another overtime, use the sample code below:
IEnumerator LiftObj(GameObject playerToMove, Vector3 toPoint, float byTime)
{
Vector3 fromPoint = playerToMove.transform.position;
float counter = 0;
while (counter < byTime)
{
counter += Time.deltaTime;
playerToMove.transform.position = Vector3.Lerp(fromPoint, toPoint, counter / byTime);
yield return null;
}
}
Move from current myGameObject position to Vector3(0, 5, 0) in 4 seconds:
StartCoroutine(LiftObj(myGameObject, new Vector3(0, 5, 0), 4));

You're using a while loop in an incorrect place here.
While loops continue to execute with in one frame as long as the statement is true.
This means that regardless of how low your magnitude multiplier is (the 0.001f) it will just take more iterations with in the same frame to reach the target.
You should check to see each frame whether the object has reached its goal yet, and if it hasn't apply the transformation.

Related

How can I move the object back to his parent position as child?

IEnumerator ThrowObject(Transform objectToMove, Transform toPosition, float duration)
{
Vector3 originPos = objectToMove.position;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, toPosition.position) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, toPosition.position, time);
yield return null;
}
var naviParent = GameObject.Find("Navi Parent");
StartCoroutine(ThrowBack(objectToMove, naviParent.transform.position , duration));
}
IEnumerator ThrowBack(Transform objectToMove,Vector3 originalPosition , float duration)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, originalPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, time);
//yield return null essentially means "return here every frame"
yield return null;
}
}
When the game start NAVI is child of Navi Parent. Navi Parent position is set to 0,0,0
Also NAVI is at position 0,0,0
Now NAVI is start moving to the target in the method ThrowObject and when NAVI reached the target a new Coroutine start and NAVI is moving back to his parent :
var naviParent = GameObject.Find("Navi Parent");
But when NAVI is reaching to Navi Parent back his position is not at 0,0,0 :
You can see NAVI is not back to the Navi Parent(The player hand) but he is back close to it.
Now NAVI position is : X 0.002350773 Y 0.0007914092 Z 0.002649077
And if I change it on my own the NAVI position to 0,0,0 now NAVI is in the player hand as it should be :
I want that when the NAVI is moving back in the ThrowBack that he will end at 0,0,0 and not that I will have to change it back to 0,0,0
And if I will change NAVI in the end after the second Coroutine ends it will looks like navi change position like jumping or changing position rude and I want it to move smooth back to 0,0,0 in the while loop.
I suspect your issue is with:
float time = Vector3.Distance(currentPos, originalPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, time);
You are evaluating the currentPos every frame which then decreases time, eventually, due to also applying deltaTime, this time gets very small as it approaches the destination.
MoveTowards' third parameter is a maxDistanceDelta, which I assume is what you're trying to use time for, variable name notwithstanding. However, it is intended as a max speed limiter.
Try replacing the above two lones of code with:
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, Speed * Time.deltaTime);
Or, at the very least, let time be no less than some small positive value (eg, time = Mathf.Max(time, 0.001f))
I think it happens because of your counter. Sometimes counter gets a bigger value than the duration and that might effect the calculations. Also using Vectore.Lerp is better. Check this out and see if your problem gets fixed.
private IEnumerator ThrowObject(Transform objectToMove, Transform toPosition, float duration)
{
Vector3 originPos = objectToMove.position;
float counter = 0;
while (objectToMove.position != toPosition.position)
{
counter += Time.deltaTime;
if(counter > duration)
{
counter = duration;
}
objectToMove.position = Vector3.Lerp(originPos, toPosition.position, counter / duration);
yield return null;
}
var naviParent = GameObject.Find("Navi Parent");
StartCoroutine(ThrowBack(objectToMove, naviParent.transform.position, duration));
}
private IEnumerator ThrowBack(Transform objectToMove, Vector3 originalPosition, float duration)
{
Vector3 originPos = objectToMove.position;
float counter = 0;
while (objectToMove.position != originalPosition)
{
counter += Time.deltaTime;
if (counter > duration)
{
counter = duration;
}
objectToMove.position = Vector3.Lerp(originPos, originalPosition, counter / duration);
yield return null;
}
}
My assumption is that you are trying to move the object back to the parent with a set time no matter the distance.
If I'm right the method you should use is this.
float duration = 1f;
float time = 0f;
while (time < 1)
{
time += Time.deltaTime / duration;
object.localPosition = Vector3.Lerp(object.localPosition, targetPosition, time);
yield return null;
}
That will move an object to a new position with a set duration.

How to implement lerp for smooth movement

I want achieve moving object on x axis only with lerp to get smoothly movement.
this is picture what i need
I don't know how i can implement lerp to this code to get smooth movement between these values, it now works but it teleport the player and that is not smooth movement what i want to achieve
This is my working code that teleports player:
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 desiredPos = new Vector3(transform.position.x + 1.5f, transform.position.y, transform.position.z);
transform.position = desiredPos;
}
if (Input.GetMouseButtonDown(1))
{
Vector3 desiredPos = new Vector3(transform.position.x -1.5f, transform.position.y, transform.position.z);
transform.position = desiredPos;
}
}
I want to implement this but i don't understand how to do it .. When i put all code into update the player don't even move.. It only works for me when i copy paste all the code from docs, but how i can move the time from start method to update and always do the same to achieve to get smooth movement for player when going left and right i don't know really please help me guys..
This is the code that works but i don't know how to change it for my example..
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
There are multiple ways. I would not use Translate as this gives you little control here but rather e.g. MoveTowards which makes sure you have no over shooting at the end. Use this for a linear movement with a given moveSpeed:
// set move speed in Units/seconds in the Inspector
public float moveSpeed = 1f;
private Vector3 desiredPos;
private bool isMoving;
private void Update()
{
if (!isMoving && Input.GetMouseButtonDown(0))
{
desiredPos = transform.position + Vector3.right * 1.5f;
isMoving = true;
}
if (!isMoving && Input.GetMouseButtonDown(1))
{
desiredPos = transform.position - Vector3.right * 1.5f;
isMoving = true;
}
if(isMoving)
{
transform.position = Vector3.MoveTowards(transform.position, desiredPos, moveSpeed * Time.deltaTime);
// this == is true if the difference between both
// vectors is smaller than 0.00001
if(transform.position == desiredPos)
{
isMoving = false;
// So in order to eliminate any remaining difference
// make sure to set it to the correct target position
transform.position = desiredPos;
}
}
}
Or as you asked use Vector3.Lerp like e.g.
// a factor between 0 and 1
[Range(0, 1)] public float lerpFactor;
...
transform.position = Vector3.Lerp(transform.position, desiredPos, lerpFactor);
lerpFactor has to be a value between 0 and 1 where in our case 0 would meen the object never moves and 1 it directly jumps to the target position. In other words the closer you set it to 0 the slower it will reach the target, the closer you set it to 1 the faster it will reach the target.
a lot of people do this to get "smooth" movements but what actually happens is e.g. if you set 0.5 for lerpFactor then every frame the object is placed in the middle between current position and target position.
That looks somehow smooth, moves very fast at the beginning and very very slow at the end ... but: It actually never really reaches the target position but just gets very slow.
For your case that is fine since anyway we compare the current and target position using == with a precision of 0.00001. One just has to have in mind how Lerp works.
But with this you won't have any control neither over the move speed nor the duration.
If you want overall more controll (as I do) I would recommend to use a Coroutine (it is not absolutely neccessary and you could do the same in Update as well but in my eyes Coroutines are better to maintain and keep track of).
Than you could also make a smooth eased-in and eased-out movement with an always fixed duration regardless how far the distance is
// set desired move duration in seconds
public float moveDuration = 1;
private bool isMoving;
privtae void Update()
{
if (!isMoving && Input.GetMouseButtonDown(0))
{
StartCoroutine(transform.position + Vector3.right * 1.5f, moveDuration);
}
if (!isMoving && Input.GetMouseButtonDown(1))
{
StartCoroutine(transform.position - Vector3.right * 1.5f, moveDuration);
}
}
private IEnumerator Move(Vector3 targetPosition, float duration)
{
if(isMoving) yield break;
isMoving = true;
var startPosition = transform.position;
var passedTime = 0f;
do
{
// This would move the object with a linear speed
var lerpfactor = passedTime / duration;
// This is a cool trick to simply ease-in and ease-out
// the move speed
var smoothLerpfactor = Mathf.SmoothStep(0, 1, lerpfactor);
transform.position = Vector3.Lerp(startPosition, targetPosition, smoothLerpfactor);
// increase the passedTime by the time
// that passed since the last frame
passedTime += Time.deltaTime;
// Return to the main thread, render this frame and go on
// from here in the next frame
yield return null;
} while (passedTime < duration);
// just like before set the target position just to avoid any
// under shooting
transform.position = targetPosition;
isMoving = false;
}
and you could still extend this example to also take the dtsnace to move into account like e.g.
var actualDuration = duration * Vector3.Distance(startPosition, targetPosition);
and then later everywhere use actualDuration.
Use transform.Translate instead:
public float moveSpeed = 3f;
void Update ()
{
//Moves Left and right along x Axis
transform.Translate(Vector3.right * Time.deltaTime * Input.GetAxis("Horizontal")* moveSpeed);
}

Use MoveTowards with duration instead of speed

I want to move GameObject from position A to B with Vector3.MoveTowards within x seconds and in a coroutine function. I know how to do this with Vector3.Lerp but this time would prefer to do it with Vector3.MoveTowards since both functions behave differently.
With Vector3.Lerp, this is done like this:
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
}
I tried to do the the-same thing with Vector3.MoveTowards, but it's not working properly. The problem is that the move finishes before the x time or duration. Also, it doesn't move smoothly. It jumps to the middle of both positions than to the end of position B.
This is the function that uses Vector3.MoveTowards with the issue above:
IEnumerator MoveTowards(Transform objectToMove, Vector3 toPosition, float duration)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, toPosition) / duration;
objectToMove.position = Vector3.MoveTowards(currentPos, toPosition,
time);
Debug.Log(counter + " / " + duration);
yield return null;
}
}
How do you move GameObject from position A to B with Vector3.MoveTowards within x seconds and in a coroutine function?
Please do not suggest Vector3.Lerp as that's not what I want to use.
EDIT:
Replacing
float time = Vector3.Distance(currentPos, toPosition) / duration;
with
float time = Vector3.Distance(startPos, toPosition) / (duration * 60f);
works but introduces a problem when focus is shifted from Unity to another application. Doing that causes the movement to not finish. Calculating it every frame instead of once before starting the timer seems more reasonable.
MatrixTai's answer fixed both problems.
As I have long time not touching Unity... but I do believe you mess up in the calculation.
First of all, Vector3.MoveTowards(currentPos, toPosition, time) is talking about
Walking from currentPos to toPosition with each frame moving
certain distance time.
Thus using time as name is confusing, but fine lets keep it.
However, you will notice in the statement, time is something move each frame. But Vector3.Distance(currentPos, toPosition) / duration is velocity (m/s), not (m/frame). To make it (m/frame), simply times Time.deltatime which is (s/frame).
Secondly,
When in coroutine, which means the function is iterated each frame. In the line float time = Vector3.Distance(currentPos, toPosition) / duration * Time.deltatime;, what you will notice is that the time keeps on decreasing when going next frame as distance become smaller and smaller.
To be more concrete, we can do some math. Typically this should be done with calculus, but lets simplify it by considering just 2 points. When object postion d = 0, and object postion d ~= 9.9, assume endpoint at 10.
At point 1, the object has time = (10-0)/duration, full speed.
At point 2, the object has time = (10-9.9)/duration, 1/10 of full speed.
Unless you want it to move slower every frame, you cannot hold the value of duration unchanged. As after each frame, you want the velocity to be kept, duration should thus decreases with distance.
To make that physics work, minus the duration for the time passed.
So the final solution is
float time = Vector3.Distance(currentPos, toPosition) / (duration-counter) * Time.deltaTime;
Here is the complete function:
IEnumerator MoveTowards(Transform objectToMove, Vector3 toPosition, float duration)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, toPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, toPosition, time);
Debug.Log(counter + " / " + duration);
yield return null;
}
}

Object moves instantly to new position even with very low speed

I want to move an object slowly from his original position to a little higher position but this code moves the object instantly to the highest position even when i use really slow speed like 0.0001f. I call LiftObj() inside another code 1 time only and i tell it run until it reaches the liftOffset. What is wrong with this code?
void LiftObj(GameObject Obj) {
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset) {
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp (newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
}
but this code moves the object instantly to the highest position even
when i use really slow speed like 0.0001f.
You are not waiting at-all. The while loop will execute as fast as possible no matter how low your variable is.You should be doing this in a coroutine function then wait with the yield keyword yield return null.
IEnumerator LiftObj(GameObject Obj)
{
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset)
{
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp(newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
yield return null;
}
Debug.Log("Done Moving Up!");
}
then move it with:
StartCoroutine(LiftObj(myGameObject));
Not even sure that would work as expected because you are missing Time.delta so the movement may not be smooth. If all you want to do is move from one position to another overtime, use the sample code below:
IEnumerator LiftObj(GameObject playerToMove, Vector3 toPoint, float byTime)
{
Vector3 fromPoint = playerToMove.transform.position;
float counter = 0;
while (counter < byTime)
{
counter += Time.deltaTime;
playerToMove.transform.position = Vector3.Lerp(fromPoint, toPoint, counter / byTime);
yield return null;
}
}
Move from current myGameObject position to Vector3(0, 5, 0) in 4 seconds:
StartCoroutine(LiftObj(myGameObject, new Vector3(0, 5, 0), 4));
You're using a while loop in an incorrect place here.
While loops continue to execute with in one frame as long as the statement is true.
This means that regardless of how low your magnitude multiplier is (the 0.001f) it will just take more iterations with in the same frame to reach the target.
You should check to see each frame whether the object has reached its goal yet, and if it hasn't apply the transformation.

Approximating time and position of an object along a BezierCurve in Unity 5

I currently have an object that I'm moving a long a Bezier curve by sampling a few points along the curve and then just using Unity's MoveTowards to move the object to each point at a constant speed.
Before I do this though, I want to approximate the time that the object will reach the each of the points which I am currently doing by calculating the distance between the points and then just dividing that by the speed of the car.
The values I get are off usually off by one or two decimal places and what I've noticed is that the time values change. Does Unity do some things under the hood that I should take into account and is it possible to get more accurate results, at least to 1 decimal point?
EDIT: Here's some of the code so you can see what I'm trying to do:
This is the internal simulation
float initialTime = Time.time;
Debug.Log("Initial time simulation: " + initialTime);
float distance = 0;
Vector3 curr = transform.position;
Vector3 next;
for (int s = 1; s < steps+1; ++s)
{
float segment = (1.0f - (s/ (float)steps));
next = curve.GetPointAt(segment);
next.y = curr.y; //Only using x and z of bezier curves
distance += (next - curr).magnitude;
curr = next;
float elapsedTime = distance / speed;
output[s - 1] = new KeyValuePair<float, Vector3>(initialTime+elapsedTime, point);
}
This is what actually gets simulated
int counter = 1;
Debug.Log("Initial Time Actual: " + Time.time);
Vector3 toPoint = curve.GetPointAt((float)(steps - counter) / (steps));
toPoint.y = transform.position.y;
transform.LookAt(toPoint);
while (counter <= steps)
{
Vector3 newPos = Vector3.MoveTowards(transform.position, toPoint, speed * Time.deltaTime);
transform.LookAt(newPos);
transform.position = newPos;
if (transform.position == toPoint)
{
Debug.Log("CurrTime: " + Time.time + " " + transform.position);
++counter;
toPoint = curve.GetPointAt((float)(steps - counter) / (steps));
toPoint.y = transform.position.y;
transform.LookAt(toPoint);
}
yield return null;
}
Unless you provide more details I can only guess you are not taking Time.deltaTime into account when calculating the distance you move the car on each frame.
See, Update() function executes on every frame, but the time between frames is not necesarily uniform. Time.deltaTime gives you the time passed between the last frame and the current one.
You have to do something like this, as suggested in the Unity documentation:
private Vector3[] bezierTargets;
private float speed;
private int currentTarget;
// ...
void Update() {
// ...
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, bezierTargets[currentTarget].position, step);
}

Categories

Resources