Search Unity

Translate an Object in a direction for x seconds, wait, then move back

Discussion in 'Scripting' started by Ghujelk, Jul 12, 2016.

  1. Ghujelk

    Ghujelk

    Joined:
    May 21, 2015
    Posts:
    15
    I am trying to write a script that will have a cube move to the left for a few seconds, then rotate slightly over the y axis, then move to the right for a few more seconds than it did to the left.

    This is my code so far,
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class CrabBehavior : MonoBehaviour {
    6.     public float turnSpeed = 5f;
    7.     public float scuttleSpeed = 15f;
    8.  
    9. void Update() {
    10.         Scuttle();
    11. }
    12.  
    13. public void Scuttle()
    14.     {                      
    15.         transform.Translate(scuttleSpeed * Time.deltaTime, 0, 0);
    16.             Debug.Log ("I'm scuttling! (Right)");
    17.    
    18.         StartCoroutine(Wait(1));
    19.             Debug.Log ("I'm waiting 1 second");
    20.  
    21.         transform.Rotate(0 ,turnSpeed * Time.deltaTime ,0);
    22.             Debug.Log ("I'm turning!");
    23.  
    24.         StartCoroutine(Wait(.5f));
    25.             Debug.Log ("I'm waiting .5 seconds");
    26.  
    27.         transform.Translate(-scuttleSpeed * Time.deltaTime, 0, 0);
    28.             Debug.Log ("I'm scuttling! (Left)");
    29.  
    30.         StartCoroutine(Wait(3));
    31.             Debug.Log ("I'm waiting 3 seconds");
    32.     }
    33.  
    34. IEnumerator Wait(float delay)
    35. {
    36.      yield return new WaitForSeconds(delay);
    37. }
    38.  
    The console doesn't throw any errors, and each translation moves the cube if I comment out the other lines,
    however if nothing is commented out, nothing moves except for a slight rotation. Looking at the console I see that all of my debug.log information is happening at the exact same time.

    I think this is because it is in the Update function and the left and right translation are fighting each other, but I don't know where else I can call these translations repeatedly so this behavior gets looped.
     
  2. xjjon

    xjjon

    Joined:
    Apr 15, 2016
    Posts:
    612
    Scuttle is going to be called on every frame. Which is why nothing happens.

    Two recommendations, Add a bool at the start and end of scuttle, so it doesn't run again unless it's already finished.

    Code (csharp):
    1.  
    2.  
    3. bool complete = true;
    4. public void Scuttle(){
    5.  
    6. if(complete){
    7.  
    8. complete = false;
    9. //scuttle code
    10.  
    11. complete = true;
    12. }
    13. }
    14.  
    Another method is make add an Animator component to the cube, then just loop the animation.
     
  3. Ghujelk

    Ghujelk

    Joined:
    May 21, 2015
    Posts:
    15
    Unless I am missing something, this does not seem to alter the behavior of my script in game. Whether I call the Scuttle function in Start or Update, Awake or FixedUpdate, my debug.log outputs to the console each of my comments at the exact same time, as if it is not waiting at all before stepping to the next line of code. I'm going to go reread the WaitForSeconds documentation.

    I tried with and without your bool setup. Thank you for your suggestion though, I will likely keep the initial complete = true boolean for future use.

    Edit - I would like to try to actually move the object, although I will remember to try and use an animation if I don't need to, thanks.
     
  4. xjjon

    xjjon

    Joined:
    Apr 15, 2016
    Posts:
    612
    Actually I looked at your code, it is because the coroutine you are yielding is Wait(). So wait() will yield, but not Scuttle.

    To get the functionality I think you want, make Scuttle enumerable

    i.e.

    Code (csharp):
    1.  
    2.  
    3. [LIST=1]
    4. [*]public IEnumerator Scuttle()
    5. [*]    {                    
    6. [*]        transform.Translate(scuttleSpeed * Time.deltaTime, 0, 0);
    7. [*]            Debug.Log ("I'm scuttling! (Right)");
    8. [*]  
    9. [*]        yield return new WaitForSeconds(1);
    10. [*]            Debug.Log ("I'm waiting 1 second");
    11. [*]
    12.  
    13. [*]        transform.Rotate(0 ,turnSpeed * Time.deltaTime ,0);
    14. [*]            Debug.Log ("I'm turning!");
    15. [*]
    16.  
    17. [*]      yield return new WaitForSeconds(.5f);
    18. [*]            Debug.Log ("I'm waiting .5 seconds");
    19. [*]
    20.  
    21. [*]        transform.Translate(-scuttleSpeed * Time.deltaTime, 0, 0);
    22. [*]            Debug.Log ("I'm scuttling! (Left)");
    23. [*]
    24.  
    25. [*]        yield return new WaitForSeconds(3);
    26. [*]            Debug.Log ("I'm waiting 3 seconds");
    27. [*]    }
    28.  
    29.  
    30.  
    31. [/LIST]
    32.  
    This should work.

    Edit: the code tags look weird, don't know what happened.

    Also on other note, if you create an animation in the editor, you can animate the 'transform' of the object. So you can move the object position or whatever else. You can animate anything basically, the scale, rotation, colors, etc. It just interpolates between values.
     
  5. Ghujelk

    Ghujelk

    Joined:
    May 21, 2015
    Posts:
    15
    Using the LIST as you've shown here gives me an error on line 3, '=' is invalid attribute target. All attributes in this attribute section will be ignored

    I didn't fully understand what you meant about animating, and that description actually sounds more intuitive to me than trying to move stuff with code, I'd like to get this to work though so I better understand the pieces here. But I'll also give animation thing a shot.
     
  6. xjjon

    xjjon

    Joined:
    Apr 15, 2016
    Posts:
    612

    Sorry the list was added by the forum software, I just copied your code and edited in the forum, so it thought the line numbers were a list. Anyway, I mean this:

    Just call StartCoroutine(Scuttle) and should do what you wanted to.

    Code (csharp):
    1.  
    2.  
    3.  
    4. public IEnumerator Scuttle()
    5. {  
    6. transform.Translate(scuttleSpeed * Time.deltaTime, 0, 0);
    7. Debug.Log ("I'm scuttling! (Right)");
    8.  
    9.  yield return new WaitForSeconds(1);
    10.   Debug.Log ("I'm waiting 1 second");
    11.  
    12.   transform.Rotate(0 ,turnSpeed * Time.deltaTime ,0);
    13.   Debug.Log ("I'm turning!");
    14.  
    15.   yield return new WaitForSeconds(.5f);
    16.   Debug.Log ("I'm waiting .5 seconds");
    17.   transform.Translate(-scuttleSpeed * Time.deltaTime, 0, 0);
    18.   Debug.Log ("I'm scuttling! (Left)");
    19.  
    20.   yield return new WaitForSeconds(3);
    21.   Debug.Log ("I'm waiting 3 seconds");
    22.   }
    23.  
    24.  
    25.  
    Regarding the animations, check this out here:

    http://docs.unity3d.com/Manual/AnimationEditorGuide.html

    (Click through the pages)
     
  7. Ghujelk

    Ghujelk

    Joined:
    May 21, 2015
    Posts:
    15
    Well, even with the whole function as an IEnumerator, I am still getting debug.log info from all of them at once without any movement from the object in the Start or Update functions.

    After reading more on IEnumerator, I think I could get the behavior I am looking for if I made each aspect of the transform a separate IEnumerator, like
    Code (CSharp):
    1. IEnumerator MoveLeft(float seconds)
    2. {
    3.      yield return new WaitForSeconds(seconds);
    4.      transform.translate(scuttleSpeed * Time.deltaTime);
    5. }
    for each piece of movement and then just call each Coroutine in sequence in another function. Which may or may not be messy, but I don't know any other ways to delay an action currently.

    Just to be clear on the movement I'm trying to get I drew a little mockup in mspaint.

    This is looking from the top down onto the scene.


    Thank you so much for all your help though, I am definitely keeping the boolean thing you suggested.
     
  8. xjjon

    xjjon

    Joined:
    Apr 15, 2016
    Posts:
    612
    It's because you are calling it in Update, but with the boolean the code I added should be fine.

    You may try this piece of code:

    Code (csharp):
    1.  
    2. public class Test : MonoBehaviour {
    3.  
    4.    // Use this for initialization
    5.    void Update () {
    6.   StartCoroutine(Scuttle());
    7.    }
    8.  
    9.   bool started = false;
    10.    
    11.    IEnumerator Scuttle()
    12.   {
    13.   if (!started)
    14.   {
    15.   started = true;
    16.   Debug.Log("1");
    17.  
    18.   yield return new WaitForSeconds(2);
    19.  
    20.   Debug.Log("2");
    21.  
    22.  
    23.   started = false;
    24.   yield return new WaitForSeconds(2);
    25.  
    26.  
    27.   }
    28.   }
    29. }
    30.  
    31.  
    If you remove the if statement, so that it does not check the bool, then it will just spam the console with 1.
    However if you leave it, you will see

    1
    2
    1
    2

    with 2 seconds between the 1 & 2.
     
  9. Ghujelk

    Ghujelk

    Joined:
    May 21, 2015
    Posts:
    15
    I realized where I made a mistake in my earlier code, and that was thinking that calling the StartCoroutine(Wait(x)); would cause the progression of the code to the next line wait by 'x' seconds, but now I understand that it doesn't work like that. Which is why your recent edit which has yield return new WaitForSeconds(x); instead does work to delay the code between lines.

    It sort of works now, the object goes left, rotates, then goes right, the only issue now is the gameobject I have the script attached to is teleporting to it's new location, and instantly applying its rotation, even though I'm multiplying the value in transform.translate by Time.deltaTime. I think I was trying to avoid using Lerp but I can't remember why, so time to check the documentation!