Smooth transition from point A to point B.

Hi guys, so I have an an object (which I will call A) that I would like to move from its current position to the position of another (moving) object ( which I will call B). The logic I follow to achieve this goes as follows: 1.) I get the distance between the two objects by subtracting the position of A from the position of B. 2.) If the distance between the objects are greater than a certain value, I set the position of A equal to the position of B. Easy enough, and it works fine. However I would like for it to move towards the position of B more smoothly instead of snapping towards it, so I use Vector3.SmoothDamp. This creates a problem though, because as soon as object B is far enough away from object A, object A starts to move to object B constantly instead of just towards the position of B when the margin value is exceeded. I know why this is happening and recognise the flaw in the logic of the code, however I am struggling to find a way to get it to work. If this was slightly confusing, I'll add my code and a video of what I am trying to achieve.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class moveToSphere : MonoBehaviour
{
    public Transform B;
    public float smooth = 1f;
    Vector3 currentVelocity;


    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
       Vector3 dist = B.position - transform.position;

        if (dist.x > 5f)
        {
            transform.position = Vector3.SmoothDamp(transform.position, B.position, ref currentVelocity, smooth);
        }

    }
}

The video I attached is what my current code is doing.

And here is what I am trying to achieve (step 6 of the "tutorial" at 0:36 seconds):


It probably has a really simple solution, I just can't figure it out, I'm still rather new to unity.
Thanks in advance for any help.

9666926--1376861--2024-02-27 10-24-39.gif

Unity has Vector3.MoveTowards that does exactly what you want:

transform.position = Vector3.MoveTowards(transform.position, B.position, speed * time.deltaTime);

Also the distance between B and transform.position is not B.position - transform.position, but √(B.position.x - transform.position.x)² + (B.position.y - transform.position.y)² or simply Vector3.Distance(B.position, transform.position). What you're getting by doing B.position - transform.position is a vector that points from transform.position to B.position, also called direction. The only case your formula works for x is when both transform.position.y and B.position.y are exactly the same, which then the distance formula becomes:
√(B.position.x - transform.position.x)² + (0)² = √(B.position.x - transform.position.x)² = B.position.x - transform.position.x

You could also use Vector3.Lerp, which will give you a smooth deceleration at the end as a side effect, or a tweening library (like DOTween) which is the simplest solution (one line of fire and forget code), with the advantage of offering various easing functions that will make your movement feels more natural.

1 Like

Thanks so much for the quick response, unfortunately the same thing happens. The thing is I understand why it happens: the position of A is constantly being set to the position of B when B is further than 5 units away, however this is not what I want to happen. What I am instead looking for, is that A moves to the position which B is at when it is 5 units away. Essentially always smoothly moving A by 5 units in the direction of B when dist >= 5.
The easing functions you mentioned are quite interesting though :)

You need to keep track of if the object is already moving towards the other. If it is, you wait until it has reached its destination. You could use a bool for this.

Depending on the speed of the first object, you may need a threshold for when to interrupt and change the destination of the second. Or a dynamically adjust the speed during the motion.

For the smooth movement, a good option is to use and AnimationCurve which you can set in the inspector. You could use another animation curve to control the height of the step.

Good article about animation curves here:
https://blog.unity.com/games/animation-curves-the-ultimate-design-lever

Is there any possibility that you could show me how to use bool to do this, I've tried this in the past but still couldn't get it right. Thanks again for all the help so far :)

This is the basic idea:

using UnityEngine;

public class MoveToSphere : MonoBehaviour
{
    [SerializeField] Transform target;
    [SerializeField] AnimationCurve stepEase = new AnimationCurve(new Keyframe(0f, 0f), new Keyframe(1f, 1f));
    [SerializeField] AnimationCurve stepHeightShape = new AnimationCurve(new Keyframe(0f, 0f), new Keyframe(0.5f, 1f), new Keyframe(1f, 0f));
    [SerializeField] float stepHeight = 0.3f;
    [SerializeField] float stepDuration = 0.3f;

    private bool isMoving;
    private Vector3 startPos, targetPos;
    private float stepTime;

    void Update()
    {
        if(isMoving)
        {
            Move();
        }
        else
        {
            CheckIfShouldMove();
        }
    }

    private void CheckIfShouldMove()
    {
        float dist = Vector3.Distance(target.position, transform.position);

        if (dist > 5f)
        {
            isMoving = true;
            startPos = transform.position;
            targetPos = target.position;
            stepTime = 0f;
        }
    }

    private void Move()
    {
        stepTime += Time.deltaTime;
        float progress = stepEase.Evaluate(stepTime / stepDuration);

        Vector3 newPos = Vector3.Lerp(startPos, targetPos, progress);
        newPos.y += stepHeight * stepHeightShape.Evaluate(progress);

        transform.position = newPos;

        if(progress >= 1f)
        {
            isMoving = false;
        }
    }
}
Vector3 currentVelocity;
Vector3 myTarget;

void Update()
{
    transform.position=Vector3.SmoothDamp(transform.position,myTarget,ref currentVelocity,0.5f);
    if (Vector3.Distance(transform.position,B.position)>5 && currentVelocity.magnitude<0.1f)
        myTarget=B.position;
}