0
\$\begingroup\$
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveScrollView : MonoBehaviour
{
    float seconds;
    Vector3 begin;
    Vector3 end;
    Vector3 difference;

    public static bool go = false;
    public static bool goBack = false;
    public static bool atOriginPos = false;

    void Start()
    {
        seconds = 0.5f;

        begin = transform.localPosition;
        end = new Vector3(0, -1, 0);
        difference = end - begin;
    }

    void Update()
    {
        if (go)
        {
            if (transform.localPosition.x < end.x)
            {
                var mx = transform.localPosition.x;
                transform.localPosition = new Vector3(mx += seconds * Time.unscaledTime, -1, 0);
                difference = end + transform.localPosition;
            }
        }

        if(goBack)
        {
            if (transform.localPosition.x > begin.x)
            {
                var mx = transform.localPosition.x;
                transform.localPosition = new Vector3(mx -= seconds * Time.unscaledTime, -1, 0);
                difference = end - transform.localPosition;
            }
        }

        if(transform.localPosition == begin && goBack)
        {
            atOriginPos = true;
        }
    }
}

At the Start the begin position is (-540.0, -1.0, 0.0) also the transform.localPosition is at the same position.

Then at this line 39 :

if (transform.localPosition.x > begin.x)

The value of the x of the transform.localPosition is 3.039021 and the x of the begin is -540 So it's getting inside and move the transform.

Then when the transform has finished moving I see on this line :

if (transform.localPosition.x > begin.x)

The transform.localPosition x value is -540.7267 and the x value of the begin is still -540 So now it will not get inside and will not move the transform anymore.

But two wrong things happened :

  1. The transform stopped moving but it didn't move to it's original position only close to it.

  2. Because it didn't move to it's exact original position then the localPosition x and the begin x are never equal so the this will never be true :

    if(transform.localPosition == begin && goBack)

And the flag atOriginPos is never true.

I want the transform to move back to it's original position and then when it's reached the original position set the flag atOriginPos to true.

\$\endgroup\$
1
\$\begingroup\$

You are moving your transforms at a speed that depends on the framerate:

transform.localPosition = new Vector3(mx += seconds * Time.unscaledTime, -1, 0);

That's good because it ensures that the movement speed is steady even if the framerate is inconsistent. However, it means that your transforms are not moving a fixed distance each frame.

Let's say that your movement speed is 60 units/second and the game is trying to render 60 fps per second. Ideally, you render one frame every 1/60th of a second (16ms) and the GameObject moves exactly 1 unit each frame. However, in reality the framerate is going to fluctuate at least a little bit. Maybe one frame takes 21ms to render, and the GameObject moves 1.3125 units that frame. Or the frame only takes 15ms to render and the GameObject moves .9375 units that frame.

Since the movement we add to our position each frame is a floating point value, it's extremely unlikely that the GameObject is ever going to precisely end up at a specific position. For example, say we started at x = 0 and want to end up at x = 5. We might see movement like this:

  1. Move .9375 (total .9375)
  2. Move 1.125 (total 2.0625)
  3. Move 1 (total 3.0625)
  4. Move 1.0625 (total 4.125)
  5. Move .9375 (total 5.0625)

In 5 frames, we moved 5.0625 units and did not land exactly on x = 5. Worse, even if the framerate is 100% consistent, if our movement distance is not a whole number, we might still not exactly end up at a particular position due to floating-point precision error.

There are two solutions easy to this problem:

1. Use Vector3.MoveTowards().

The MoveTowards() function moves towards a destination by a specific amount, and will never pass the destination.

float distanceToMove = speed * Time.unscaledTime;
//We will never move past the end point
transform.localPosition = Vector3.MoveTowards(transform.localPosition, end, distanceToMove);

2. Don't use an equality comparison for positions

Instead of writing if (transform.localPosition == begin), you can do this:

float threshold = .1f; //adjust as needed for more precision
if (Vector3.Distance(transform.localPosition, end) < threshold) {
    atOriginPos = true;
}

Here, instead of checking if our position is exactly equal to some position, we check if it's closer than a certain threshold. This is much safer, but it is not perfect. If our object moved by more than threshold distance in one frame, it might go from one side of the target position to the other without ever being within threshold units.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ For the range comparison in this case, I might recommend if (transform.localPosition.x <= begin.x) - that way we can overshoot any amount and still trigger the condition correctly. \$\endgroup\$ – DMGregory Feb 9 at 22:40
  • 2
    \$\begingroup\$ @DMGregory That also works, but then we need separate cases for moving left and right. If he wants it to exactly end up at end, Vector3.MoveTowards() seems like the best bet. That approach works independently of the movement axis/direction. \$\endgroup\$ – Kevin Feb 9 at 22:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.