Splines in Unity3D

Splines in Unity3D

Blog | December 2014

So, this is the first official post on this blog, let’s make it about mistakes!

Disclosure

I’ve been developing a 2D game based on physics for which I tried to make an editor for my designer. First, I noticed there were no good way to do curves in unity, non useful I mean.

I started researching curves, and Bezier Curve looked good... So I implemented them for Unity.

For full disclosure, I did this pretty fast, and realized that this is pretty useless. To use a bit of math-speak for a second, it’s an N’s derivative curve, meaning that the smoothness of the curve is relative to the number of control points :

  1. The more control points, the smoother the curve
  2. The more control points, the further away from the curve the control points are (and the less influence on the curve they have)
  3. The more control points, the slower the algorithm.

You have been warned.

Actual code

This creates a struct Bspline.
It takes a list<Vector3> or a vector3[] of arbitrary length as input and then gives the function onCurve(t) that returns a Vector3 on the Bezier Curve, t has to be a value between 0 and 1 (0 begin the first control point, and 1 the last control point... as they are THE ONLY control points actually ON THE CURVE).

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

[System.Serializable]
public struct BSpline {
        [SerializeField]
        private List<Vector3> points;
        [SerializeField]
        private bool loopy;

        public bool Loop{
                get{
                        return loopy;
                }
                set{
                        loopy = value;
                        if(value){
                                points = new List<Vector3>(looping(points.ToArray()));
                        }else{
                                points = new List<Vector3>(unlooping(points.ToArray()));
                        }
                }
        }

        public List<Vector3> Points{
                set{
                        if(loopy){
                                points = new List<Vector3>(looping(value.ToArray()));
                        }else{
                                points = value;
                        }
                }
                get{
                        if(loopy){
                                return new List<Vector3>(unlooping(points.ToArray()));
                        }else{
                                return points;
                        }
                }
        }

        public float length{
                get{
                        return lenght(1f);
                }
        }

        public float lenght(float t, float precision = 0.1f){
                precision = Mathf.Clamp(precision, 0.001f, 1f);
                float l = 0f;
                Vector3 previous = points[0];
                for(float tee = precision ; tee <= t; tee+=precision){
                        Vector3 current = onCurve(tee);
                        l += Vector3.Distance(previous, current);
                        previous = current;
                }
                return l;
        }

        public Vector3 onCurve(float t){
                t = Mathf.Clamp01(t);
                Vector3[] Derivation = points.ToArray();
                while(Derivation.Length > 1){
                        Vector3[] nextDerivation = new Vector3[Derivation.Length - 1];
                        for(int i = 0; i < nextDerivation.Length; i++){
                                nextDerivation[i] = Vector3.Lerp(Derivation[i], Derivation[i+1], t);
                        }
                        Derivation = nextDerivation;
                }
                return Derivation[0];
        }

        public void trace(float TracingStep, Color Couleur, float duration = 0){
                Vector3 B = points[0];
                Vector3 BminusOne;

                TracingStep = Mathf.Min(1f,TracingStep);
                TracingStep = Mathf.Max(0.0001f,TracingStep);

                for(float t = 0; t <= 1f; t += TracingStep){
                        BminusOne = B;
                        B = onCurve(t);
                        Debug.DrawLine(BminusOne, B, Couleur, duration);
                }
        }

        public void append(Vector3 endpoint){
                if(loopy){
                        points.RemoveAt(points.Count - 1);
                        points[points.Count - 1] = endpoint;
                        points = new List<Vector3>(looping(points.ToArray()));
                }else{
                        points.Add(endpoint);
                }
        }

        public void append(Vector3[] appendees){
                if(loopy){
                        points.RemoveAt(points.Count - 1);
                        points.AddRange(appendees);
                        points = new List<Vector3>(looping(points.ToArray()));
                }else{
                        points.AddRange(appendees);
                }
        }

        public void appendAt(Vector3 point, int index){
                if(loopy && index >= points.Count - 2 )
                {
                        points.RemoveAt(points.Count - 1);
                        points[points.Count - 1] = point;
                        points = new List<Vector3>(looping(points.ToArray()));
                }else{
                        points.Insert(index, point);
                }
        }

        public BSpline(Vector3 InPt, Vector3 InTgt, Vector3 OutPt, Vector3 OutTgt, bool loop = false){
                this.points = new List<Vector3>(new Vector3[]{InPt, InTgt, OutPt, OutTgt});
                this.loopy = loop;
        }

        public BSpline(Vector3 InPt, Vector3 InTgt, bool loop = false){
                this.points = new List<Vector3>(new Vector3[]{InPt, InTgt});
                this.loopy = loop;
        }

        public BSpline(Vector3[] pointsV, bool loop = false){
                this.loopy = loop;
                this.points = new List<Vector3>(pointsV);
                if(loop){
                        this.points = new List<Vector3>(looping(points.ToArray()));
                }
        }

        public BSpline(List<Vector3> PointsList, bool loop = false){
                this.loopy = loop;
                this.points = PointsList;
                if(loop){
                        this.points = new List<Vector3>(looping(points.ToArray()));
                }
        }

        private Vector3[] looping(Vector3[] inV){
                Vector3[] loopingPoints = new Vector3[inV.Length + 2];
                System.Array.Copy(inV, loopingPoints, inV.Length);
                loopingPoints[loopingPoints.Length - 1] = inV[0];
                loopingPoints[loopingPoints.Length - 2] = inV[0] - inV[1];
                return loopingPoints;
        }

        private Vector3[] unlooping(Vector3[] inV){
                System.Array.Resize(ref inV, inV.Length - 2);
                return inV;
        }

        public void delete(int index){
                points.RemoveAt(index);
        }
}

The loop function is shite but allows you to create a looping curve. I needed that for something at some point... hope you enjoy.

Next time, I’ll give you my code for Catmull-Rom centripedal curves with arbitrary number of points - much, much much more efficient.

Donation

Please, help us keep on doing what we do!
You will get our eternal gratitude!

Downloads

Tags

Networking

Facebook

Youtube

Bandcamp


Se Syndiquer
Contact / Propulsé par SPIP / Squelette par læi