Skip to content

Instantly share code, notes, and snippets.

@Abban
Forked from Eraile/SplineFollower.cs
Created April 13, 2025 14:47
Show Gist options
  • Save Abban/873b2fd78cbf66f524e2db0dc7ed071e to your computer and use it in GitHub Desktop.
Save Abban/873b2fd78cbf66f524e2db0dc7ed071e to your computer and use it in GitHub Desktop.
Spline Follower for Unity, based on Unity's official Spline package
/*
* Spline Follower for Unity, based on Unity's official Spline package
*
* Developed by Pierre Mercy
* Assisted by OpenAI's GPT-4 Language Model (ChatGPT)
*
* This script allows an object to follow a spline path in Unity.
* It includes various features like speed control, time-based motion, loop, ping pong, and ease in and out functionality.
*
* The script was developed using Unity 2022 and the Splines 2.3.0 package.
* However, it should be compatible with other versions with little to no changes.
*
* For any issues, you can reach out to Pierre Mercy at http://www.nomercy-studios.com
* For any issues related to the AI-assisted part of the code, you can refer to https://www.openai.com/
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Splines;
public class SplineFollower : MonoBehaviour
{
[Header("Spline")]
[SerializeField] private SplineContainer splineContainer = null;
public SplineContainer SplineContainer
{
get
{
if (this.splineContainer == null)
this.splineContainer = this.GetComponent<SplineContainer>();
return this.splineContainer;
}
}
public Spline Spline
{
get
{
if (this.SplineContainer == null)
return null;
return this.SplineContainer.Spline;
}
}
[Header("Behaviour - Speed-based")]
public bool useSpeed = true;
public float speed = 1.0f;
[Header("Behaviour - Time-based")]
public float timeToReachEnd = 0.0f; // Time in seconds to reach the end of the spline, if greater than 0 speed is ignored
[Header("Behaviour - Smoothing")]
public bool loop = true;
public bool pingPong = false;
public bool easeInAndOut = false; // Ease in and out functionality
[Header("Progress")]
[SerializeField] private float progress = 0.0f;
[SerializeField] private bool isForward = true;
private void Start()
{
//// Optional, uncomment this or apply it another way so that time is used to determine the speed we need to travel at.
//if (timeToReachEnd > 0)
//{
// speed = Spline.GetLength() / timeToReachEnd;
//}
}
void Update()
{
if (Spline != null)
{
// Velocity
float velocity = Time.deltaTime * speed;
// Motion direction
float motionDirection = (isForward == true ? 1f : -1f);
// Determine progress here
if (useSpeed == true || this.timeToReachEnd < 0f)
{
progress += (motionDirection * (velocity / Spline.GetLength()));
}
else
{
progress += (motionDirection * (Time.deltaTime / this.timeToReachEnd));
}
// Handle looping & pingpong
if (progress > 1.0f || progress < 0.0f)
{
if (loop)
{
progress = Mathf.Repeat(progress, 1.0f);
}
else if (pingPong)
{
isForward = !isForward;
progress = Mathf.Clamp01(progress);
}
else
{
progress = Mathf.Clamp01(progress);
enabled = false; // stop moving if not looping or ping-ponging
}
}
// If easeInAndOut is true, use the Smoothstep function to ease the motion
float easedProgress = easeInAndOut ? Smoothstep(progress) : progress;
// Calculate position
Vector3 estimationPosition = this.SplineContainer.EvaluatePosition(easedProgress);
// Apply position
transform.position = Vector3.Lerp(transform.position, estimationPosition, Time.deltaTime * speed);
}
}
// The Smoothstep function for easing
float Smoothstep(float x)
{
return x * x * (3 - 2 * x);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment