I'm having a few issues with (Catmull)Splines that i need help with.
Hello i am trying to implement 3 things into my spline that i need help with.
(1) As you can see in Pic1 there is a missing cube at the Control Points along the spline. How do i fix the missing cubes on the spline?
-Pic1
(2) How do i change the Segment count as you can see in Pic2(SEG1)there is 9 yellow dots in between two points. How do i Increase/Decrease the amount of yellow dots?
(3) How do i have the Cubes follow the Tangent points after instantiating them(See Pic2(SEG2) to see what i mean)? -Pic2
And here is the code so you know what i have done.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Catmull : MonoBehaviour {
public List<Transform> controlPointsList = new List<Transform>();
public bool isLooping = true;
public GameObject obj;
Vector3 p0;
Vector3 p1;
Vector3 p2;
Vector3 p3;
void Start()
{
for(int i = 0; i < controlPointsList.Count; i++)
Object(i);
}
void OnDrawGizmos()
{
for (int i = 0; i < controlPointsList.Count; i++)
{
//Cant draw between the endpoints
//Neither do we need to draw from the second to the last endpoint
//...if we are not making a looping line
if ((i == 0 || i == controlPointsList.Count - 2 || i == controlPointsList.Count - 1) && !isLooping)
{
continue;
}
DisplayCatmullRomSpline(i);
}
Gizmos.color = Color.white;
//Draw a sphere at each control point
for (int i = 0; i < controlPointsList.Count; i++)
{
Gizmos.DrawWireSphere(controlPointsList[i].position, 0.3f);
}
}
Vector3 ReturnCatmullRom(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
Vector3 a = 0.5f * (2f * p1);
Vector3 b = 0.5f * (p2 - p0);
Vector3 c = 0.5f * (2f * p0 - 5f * p1 + 4f * p2 - p3);
Vector3 d = 0.5f * (-p0 + 3f * p1 - 3f * p2 + p3);
Vector3 pos = a + (b * t) + (c * t * t) + (d * t * t * t);
return pos;
}
void DisplayCatmullRomSpline(int pos)
{
//Clamp to allow looping
p0 = controlPointsList[ClampListPos(pos - 1)].position;
p1 = controlPointsList[pos].position;
p2 = controlPointsList[ClampListPos(pos + 1)].position;
p3 = controlPointsList[ClampListPos(pos + 2)].position;
//Just assign a tmp value to this
Vector3 lastPos = Vector3.zero;
//t is always between 0 and 1 and determines the resolution of the spline
//0 is always at p1
for (float t = 0; t < 1; t += 0.1f)
{
//Find the coordinates between the control points with a Catmull-Rom spline
Vector3 newPos = ReturnCatmullRom(t, p0, p1, p2, p3);
//Cant display anything the first iteration
if (t == 0)
{
lastPos = newPos;
continue;
}
Gizmos.color = Color.white;
Gizmos.DrawLine(lastPos, newPos);
Gizmos.color = Color.yellow;
var tan = ReturnCatmullRomTangent(t, p0, p1, p2, p3);
Gizmos.DrawLine(newPos, newPos + tan * 3);
Gizmos.DrawSphere(newPos, 0.3f);
lastPos = newPos;
}
//Also draw the last line since it is always less than 1, so we will always miss it
Gizmos.DrawLine(lastPos, p2);
}
//Clamp the list positions to allow looping
//start over again when reaching the end or beginning
int ClampListPos(int pos)
{
if (pos < 0)
{
pos = controlPointsList.Count - 1;
}
if (pos > controlPointsList.Count)
{
pos = 1;
}
else if (pos > controlPointsList.Count - 1)
{
pos = 0;
}
return pos;
}
public Vector3 ReturnCatmullRomTangent(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
Vector3 b = 0.5f * (p2 - p0);
Vector3 c = (2f * p0 - 5f * p1 + 4f * p2 - p3);
Vector3 d = 1.5f * (-p0 + 3f * p1 - 3f * p2 + p3);
Vector3 tangent = b + c * t + d * t * t;
return tangent.normalized;
}
void Object(int pos)
{
p0 = controlPointsList[ClampListPos(pos - 1)].position;
p1 = controlPointsList[pos].position;
p2 = controlPointsList[ClampListPos(pos + 1)].position;
p3 = controlPointsList[ClampListPos(pos + 2)].position;
Vector3 lastPos = Vector3.zero;
for (float t = 0; t < 1; t += 0.1f)
{
Vector3 newPos = ReturnCatmullRom(t, p0, p1, p2, p3);
if (t == 0)
{
lastPos = newPos;
continue;
}
var tan = ReturnCatmullRomTangent(t, p0, p1, p2, p3);
Instantiate(obj, newPos, Quaternion.LookRotation(tan));
lastPos = newPos;
}
}
}
Answer by Bunny83 · Aug 15, 2016 at 11:55 AM
First of all i moved this question to the help room. This question is no longer a general Unity question but a specific debugging / general help question. If you have further questions like this you should post them in the Help Room. If you don't know what i'm talking about you might want to have a look at the user guide of this site.
Well this question finally tells me you haven't understood (or even read?) the code you've copied at all. In line "92" of your posted code there's a comment. It specifically draws the last segment.
How many points are generated is controlled by the for loop in line 70 (and line 134). It goes from 0 to 1 at a step size of "0.1". Therefore you get 10 points along one segment.
I'm not sure what you made p0 to p3 member variables of the class. They should be local variables as you don't need / should store them outside of each method.
The way the original code draws the lines is a bit "clumsy". If you want a certain point count it's better to use an int variable that iterates through the point count you want and inside the loop you calculate the required "t" value. Also in the "drawing code" you need two points at the same time to be able to draw a line between them. In your object placement you only need a single point / tangent at a time:
void PlaceObjectsOnSegment(int pos, int segCount)
{
Vector3 p0 = controlPointsList[ClampListPos(pos - 1)].position;
Vector3 p1 = controlPointsList[pos].position;
Vector3 p2 = controlPointsList[ClampListPos(pos + 1)].position;
Vector3 p3 = controlPointsList[ClampListPos(pos + 2)].position;
for (int i = 0; i < segCount; i++)
{
float t = (float)i / segCount;
Vector3 newPos = ReturnCatmullRom(t, p0, p1, p2, p3);
var tan = ReturnCatmullRomTangent(t, p0, p1, p2, p3);
Instantiate(obj, newPos, Quaternion.LookRotation(tan));
}
}
The segCount parameter controls how many object you want to place between two control points.
I'm not sure if i got your last question right. If you want the objects once instantiated follow their respective points on the spline when the spline changes, you're getting in trouble with your current approach. First of all at the moment you don't store any references to your instantiated objects. So there's no way to identify a particular instance. You have to create an array where you store the Transforms of each object you create so you can "replace" them. For this it would be easier to have the code in a single method with a nested loop:
public int objCountPerSeg = 10;
private Transform[] objects;
void CreateObjects()
{
// calculate the total object count we need
int objectCount = controlPointsList.Count * objCountPerSeg;
// create an array that can hold all objects we need.
objects = new Transform[objectCount];
for (int i = 0; i < objectCount; i++)
{
// Make sure you declared "obj" as "public Transform obj;" at the top.
objects[i] = Instantiate(obj);
}
PlaceObjecsOnSpline();
}
void PlaceObjecsOnSpline()
{
for (int i = 0; i < controlPointsList.Count; i++)
{
Vector3 p0 = controlPointsList[ClampListPos(i - 1)].position;
Vector3 p1 = controlPointsList[i].position;
Vector3 p2 = controlPointsList[ClampListPos(i + 1)].position;
Vector3 p3 = controlPointsList[ClampListPos(i + 2)].position;
for (int n = 0; n < objCountPerSeg; n++)
{
float t = (float)n / objCountPerSeg;
Vector3 newPos = ReturnCatmullRom(t, p0, p1, p2, p3);
Vector3 tan = ReturnCatmullRomTangent(t, p0, p1, p2, p3);
Transform inst = objects[i * objCountPerSeg + n];
inst.position = newPos;
inst.rotation = Quaternion.LookRotation(tan);
}
}
}
You would call "CreateObjects()" once in Start to create all the objects and simply call "PlaceObjecsOnSpline()" whenever you want to update their positions.
Your answer
Follow this Question
Related Questions
How do i calculate mesh on a spline?? 0 Answers
How do i Instantiate sub-Points with in Multiple Points?? 0 Answers
How do i have game object in the middle of a Catmull spline? 0 Answers
How do I Instantiate a prefab at the x axis position of where the UI button was clicked 1 Answer
How to fix snake behavior in Unity3d? 0 Answers