4

Good afternoon,

I am attempting to implement a GameObject in Unity that moves along a Cubic CatMull-Rom Spline given 8 constrained random values. I have implemented a function, ComputePointOnCatmullRomCurve, that returns a point on a cubic Catmull-Rom curve (given a scalar 'u' from 0 to 1 and segment_number which indicates which 4 pts to use for interpolation).

I am having trouble implementing the update function to allow for the GameObject to move smoothly. I am currently calling ComputePointOnCatmullRomCurve each update and am incrementing the segment_number each time. The GameObjects position is then set to the result of the function.

However this leads to a GameObject that moves extremely quick. I believe my Update function is incorrect, but I am unsure about how to move the GameObject with respect to the points that are being output by the interpolation function.

If someone would be able to explain to me what I am doing incorrectly, or provide an example or link to an example, it would be very helpful!

Function to compute point on curve:

Vector3 ComputePointOnCatmullRomCurve(float u, int segmentNumber)
{
    // TODO - compute and return a point as a Vector3       
    // Points on segment number 0 start at controlPoints[0] and end at controlPoints[1]
    // Points on segment number 1 start at controlPoints[1] and end at controlPoints[2]
    //       etc...

    Vector3 point = new Vector3();

    float c0 = ((-u + 2f) * u - 1f) * u * 0.5f;
    float c1 = (((3f * u - 5f) * u) * u + 2f) * 0.5f;
    float c2 = ((-3f * u + 4f) * u + 1f) * u * 0.5f;
    float c3 = ((u - 1f) * u * u) * 0.5f;

    Vector3 p0 = controlPoints[(segmentNumber - 1) % NumberOfPoints];
    Vector3 p1 = controlPoints[segmentNumber % NumberOfPoints];
    Vector3 p2 = controlPoints[(segmentNumber + 1) % NumberOfPoints];
    Vector3 p3 = controlPoints[(segmentNumber + 2) % NumberOfPoints];

    point.x = (p0.x * c0) + (p1.x * c1) + (p2.x * c2) + (p3.x * c3);
    point.y = (p0.y * c0) + (p1.y * c1) + (p2.y * c2) + (p3.y * c3);
    point.x = (p0.z * c0) + (p1.z * c1) + (p2.z * c2) + (p3.z * c3);

    return point;
}

Update Function:

void Update () {
    // TODO - use time to determine values for u and segment_number in this function call
    // 0.5 Can be used as u
    time += DT;

    segCounter++;

    Vector3 temp = ComputePointOnCatmullRomCurve(time, segCounter);
    transform.position = temp;
}

Variables:

const int NumberOfPoints = 8;
Vector3[] controlPoints;

const int MinX = -5;
const int MinY = -5;
const int MinZ = 0;

const int MaxX = 5;
const int MaxY = 5;
const int MaxZ = 5;

float time = 0;
const float DT = 0.01f;
public static int segCounter = 0;

Thanks!

Matt

Masoud Mohammadi
  • 1,721
  • 1
  • 23
  • 41
Marcus Koz
  • 255
  • 5
  • 21
  • 1
    Since you say it's moving too quick, does changing `time += DT;` to `time += DT * Time.deltaTime;` resolve your timing issues? See http://docs.unity3d.com/ScriptReference/Time-deltaTime.html – Chris McFarland Nov 03 '14 at 22:26
  • Are you incrementing the segment that the entity is on every single Update call? – Matt Coubrough Nov 03 '14 at 22:27
  • @Chris adding Time.delatTime does not resolve the issue. The time variable can be set at constant, say 0.5 and fast motion still occurs. – Marcus Koz Nov 03 '14 at 22:31
  • @MattCoubrough Yes, the reference point for the segment is being incremented each update, the three other points used for interpolation based off that initial point are initialized within the ComputePointOnCatmullRomCurve – Marcus Koz Nov 03 '14 at 22:35

1 Answers1

4

Your are doing 2 errors in the Update function.

First error:

You are incrementing the index of the current segment at each frame (segmentNumber). I guess this should be done only when the object complete it's travel along the previous spline segment.

Hint:

For spline curves defined by multiple patches I usually express the time (u) in the range [0,n] where n is the number of segment defining the curve. This way you can retrieve the index of the current patch (segmentNumber) simply extracting the integral part from the parameter. Something like:

int segmentNumber =  Mathf.FloorToInt(u);
float segmentU = u - segmentNumber;

Second Error

I don't know what your DT variable is, but unless you are scaling it by the frame delta time somewhere else, you have to do it. Basically you can increase time this way:

time += Time.deltaTime * speedAlongCurve;

I hope it helps.

Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • Okay, so the change in time will allow for the movement of the GameObject along the curve. Changing the index of the GameObject each update would force it to jump from position to position. So the problem is to increment the segmentNumber when appropriate, correct? I apologize, but I am having a difficult time understanding what you are trying to achieve in the first snippet of code you included. – Marcus Koz Nov 03 '14 at 23:30
  • As stated in the above comment I am having a hard time understanding how your included snippet of code functions. I was able to get a functional implementation by incrementing the segment number only when (time == 1). I also reset time to 0 at that point. However, I have a feeling that this works only by coincidence and that there is a more appropriate way to cycle through segments. – Marcus Koz Nov 03 '14 at 23:54
  • 1
    @Matt Koczwara: nevermind, first snippet was just an hint and should be functionally equivalent to increment segment index when time == 1. So if you do like that and works it's fine. The first snippet simply does the same thing: for example let's say you are in the 2nd seg at time 0.5, with your approach -> t=0.5 seg=1 , with the snippet tTot = 1.5 seg =floortoint(1.5) = 1, t = 1.5 - 1 = 0.5. It's the same. – Heisenbug Nov 04 '14 at 08:25