Incorporating touch/mouse input into a rail system with objects transforming on the rail?

Made a cool rail system by following a tutorial where objects transform on the rail automatically from point 1-2-3-4. I’m trying to incorporate touch/mouse input where when you grab the object on the rail, it transform on the rail by dragging it on the rail. I’ve been stuck on this for a month now and can’t figure it out.

Any ideas?

Thanks.

Mover script (moves gameobject on rail):

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

public class Mover : MonoBehaviour {

    public Rail rail;
    public PlayMode mode;

    public float speed = 2.5f;
    public bool isReversed;
    public bool isLooping;
    public bool pingPong;

    private int currentSeg;
    private float transition;
    private bool isCompleted;

    private void Update()
    {
        if (!rail)
        {
            return;
        }
        if (!isCompleted)
        {
            Play(!isReversed);
        }
    }

    private void Play(bool forward = true)
    {
        float m = (rail.nodes[currentSeg + 1].position - rail.nodes [currentSeg].position).magnitude;
        float s = (Time.deltaTime * 1 / m) * speed;            
        transition += (forward) ? s : -s;

        if (transition > 1)
        {
            transition = 0;
            currentSeg++;
            if (currentSeg == rail.nodes.Length - 1)
            {
                if (isLooping)
                {
                    if (pingPong)
                    {
                        transition = 1;
                        currentSeg = rail.nodes.Length - 2;
                        isReversed = !isReversed;
                    }
                    else
                    {
                        currentSeg = 0;
                    }
                }
                else
                {
                    isCompleted = true;
                    return;
                }
            }
        }
        else if (transition < 0)
        {
            transition = 1;
            currentSeg--;
            if (currentSeg == - 1)
            {
                if (isLooping)
                {
                    if (pingPong)
                    {
                        transition = 0;
                        currentSeg = 0;
                        isReversed = !isReversed;
                    }
                    else
                    {
                        currentSeg = rail.nodes.Length - 2;
                    }
                }
                else
                {
                    isCompleted = true;
                    return;
                }
            }
        }

        transform.position = rail.PositionOnRail(currentSeg, transition, mode);
        transform.rotation = rail.Orientation(currentSeg, transition);
    }
}

Rail script (creates rail with Linear or Catmull options and with handles in editor):

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

public enum PlayMode
{
    Linear,
    Catmull,
}

[ExecuteInEditMode]
public class Rail : MonoBehaviour {
    #if UNITY_EDITOR
    public Transform[] nodes;

    private void Start()
    {
        nodes = GetComponentsInChildren<Transform>();
    }

    public Vector3 PositionOnRail(int seg, float ratio, PlayMode mode)
    {
        switch(mode)
        {
            default:
            case PlayMode.Linear:
            return LinearPosition(seg, ratio);
            case PlayMode.Catmull:
            return CatmullPosition(seg, ratio);
        }
    }

    public Vector3 LinearPosition(int seg, float ratio)
    {
        Vector3 p1 = nodes[seg].position;
        Vector3 p2 = nodes[seg + 1].position;

        return Vector3.Lerp(p1, p2, ratio);
    }

    public Vector3 CatmullPosition(int seg, float ratio)
    {
        Vector3 p1, p2, p3, p4;

        if (seg == 0)
        {
            p1 = nodes[seg].position;
            p2 = p1;
            p3 = nodes[seg + 1].position;
            p4 = nodes[seg + 2].position;
        }
        else if (seg == nodes.Length - 2)
        {
            p1 = nodes[seg - 1].position;
            p2 = nodes[seg].position;
            p3 = nodes[seg + 1].position;
            p4 = p3;
        }
        else
        {
            p1 = nodes[seg - 1].position;
            p2 = nodes[seg].position;
            p3 = nodes[seg + 1].position;
            p4 = nodes[seg + 2].position;
        }

        float t2 = ratio * ratio;
        float t3 = t2 * ratio;

        float x =
        0.5f * ((2.0f * p2.x)
        + (-p1.x + p3.x) * ratio
        + (2.0f * p1.x - 5.0f * p2.x + 4 * p3.x - p4.x)
        * t2 + (-p1.x + 3.0f * p2.x - 3.0f * p3.x + p4.x)
        * t3);

        float y =
        0.5f * ((2.0f * p2.y)
        + (-p1.y + p3.y) * ratio
        + (2.0f * p1.y - 5.0f * p2.y + 4 * p3.y - p4.y)
        * t2 + (-p1.y + 3.0f * p2.y - 3.0f * p3.y + p4.y)
        * t3);

        float z =
        0.5f * ((2.0f * p2.z)
        + (-p1.z + p3.z) * ratio
        + (2.0f * p1.z - 5.0f * p2.z + 4 * p3.z - p4.z)
        * t2 + (-p1.z + 3.0f * p2.z - 3.0f * p3.z + p4.z)
        * t3);

        return new Vector3(x, y, z);
    }

    public Quaternion Orientation(int seg, float ratio)
    {
        Quaternion q1 = nodes[seg].rotation;
        Quaternion q2 = nodes[seg + 1].rotation;

        return Quaternion.Lerp(q1, q2, ratio);
    }

    private void OnDrawGizmos()
    {
        for (int i = 0; i < nodes.Length - 1; i++)
        {
        Handles.DrawDottedLine(nodes[i].position, nodes[i + 1].position, 3.0f);
        }
    }  
    #endif
}

MouseDrag script (can click on object with mouse to move and also works with touch):

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

public class MouseDrag : MonoBehaviour {

    float distance = 20;

    void OnMouseDrag()
    {
        Vector3 mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance);
        Vector3 objPosition = Camera.main.ScreenToWorldPoint(mousePosition);
      
        transform.position = objPosition;

    }
}

I made some changes to your Mover and your MouseDrag so that when you grab the target, it disables the mover temporarily. But that is only part of your problem:

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

public class MouseDrag : MonoBehaviour {
 
    float distance = 20;

    public Mover mover;

    float TimerToDisableTheMover;

    void OnMouseDrag()
    {
        Vector3 mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance);
        Vector3 objPosition = Camera.main.ScreenToWorldPoint(mousePosition);
     
        transform.position = objPosition;

        TimerToDisableTheMover = 0.1f;  // how long to disable following
    }

    void Update()
    {
        if (TimerToDisableTheMover > 0)
        {
            TimerToDisableTheMover -= Time.deltaTime;

            mover.SuspendFollowing = true;
        }
        else
        {
            mover.SuspendFollowing = false;
        }
    }
}

and:

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

public class Mover : MonoBehaviour {
 
    public Rail rail;
    public PlayMode mode;
 
    public float speed = 2.5f;
    public bool isReversed;
    public bool isLooping;
    public bool pingPong;
 
    private int currentSeg;
    private float transition;
    private bool isCompleted;

    public bool SuspendFollowing;

    private void Update()
    {
        if (!rail)
        {
            return;
        }
        if (SuspendFollowing)
        {
            return;
        }
        if (!isCompleted)
        {
            Play(!isReversed);
        }
    }
 
    private void Play(bool forward = true)
    {
        float m = (rail.nodes[currentSeg + 1].position - rail.nodes [currentSeg].position).magnitude;
        float s = (Time.deltaTime * 1 / m) * speed;          
        transition += (forward) ? s : -s;
     
        if (transition > 1)
        {
            transition = 0;
            currentSeg++;
            if (currentSeg == rail.nodes.Length - 1)
            {
                if (isLooping)
                {
                    if (pingPong)
                    {
                        transition = 1;
                        currentSeg = rail.nodes.Length - 2;
                        isReversed = !isReversed;
                    }
                    else
                    {
                        currentSeg = 0;
                    }
                }
                else
                {
                    isCompleted = true;
                    return;
                }
            }
        }
        else if (transition < 0)
        {
            transition = 1;
            currentSeg--;
            if (currentSeg == - 1)
            {
                if (isLooping)
                {
                    if (pingPong)
                    {
                        transition = 0;
                        currentSeg = 0;
                        isReversed = !isReversed;
                    }
                    else
                    {
                        currentSeg = rail.nodes.Length - 2;
                    }
                }
                else
                {
                    isCompleted = true;
                    return;
                }
            }
        }
     
        transform.position = rail.PositionOnRail(currentSeg, transition, mode);
        transform.rotation = rail.Orientation(currentSeg, transition);
    }
}

That gets you closer, but the part you’re missing is to look up the closest point on the rail from the transform, which would probably be driven by the MouseDrag class, and would need a reference to the rail as well, but which could reuse your follow code.

Try your hand at the above after the changes listed above. I recommend saving your original scripts in case you don’t want those changes.