dorien
dorien

Reputation: 5387

Dynamically changing speed of object in Unity

I need an object to move up and down according to a dynamic timing. The exact locations are stored in a List() called Timings. This will contain sorted entries such as 0.90 1.895 2.64 3.98... These timings are relative the playing of music, so they can be compared to TheMusic.time. (TheMusic is my AudioSource).

Right now (see code below), it's moving statically up and down using moveSpeed. How can I make it so that alternatively, the top and bottom point are reached at predefined times? When it reaches the end of the list of timings the movement should stop.

public class Patrol : MonoBehaviour {

    public Transform[] patrolPoints;  //contains top and bottom position
    public float moveSpeed; //needs to be changed dynamically

    private int currentPoint; 

    // Initialization
    void Start () {
        transform.position = patrolPoints [0].position;
        currentPoint = 0;
    }

    // Update is called once per frame
    void Update () {

        print (currentPoint);
        if (currentPoint >= patrolPoints.Length) {
            currentPoint = 0;
        }

        if (transform.position == patrolPoints [currentPoint].position) {
            currentPoint++;
        }

        transform.position = Vector3.MoveTowards (transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);

    }
}

It is important that there is no moving away from the absolute time point. E.g. when Timings reaches a high time such as 1009 there shouldn't be much drift.

Also note that other things (such as changing color and checking user behaviour) need to happen at the same time, see my other question.

Upvotes: 2

Views: 1122

Answers (3)

Programmer
Programmer

Reputation: 125245

The path to your solution is this answer which shows function to move GameObject over time.

With that function in hand, you can start a coroutine, loop over the List that contains the beatsTimer. Inside each loop you should have a variable that determines if you should go up or down after each loop. In my example I named that variable upDownMoveDecider. This variable will decide which index of the patrolPoints array to use.(I assume there are only two points with index 0 and 1).

Also, you can call the moveToX function inside that for loop each time. Make sure to yield it so that it will wait for the moveToX function to finish or return before going to the next function.


Below is what that should look like:

public Transform[] patrolPoints;  //contains top and bottom position
List<float> beatsTimer = new List<float>();
public Transform objectToMove;

void Start()
{
    //For testing purposes
    beatsTimer.Add(0.90f);
    beatsTimer.Add(1.895f);
    beatsTimer.Add(2.64f);
    beatsTimer.Add(3.98f);

    //Start the moveobject
    StartCoroutine(beginToMove());
}

IEnumerator beginToMove()
{
    // 0 = move up, 1 = move down
    int upDownMoveDecider = 0;

    //Loop through the timers
    for (int i = 0; i < beatsTimer.Count; i++)
    {
        if (upDownMoveDecider == 0)
        {
            //Move up
            Debug.Log("Moving Up with time: " + beatsTimer[i] + " in index: " + i);
            //Start Moving and wait here until move is complete(moveToX returns)
            yield return StartCoroutine(moveToX(objectToMove, patrolPoints[upDownMoveDecider].position, beatsTimer[i]));

            //Change direction to 1 for next move
            upDownMoveDecider = 1;
        }
        else
        {
            //Move down
            Debug.Log("Moving Down with time: " + beatsTimer[i] + " in index: " + i);
            //Start Moving and wait here until move is complete(moveToX returns)
            yield return StartCoroutine(moveToX(objectToMove, patrolPoints[upDownMoveDecider].position, beatsTimer[i]));

            //Change direction to 0 for next move
            upDownMoveDecider = 0;
        }
    }
}

IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
    float counter = 0;

    //Get the current position of the object to be moved
    Vector3 startPos = fromPosition.position;

    while (counter < duration)
    {
        counter += Time.deltaTime;
        fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
        yield return null;
    }
}

The code inside that for loop is long but it is easier to understand. Here is the shorter version with no if/else statement.

//Loop through the timers
for (int i = 0; i < beatsTimer.Count; i++)
{
    //Move up/Down depening on the upDownMoveDecider variable
    string upDown = (upDownMoveDecider == 0) ? "Up" : "Down";
    Debug.Log("Moving " + upDown + " with time: " + beatsTimer[i] + " in index: " + i);

    //Start Moving and wait here until move is complete(moveToX returns)
    yield return StartCoroutine(moveToX(objectToMove, patrolPoints[upDownMoveDecider].position, beatsTimer[i]));

    //Change direction
    upDownMoveDecider = (upDownMoveDecider == 1) ? 0 : 1;
}

Upvotes: 0

David
David

Reputation: 16277

Don't reinvent the wheels. Use tween libraries, e.g. iTween. There are more than 20 easing types defined for you:

enter image description here

Example code:

iTween.MoveBy(gameObject,iTween.Hash(
 "x"   , 2,
 "time", 0.2f
));

Upvotes: 0

Thalthanas
Thalthanas

Reputation: 506

Add a float array lets say public float[] moveSpeedArray;

You can populate this array in Start() or in editor as you like.

You are already getting your currentPoint and updating it.

Give that currentPoint as index of your moveSpeedArray.

Like;

transform.position = Vector3.MoveTowards (transform.position, patrolPoints[currentPoint].position, moveSpeedArray[currentPoint] * Time.deltaTime);

So like this you can move your object to places in different predefined times.

Hope this helps! Cheers!

Upvotes: 1

Related Questions