问题
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.
回答1:
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
totoPosition
with each frame moving certain distancetime
.
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;
}
}
来源:https://stackoverflow.com/questions/51165278/use-movetowards-with-duration-instead-of-speed