Keep constant speed using rigidbody2d.MovePosition()

Hi, very new in C# and Unity, I’m writing my first script to move my Player along a line drawn with a mouse.
So basically my script allows the player to draw the line with the mouse, once I finish moving the mouse I start to move the player with rigidbody2d.MovePosition().

Here is the issue:

if I move the mouse slowly or fast, the points of my line render are far or close to each other causing the MovePosition to move the player faster or slower.

Is there any way to modify the speed? How could I do this task in order to keep constant speed along the line?

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

public class PlayerMove : MonoBehaviour
{
    private Rigidbody2D rb;
    public LineRenderer lr;


    private Vector2 velocity = new Vector2(1.75f, 1.1f);
    //Not customizable:
    private float timer = 0;
    private int currentWayPoint = 0;
    private int wayIndex;
    private bool move;
    private bool touchStartedOnPlayer;
    public List<GameObject> wayPoints;


    public GameObject wayPoint;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        lr.enabled = false;
        wayIndex = 1;
        move = false;
        touchStartedOnPlayer = false;

    }

    public void OnMouseDown()
    {
        lr.enabled = true;
        touchStartedOnPlayer = true;
        lr.SetPosition(0, transform.position);

    }

  
   
    public void Update()
    {

        if (Input.GetMouseButton(0) && touchStartedOnPlayer)
        {
            Vector2 worldMousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            GameObject newWayPoint = Instantiate(wayPoint, position: worldMousePos, Quaternion.identity);
            wayPoints.Add(newWayPoint);
            lr.positionCount = wayIndex + 1;
            lr.SetPosition(lr.positionCount - 1, worldMousePos);
            timer = 0;
            wayIndex++;
        }

        timer += Time.deltaTime;


        if (Input.GetMouseButtonUp(0))
        {
            touchStartedOnPlayer = false;
            move = true;
        }

       

    }

    private void FixedUpdate()
    {
        if (move && wayPoints.Count > 1)
        {

            rb.MovePosition(wayPoints[currentWayPoint].transform.position);
           
            if (transform.position == wayPoints[currentWayPoint].transform.position)
            {
                currentWayPoint++;
            }
            if (currentWayPoint == wayPoints.Count)
            {
                move = false;
                wayIndex = 1;
                currentWayPoint = 0;
                wayPoints.Clear();
            }
        }
    }
}

8500250--1131794--Oct-09-2022 21-00-01.gif

I just threw this together. Essentially the trick is to calculate the normalised unit-time (0->1) between waypoints. You can then simply use Vector2.Lerp to interpolate between the waypoints.

Add this to a script named “WaypointFollower” on a GameObject with a Kinematic Rigidbody2D on it and hit play. You should be able to change the speed in realtime too and it should always move at that speed. If you set the Kinematic body to interpolate it’ll also move smoothly per-frame too.

Hope it helps.

using System.Collections.Generic;
using UnityEngine;

public class WaypointFollower : MonoBehaviour
{
    public float Speed = 2f;

    private List<Vector2> WayPoints = new List<Vector2>();
    private Rigidbody2D m_Rigidbody;
    private int WaypointTarget;

    private float ElapsedTime;
    private float WaypointTimestep;
    private Vector2 WaypointPositionFrom;
    private Vector2 WaypointPositionTo;
    private bool WaypointMoveActive;

    void Start()
    {
        // Create an unevenly spaced waypoint circle.
        for(var angle = 0f; angle < (Mathf.PI * 2f); angle += Random.Range(Mathf.PI / 100f, Mathf.PI / 10f))
        {
            WayPoints.Add(new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * 4f);
        }

        // Fetch the rigidbody.
        m_Rigidbody = GetComponent<Rigidbody2D>();

        // Move the rigidbody to the first waypoint.
        m_Rigidbody.position = WayPoints[0];
    }

    void FixedUpdate()
    {
        if (!WaypointMoveActive)
        {
            // Calculate way points to move between.
            WaypointPositionFrom = WayPoints[WaypointTarget];
            WaypointTarget = (WaypointTarget+1) % WayPoints.Count;
            WaypointPositionTo = WayPoints[WaypointTarget];

            // Calculate the normalised time-step to move between waypoints.
            var wayPointPath = WaypointPositionTo - WaypointPositionFrom;
            WaypointTimestep = 1f / (wayPointPath.magnitude / Speed);

            // Reset the move state.
            ElapsedTime = 0f;
            WaypointMoveActive = true;
        }

        // Move the body.
        m_Rigidbody.MovePosition(Vector2.Lerp(WaypointPositionFrom, WaypointPositionTo, ElapsedTime));

        // Adjust the waypoint time and status.
        ElapsedTime += WaypointTimestep * Time.deltaTime;
        WaypointMoveActive = ElapsedTime < 1f;
    }

    // Draw the waypoints.
    private void OnDrawGizmos()
    {
        if (m_Rigidbody)
        {
            // Draw the rigidbody.
            Gizmos.color = Color.white;
            Gizmos.DrawSphere(m_Rigidbody.position, 0.15f);
        }

        // Draw the waypoints.
        Gizmos.color = Color.green;
        for(var i = 0; i < WayPoints.Count; i++)
        {
            Gizmos.DrawSphere(WayPoints[i], 0.1f);
            Gizmos.DrawLine(WayPoints[i], WayPoints[(i+1) % WayPoints.Count]);
        }
    }
}
1 Like

Well, I guess it wasn’t urgent then. No reply. :wink: