2

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.

dorien
  • 5,265
  • 10
  • 57
  • 116
  • You mean moving up and down periodically like a Senoid? – Ignacio Alorre Jul 26 '17 at 05:43
  • Well it's a ball that bounces to the beat of music (which is defined by the list of times) – dorien Jul 26 '17 at 06:10
  • Where is the up and down position stored? – Programmer Aug 07 '17 at 16:18
  • Patrolpoints contains the up and down position. – dorien Aug 07 '17 at 16:45
  • Ok. Now, explain the beats = 1 second, 1.9 seconds, 2.1 seconds, 4 seconds, etc values....Where do you get those values and when do they change? I don't understand that part. – Programmer Aug 07 '17 at 19:52
  • I've clarified the question. Beats are stored in a List() called Timings. – dorien Aug 08 '17 at 00:45
  • 1
    Hi, for some reasons, I am not notified when you comment. You can use the @Programmer to notify me. Let me just be clear with your edit....It goes from up to down with in 0.90 seconds then down to up in 1.895 seconds and then up to down again in 2.64 seconds and finally down to up in 3.98 seconds? If this is it, what happens when it reaches the end of the List? Stop? Clear the list? – Programmer Aug 08 '17 at 09:54
  • @Programmer At each time of the list it should reach alternatively top- bottom- top- bottom- top- bottom-... position. So basically indicating the beats of a song with the movement of the ball. – dorien Aug 08 '17 at 13:10
  • @Programmer When it reaches the end of the list, the object should stop moving. – dorien Aug 09 '17 at 05:38
  • I was waiting for you to answer the question and you just did. Check my answer. – Programmer Aug 09 '17 at 12:38

3 Answers3

1

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!

Thalthanas
  • 496
  • 6
  • 10
  • Thank you. I've tried this. However, if I put [0 0.5 3] in the array. The moving goes way slower then .5 seconds and 3 seconds. Perhaps the time units are off? – dorien Jul 27 '17 at 02:48
  • Maybe 0.5 * Time.deltaTime is not equal to 0.5 seconds or 3 * Time.deltaTime is not equal to 3 seconds? I can't confirm this right now. If not, try with different values which will be correspond to seconds and come up with a formula ? @dorien – Thalthanas Jul 27 '17 at 06:03
  • I had to change some things to your suggestions. The index for moveSpeedArray should not be currentPoint, but another counter that keeps going up (not up and down like currentPoint). Then the array needs to be formatted as the difference between times, not the absolute time. Then it seems to work. Thanks! – dorien Aug 05 '17 at 04:58
  • Actually, I still don't have it entirely, as I believe I should take into account the distance between the two object to be able to pass along the speed so that the object arrives in the required timing window. – dorien Aug 09 '17 at 07:39
0

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
));
David
  • 15,894
  • 22
  • 55
  • 66
  • How would this help me to get the times between the top/bottom to be exactly on the seconds I determine in the list? – dorien Jul 27 '17 at 02:50
  • Do I understand it correctly that iTween.MoveBy needs to be executed once on each 'turn' and it will move linearly by default? x in your example is the x coordinate it needs to move towards? – dorien Aug 09 '17 at 07:41
  • I think some one has marked another reply as answer, you 'd better use that solution? – David Aug 09 '17 at 08:52
  • Thanks, but those timings don't add up – dorien Aug 09 '17 at 08:54
0

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;
}
Programmer
  • 121,791
  • 22
  • 236
  • 328