Positioning an object on a spline relative to Player proximity

So I’m looking for a way to position an object along a spline/bezier curve at the point of the shortest distance to the player, and update smoothly each frame. Picture:
alt text

I’m doing this because I’m trying to set up some kind of player driven rail camera similar to that of God of War 3 (though less elaborate). Want the camera to follow along the rail smoothly as a player moves through the area.

I’m willing to get any asset pack that might help with this (I know a few have splines setup out of the box). Also, if there is a better way to do what I’m trying to do - I’m all ears :slight_smile:

Thanks!

I’ve done the same in a racing game where I have a bezier path along the center of the road. The best / easiest way I’ve found is to iterate through the path and sub divide the interval several times to get a good result.

I’ve done this even on android / iPad for all 4 wheels and it works great.

edit

Here’s a part of my Bezier class. I also made a Path class which consists of multiple Bezier curves. The whole thing is a bit more complicated, but this class should work for testing single cubic bezier curves. Note that It might not return the exact position since this is a high order formula and there are cases where multiple points matches (up to 3 i guess ;)). The accuracy depends on how extreme the bezier shape looks like and on the iteration count. I used 10 seperate line sections per iteration. It might help to increase the 10 of the first sampling to reduce the chance of picking the wrong section.

public class Bezier
{
    public Vector3 P1;
    public Vector3 P2;
    public Vector3 P3;
    public Vector3 P4;
    public Bezier(Vector3 aP1, Vector3 aP2, Vector3 aP3, Vector3 aP4)
    {
        P1 = aP1;
        P2 = aP2;
        P3 = aP3;
        P4 = aP4;
    }
    public Vector3 Evaluate(float aTime)
    {
        var t = Mathf.Clamp01(aTime);
        return (((-P1 + 3*(P2-P3) + P4)* t + (3*(P1+P3) - 6*P2))* t + 3*(P2-P1))* t + P1;
    }

    // Calculates the best fitting time in the given interval
    private float CPOB(Vector3 aP, float aStart, float aEnd, int aSteps)
    {
        aStart = Mathf.Clamp01(aStart);
        aEnd = Mathf.Clamp01(aEnd);
        float step = (aEnd-aStart) / (float)aSteps;
        float Res = 0;
        float Ref = float.MaxValue;
        for (int i = 0; i < aSteps; i++)
        {
            float t = aStart + step*i;
            float L = (Evaluate(t)-aP).sqrMagnitude;
            if (L < Ref)
            {
                Ref = L;
                Res = t;
            }
        }
        return Res;
    }

    public float ClosestTimeOnBezier(Vector3 aP)
    {
        float t = CPOB(aP, 0, 1, 10);
        float delta = 1.0f / 10.0f;
        for (int i = 0; i < 4; i++)
        {
            t = CPOB(aP, t - delta, t + delta, 10);
            delta /= 9;//10;
        }
        return t;
    }

    public Vector3 ClosestPointOnBezier(Vector3 aP)
    {
        return Evaluate(ClosestTimeOnBezier(aP));
    }
}

Hey Guys!
I just thought I’d post my solution to this problem, this should return practically the exact closest position and it does it with very little overhead (1 to 2 Magnitude calls and 1 Dot-Product).

To do this, we’ll be moving along the curve, by moving in-between intervals, and we’ll consider space between intervals a line. Using simple math (I’ll post under here) we can calculate the closest point to a line. As our camera moves along the path, we’ll calculate the closest point to the 2 surrounding intervals, If the closest point is one of the ends, move the camera in that direction, if it’s not, then thats our closest point.

ClosestPointInfo ClosestPointOnLine(Vector3 a_StartPoint, Vector3 a_EndPoint, Vector3 a_CheckPoint)
    {
        Vector3 m_TempVector1 = a_CheckPoint - a_StartPoint;
        Vector3 m_TempVector2 = (a_EndPoint - a_StartPoint).normalized;

        float m_Distance = (a_StartPoint - a_EndPoint).magnitude;
        float m_DotProduct = Vector3.Dot(m_TempVector2, m_TempVector1); 

        if (m_DotProduct <= 0.0f)
        {
            ClosestPointInfo m_ClosestPoint;
            m_ClosestPoint.m_Position   = a_StartPoint;
            m_ClosestPoint.m_ReturnType = ReturnType.StartPoint;

            return m_ClosestPoint;
        }

        if (m_DotProduct >= m_Distance)
        {
            ClosestPointInfo m_ClosestPoint;
            m_ClosestPoint.m_Position   = a_EndPoint;
            m_ClosestPoint.m_ReturnType = ReturnType.EndPoint;

            return m_ClosestPoint;
        }

        Vector3 m_TempVector3   = m_TempVector2 * m_DotProduct;

        ClosestPointInfo m_ClosestPoint1;
        m_ClosestPoint1.m_Position   = a_StartPoint + m_TempVector3;
        m_ClosestPoint1.m_ReturnType = ReturnType.MiddlePoint;

        return m_ClosestPoint1;
    }

And I use this struct to hold the data

public struct ClosestPointInfo
{
    public ReturnType m_ReturnType;
    public Vector3    m_Position;
}

I’m happy to answer any questions if I’m not super clear. C:

Thanks @Bunny83, I’m currently just going to pursue “magnets” along the path of each tunnel segment that will pull the camera with forces toward the lines between them. It is pretty simple math because the tunnels are linear with x/y locked and they only move on the z axis.