I am creating an elevator of which constantly moves from Point A to Point B.
I cannot just use transform.position ( location here * speed here etc)
since I have a player that has a Rigidbody
, and if using that it would flicker my player out whenever I am on the elevator. I also tried parenting the Player whenever its on the elevator (and de-parents it when it jumps etc), that fixes the flickering however it somewhat bugs the player's jumping mechanism.
Last resort is using a Rigidbody
to my elevator and moves it with this code:
private void FixedUpdate()
{
Vector2 l_mypos = new Vector2(transform.position.x, transform.position.y);
Vector2 l_target = new Vector2(_targetPoint.position.x, _targetPoint.position.y);
if (l_mypos != l_target)
{
MoveElevator(l_target);
Debug.Log(l_mypos + " - " + l_target);
}
else
Debug.Log("reached");
}
private void MoveElevator(Vector2 toTarget)
{
Vector2 direction = (toTarget - (Vector2)transform.position).normalized;
_elevatorRB.MovePosition((Vector2)transform.position + direction * _speed * Time.deltaTime);
}
This moves the elevator towards the direction given, however it doesn't reach the "reached" condition. I placed a debug.log there to see both mpos
and target
to see the differences. It ends with 0, 10, 0 - 0, 10, 0
meaning both my target and the elevator's position is already the same. However it doesn't reach the else condition, and the elevator keeps flickering at Point B.
The ==
operator for Vector2
uses an estimation of 0.00001
for equality.
However it is very possible that you overshoot the target in
(Vector2)transform.position + direction * _speed * Time.deltaTime
since your final velocity speed * Time.deltaTime
is certainly greater than 0.00001
. (Except your speed
is smaller than 0.0006
which I doubt.)
The value you see in the Debug.Log
is the result of a Vector3.ToString
which uses human readable rounded values and does not show the actually float
values! From the source code
public override string ToString()
{
return ToString(null, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format)
{
return ToString(format, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (string.IsNullOrEmpty(format)) format = "F1"; // <- !! HERE !!
return UnityString.Format("({0}, {1}, {2})", x.ToString(format, formatProvider), y.ToString(format, formatProvider), z.ToString(format, formatProvider));
}
You should rather use Vector2.MoveTowards
which avoids this overshooting.
The API of Vector3.MoveTowards
actually explains it better then the one of Vector2.MoveTowards
Calculate a position between the points specified by
current
andtarget
, moving no farther than the distance specified bymaxDistanceDelta
.Use the
MoveTowards
member to move an object at thecurrent
position toward thetarget
position. By updating an object’s position each frame using the position calculated by this function, you can move it towards the target smoothly. Control the speed of movement with themaxDistanceDelta
parameter. If thecurrent
position is already closer to thetarget
thanmaxDistanceDelta
, the value returned is equal totarget
; the new position does not overshoot target. To make sure that object speed is independent of frame rate, multiply themaxDistanceDelta
value byTime.deltaTime
private void FixedUpdate()
{
// Vector3 and Vector2 have implicit operators allowing to use
// both types exchangeably.
// In order actively to convert them you can simply typecast between them
var l_mypos = (Vector2) transform.position;
var l_target = (Vector2) _targetPoint.position;
if (l_mypos != l_target)
{
MoveElevator(l_target);
Debug.Log(l_mypos + " - " + l_target);
}
else
{
Debug.Log("reached");
}
}
private void MoveElevator(Vector2 toTarget)
{
var pos = Vector2.MoveTowards(transform.position, toTarget, _speed * Time.deltaTime);
_elevatorRB.MovePosition(pos);
}