Getting constant speed from iTween (Solution Included - c# files attached)

This is a solution… not a question, C# files are attached at the bottom of this post…

So I started using iTween yesterday, and I’d like to say thank you to the developer for writing this tool, It will be a massive time saver for me I have no doubt.
While using it though I came to realize that it was missing 2 specific features that I need for my current work.

  1. The ability to sample a curves position uniformly based on length (ie constant speed in real world units)
  2. The ability to get the normal of a curve at a given point.
    . Only way I can see to do this is using a dummy object and the PutOnPath() method, then read the rotation of the object.

A quick search online revealed many people having this same issue (at least with #1), so I’ve come up with a solution to both that will hopefully help others.

Other peoples workarounds seemed to suffer from accuracy problems that I think this one should help eliminate, also it doesn’t require re-sampling to a new curve (my first thought), and doesn’t add much at all to ram or cpu usage.
The concept is to use the AnimationCurve type built into unity and create a lookup table (ie. valueA == valueB type relationship).

The class is simple, meant only to answer the 2 features missing above.
All movement will have to be done in code, I’ve included a simple example script, as well as an Editor script that makes creating the spline much easier, (It is my first editor script so it looks rather messy but it works well, all controls are straight forward and shouldn’t require any explanation).

Other than the control points of the curve the only other value is “samples”, this number can be kept very low and still get good results since the AnimationCurve automatically interpolates the results of my sampling. The only time I’ve needed to up the samples is when you are looping the spline and the tangent from first and last point is high, or if your curvature has massive changes between points.
Numbers between 5-20 should be fine for almost every situation.

As commented in the class, if the samples setting or the points in the array for the curve change you’ll want to run Rebuild() on the curve, The editor script takes care of this automatically, but if you change point locations in code you’ll have to run Rebuild() manually.

One thing I couldn’t figure out, is how do I disable the control point handles from being movable if I’m orbiting in the scene view with alt key pressed down? Hopefully someone here can enlighten me?

Hope its of use to others.
Here are the files…

New Component that uses iTween
1248422–53881–$MotionPath.cs (6 KB)

Inspector for MotionPath type
1248422–53882–$MotionPathEditor.cs (4.54 KB)

Example of how to use
1248422–53888–$FollowMotionPath.cs (793 Bytes)

  1. If you need your object to travel at a constant rate on a path, I am not sure iTween supports that.
  2. Yes, I think something like that would be wise.
  1. Now it can provided that you move your object in code… with my class you can sample the curve with uv values between 0-1 and it will be normalized to the length of the curve.
    The method of the class to do this is PointOnNormalizedPath()

  2. Same for the normal of the curve
    The method of the class to do this is NormalOnNormalizedPath()

I also have PointOnPath() which works the same as the default behavior of iTweens PointOnPath() method
and NormalOnPath() that will get the normal of the path at the default (non-normalized) iTween position No need to use PutOnPath() with dummy objects to get the normal.

Haven’t tried yet, but you should be able to use iTweens more generic functions like FloatUpdate() to tween your uv value before putting it into my script. That way you should be able to get easing using iTween, and maintain normalized paths.

Just wanted to add my thanks and a way to test your theory. I added this script to the same gameobject that has the “FollowMotionPath” script and inspected the speed factor of it:

using UnityEngine;
using System.Collections;

public class calcSpeed : MonoBehaviour {

	private Vector3 prevPosition;

	public float speed;

	// Use this for initialization
	void Start () {
		prevPosition = transform.position;
	}
	
	// Update is called once per frame
	void FixedUpdate () {
	
		var diff = transform.position - prevPosition;

		speed = diff.magnitude * (1f / Time.deltaTime);

		prevPosition = transform.position;
	}
}

again, thanks. It fluctuated around the given speed, between 0.995 and 1.005 (speed given was 1), which is good.

Hello there,

Great job on making this, but I’ve got a problem with actually “applying” it.
Not sure where to put what…
I feel stupid for asking this, but I did look through all the scripts and try to understand where things go, but I wasn’t able to get it to work.

One of the things gives me a strange error saying that this script doesn’t (something) of Mono Behaviour.

Any info would be great, my project is basically stuck at fixing the speed consistency.

-Note: I have gotten suggestions on using different “tween” systems, but they don’t work for me as well as this does with all the functions and triggers I already made.
Thanks.

I’m not sure exactly where your having difficulty, so I’ll just run down some quick setup instructions… I haven’t viewed this in a while so my apologies if there are any inaccuracies.

Install:

  1. MotionPathEditor.cs is the editor script for the MotionPath component… this must be put into a folder named “Editor” within your unity project DOCS

  2. MotionPath.cs is a gameObject component that can be placed anywhere within your unity project. This is added to any gameobject and is where you define the points of your motion path.

  3. FollowMotionPath.cs is just an example script of how to use the component.
    In this case you need to set the motionPath variable of the script to the path you created by dragging the gameObject with the MotionPath.cs component into this field in the inspector window DOCS

The key lines to note within the example are

Vector3 pos = motionPath.PointOnNormalizedPath(uv);
Vector3 norm = motionPath.NormalOnNormalizedPath(uv);

This is how you lookup a position and a normal on your curve in a normalized way
So for the values of uv… 0 = start of curve, 0.5=middle of curve in realworld units, 1 = end of curve
So with each update you increase the uv value and your object will move allong the curve

This line uses the length of the path to figure out what the uv increment should be… speed is in real world units per second

uv += ((speed / motionPath.length) * Time.fixedDeltaTime);

This line is used if loop is set to true to make sure that once the uv value goes beyond 1 it will loop back to 0

uv = (uv<0?1+uv:uv) %1;

Hopefully that answers your question.
Its been a while since I posted this, glad to know it may be of use to someone.

1 Like

Works perfectly, thanks for a solution to this.

This is working great to get a constant speed, but I am having an issue with the “forward” part of your example. I am working in a 2D space (only X,Y). I have an object moving along a path that should always face the direction of travel. That is all good so far. I start out by moving left. the first thing that happens is Unity orients my object so that the objects z axis is facing the direction of travel and the y axis is up. However, lets say that I make a U-turn so that my new direction is directly to the right. At the point along that curve where the object’s y axis would begin pointing in a downward direction, the object flips so that y is again pointing up. if there a way to stop it from flipping?

if I understand the problem…
perhaps you want to change line 31 of the FollowMotionPath.cs file, so instead of providing a forward vector only, you should use the LookAt() method of the transform… that way you can provide your own up vector too.
This function wants a Vector3 for the target position… To get that

Vector3 targetPosition = speed>0? pos+norm:pos-norm;
transform.LookAt(targetPosition, "yourUpVectorHere")

You could also just try adding the following after line 31, but I’m not so sure the results will be reliable.

transform.up = "upVectorHere";

I’m hesitant to tell you how I’d get your up vector unless I saw a screen shot of the motion path that is the problem. If you have a spline that is like a racetrack and you always want you up vector of the object facing outward from that curve, that could be a tricky problem to solve.
If you just need to make sure the object is facing a direction relative to its direction of travel, using a fixed (or changing based on direction) vector might work.

If you upload a screenshot of the motion path you want and what direction you want the object to be facing & up vector along that path, I can give it you some more advice.

I have an oval path set up and modified the code so I can implement an ‘offset’ parameter. This simply adds distance from the path in the object’s transform.right after the object is positioned on the path.
This works great, but the objects at greater radii move at the same speed and complete the path at the exact same time as objects with smaller radii. This is understandable and I can see in the code why it is happening, but I can’t figure out how to fix it. I’d rather not create umpteen identical paths and then try to ease between them -_-
If you guys can think of anything any help would be great.

All right here is what I currently have. It positions the object and then repositions it later if the object has travelled past the limit.
My only issues here are:
A pretty wobbly speed (Will range from +/- 5 mph at speeds under 20mph and it only gets worse from there)
And lastly I can’t seem to get the loop to work. Probably because it tried to make a jump and the script limits it? IDK. It seems to teleport the object to 0.6 or so.

using UnityEngine;
using System.Collections;

public class FollowMotionPath : MonoBehaviour
{
    public MotionPath motionPath;
    public float startPosition;
    public float speed;                                        // Realworld units per second you want your object to travel
    public bool loop;
    public float offset;
    float myUV;

    private Vector3 lastPos;
    private float lastUV;

    void Start()
    {
        myUV = startPosition;
        if (motionPath == null)
            enabled = false;
    }
   
   
    void FixedUpdate()
    {
        //Storing last values for a delta
        lastPos = transform.position;
        lastUV = myUV;

        myUV += ((speed / motionPath.length) * Time.fixedDeltaTime);            // This gets you uv amount per second so speed is in realworld units
        if (loop)
            myUV = (myUV<0?1+myUV:myUV) %1;
        else if (myUV > 1)
            enabled = false;
        Vector3 pos = motionPath.PointOnNormalizedPath(myUV);
        Vector3 norm = motionPath.NormalOnNormalizedPath(myUV);

        //Incorporating the offset
        Vector3 targetPos = pos + (transform.right*offset);
        //Converting speed input to MPH then setting up deltas
        float maxDistance = (speed / 2.236936f) * Time.fixedDeltaTime;
        float deltaUV = myUV - lastUV;
        float deltaDist = Vector3.Distance( targetPos, lastPos );
        //If the travelled distance is farther than we can go at the selected speed
        if (deltaDist > maxDistance) {
            //Clamp the UV change in proportion to the clamped distance change
            myUV = lastUV + deltaUV*(maxDistance/deltaDist);
            //Reincorporated the loop to see if it would work. Nope.
            if (loop)
                myUV = (myUV<0?1+myUV:myUV) %1;
            else if (myUV > 1)
                enabled = false;
            //Putting it back on the path and offsetting it again
            pos = motionPath.PointOnNormalizedPath(myUV);
            targetPos = pos + (transform.right*offset);
        }
        //Assigning a position
        transform.position = targetPos;
        transform.forward = speed >0?norm:-norm;
    }
   
}

Thank you very very much for your hard work Sannyasi! You have saved lots of time for us by creating this MotionPath script! This is the best working method what I tried! It just works like a charm without bumpy, blocky motion. Keep up the good work!