Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,54 @@ public class BezierPath {
public event System.Action OnModified;
public enum ControlMode { Aligned, Mirrored, Free, Automatic };

#region Fields

[SerializeField, HideInInspector]
List<Vector3> points;
[SerializeField, HideInInspector]
bool isClosed;
[SerializeField, HideInInspector]
PathSpace space;
[SerializeField, HideInInspector]
ControlMode controlMode;
[SerializeField, HideInInspector]
float autoControlLength = .3f;
[SerializeField, HideInInspector]
bool boundsUpToDate;
[SerializeField, HideInInspector]
Bounds bounds;

// Normals settings
[SerializeField, HideInInspector]
List<float> perAnchorNormalsAngle;
[SerializeField, HideInInspector]
float globalNormalsAngle;
[SerializeField, HideInInspector]
bool flipNormals;

#endregion

#region Constructors

/// <summary> Creates a two-anchor path centred around the given centre point </summary>
///<param name="isClosed"> Should the end point connect back to the start point? </param>
///<param name="space"> Determines if the path is in 3d space, or clamped to the xy/xz plane </param>
public BezierPath (Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) {

Vector3 dir = (space == PathSpace.xz) ? Vector3.forward : Vector3.up;
float width = 2;
float controlHeight = .5f;
float controlWidth = 1f;
points = new List<Vector3> {
centre + Vector3.left * width,
centre + Vector3.left * controlWidth + dir * controlHeight,
centre + Vector3.right * controlWidth - dir * controlHeight,
centre + Vector3.right * width
};

perAnchorNormalsAngle = new List<float> () { 0, 0 };

Space = space;
IsClosed = isClosed;
#region Fields

[SerializeField, HideInInspector]
List<Vector3> points;
[SerializeField, HideInInspector]
bool isClosed;
[SerializeField, HideInInspector]
PathSpace space;
[SerializeField, HideInInspector]
ControlMode controlMode;
[SerializeField, HideInInspector]
float autoControlLength = .3f;
[SerializeField, HideInInspector]
bool boundsUpToDate;
[SerializeField, HideInInspector]
Bounds bounds;

// Normals settings
[SerializeField, HideInInspector]
List<float> perAnchorNormalsAngle;
[SerializeField, HideInInspector]
float globalNormalsAngle;
[SerializeField, HideInInspector]
bool flipNormals;

#endregion

#region Constructors

/// <summary> Creates a two-anchor path centred around the given centre point </summary>
///<param name="isClosed"> Should the end point connect back to the start point? </param>
///<param name="space"> Determines if the path is in 3d space, or clamped to the xy/xz plane </param>
public BezierPath (Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) {

Vector3 dir = (space == PathSpace.xz) ? Vector3.forward : Vector3.up;
float width = 2;
float controlHeight = .5f;
float controlWidth = 1f;
points = new List<Vector3> {
centre + Vector3.left * width,
centre + Vector3.left * controlWidth + dir * controlHeight,
centre + Vector3.right * controlWidth - dir * controlHeight,
centre + Vector3.right * width
};

perAnchorNormalsAngle = new List<float> () { 0, 0 };
Space = space;
IsClosed = isClosed;
}

/// <summary> Creates a path from the supplied 3D points </summary>
Expand Down Expand Up @@ -115,6 +114,20 @@ public BezierPath (IEnumerable<Transform> transforms, bool isClosed = false, Pat
public BezierPath (IEnumerable<Vector2> points, PathSpace space = PathSpace.xyz, bool isClosed = false):
this (points.Select (p => new Vector3 (p.x, p.y)), isClosed, space) { }

/// <summary> Cloning constructor, creates a perfect copy of a BezierPath </summary>
public BezierPath(BezierPath path) {
points = new List<Vector3>(path.points);
isClosed = path.isClosed;
space = path.space;
controlMode = path.controlMode;
autoControlLength = path.autoControlLength;
boundsUpToDate = path.boundsUpToDate;
bounds = path.bounds;
perAnchorNormalsAngle = new List<float>(path.perAnchorNormalsAngle);
globalNormalsAngle = path.globalNormalsAngle;
flipNormals = path.flipNormals;
}

#endregion

#region Public methods and accessors
Expand Down Expand Up @@ -483,6 +496,17 @@ public Bounds PathBounds {
}
}

/// Add multiple points at once to our list of points
public void AddPoints(List<Vector3> pointList) {
foreach (Vector3 point in pointList) {
points.Add(point);
}
if (controlMode == ControlMode.Automatic) {
AutoSetAllAffectedControlPoints(points.Count - 1);
}
NotifyPathModified();
}

#endregion

#region Internal methods and accessors
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using UnityEngine;
using System.Collections.Generic;

namespace PathCreation.Examples {

/// <summary>
/// This component stiches multiple BezierPaths smoothly together into a single one.
/// multiple BezierPaths that are part of PathCreators together and creates a single resulting path,
/// which then gets applied to the PathCreator component on this gameobject. Useful for stiching togteher multiple paths into one.
/// </summary>
[RequireComponent(typeof(PathCreator))]
public class BezierPathJoiner : MonoBehaviour {

[SerializeField]
List<PathCreator> pathCreatorsToJoin = new List<PathCreator>();
PathCreator pathCreator = null;

/// <summary> Stiches the BezierPaths of the referenced PathCreators together. </summary>
public void JoinPaths() {
JoinPaths(pathCreatorsToJoin);
}

/// <summary> Stiches the BezierPaths of multiple PathCreators smoothly together and applies the path to the PathCreator on this GameObject.</summary>
/// <param name="pathCreatorList">A list of PathCreators whose BezierPaths will be joined to a single path.</param>
void JoinPaths(List<PathCreator> pathCreatorList) {
if (pathCreator == null)
pathCreator = GetComponent<PathCreator>();

if (pathCreatorList.Count < 2) {
Debug.LogError("There need to be at least two PathCreators referenced to join them together!");
return;
}

// copy the first path - it will be our starting path
PathCreator startingCreator = pathCreatorList[0];
BezierPath joinedPath = new BezierPath(pathCreatorList[0].bezierPath);

// iterate over all subsequent paths to add their points to our first path
for(int i = 1; i < pathCreatorList.Count; i++) {
PathCreator creator = pathCreatorList[i];
if (creator == null)
continue;

BezierPath path = creator.bezierPath;
if (path == null || path.NumPoints == 0)
continue;

// create point list to append to our joinedPath
List<Vector3> points = new List<Vector3>();

// Calculate points halfway between paths
// -- This is needed because bezier paths are missing tangent points at before their startpoint and after their endpoint.
// -- When just appending the points of the next path, the first actual path waypoint would be treated as the tangent point of the last paths endpoint.
// -- Thus, we need to create two tangent points in betweeen, one for the last point of the last path, and one for the first point of the next path.
PathCreator lastCreator = pathCreatorList[i - 1];
Vector3 lastPoint = lastCreator.transform.rotation * lastCreator.bezierPath[lastCreator.bezierPath.NumPoints - 1];
Vector3 lastTangent = lastCreator.transform.rotation * lastCreator.bezierPath[lastCreator.bezierPath.NumPoints - 2];
Vector3 firstPoint = creator.transform.rotation * path[0];
Vector3 firstTangent = creator.transform.rotation * path[1];

// the new tangent points that would otherwise be missing between both paths
Vector3 lastPointNewTangent = lastPoint + (lastPoint - lastTangent);
Vector3 firstPointNewTangent = firstPoint + (firstPoint - firstTangent);

// adjust for worldposition of creators
lastPointNewTangent += lastCreator.transform.position - startingCreator.transform.position;
firstPointNewTangent += creator.transform.position - startingCreator.transform.position;

// add the two missing tangent points in between both paths
points.Add(lastPointNewTangent);
points.Add(firstPointNewTangent);

// add the rest of the points to our path
for (int point = 0; point < path.NumPoints; point++) {
// add point to list and compensate for different transform positions and rotations of creators
points.Add((creator.transform.rotation * path[point]) + creator.transform.position - startingCreator.transform.position);
}
joinedPath.AddPoints(points);
}

// apply the created path to our PathCreator
pathCreator.bezierPath = joinedPath;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using UnityEngine;
using UnityEditor;

namespace PathCreation.Examples {

[CustomEditor(typeof(BezierPathJoiner))]
public class BezierPathJoinerEditor : Editor {

public override void OnInspectorGUI() {
DrawDefaultInspector();

BezierPathJoiner pathJoiner = (BezierPathJoiner)target;
if (GUILayout.Button("Join Paths")) {
pathJoiner.JoinPaths();
}
}
}
}