Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Mr.Z · Oct 20, 2014 at 02:08 AM · c#2drotationcoroutine

Start Coroutine after other has finished

Hi, I'm working on 2d ship game and I'm trying to get my ship to rotate and then move after having finished rotating. In order to make the rotation happen over time I'm using a while loop inside a coroutine and I'm also using another coroutine to make the ship move but I don't want the ship to start moving before its finished rotating. Here is my code:

 void Update () {
  //stuff
  StartCoroutine(RotateToTarget());
  //isRotatingToTarget = false; pointless since the coroutine wouldn't have finished rotating
  //StartCoroutine (MoveToTarget()); If I add this here it doesn't wait for the rotation to finish
 }
 
 IEnumerator RotateToTarget(){
  while(transform.rotation != rotationToTarget)
  {
   transform.rotation = Quaternion.Lerp(transform.rotation, rotationToTarget , Time.deltaTime);
   this.isRotatingToTarget = (transform.rotation != rotationToTarget);
   yield return null;                    
 }
 isRotatingToTarget = false;
 print ("Can move to target");
 StartCoroutine (MoveToTarget()); //this never gets excecuted
 
 }

 IEnumerator MoveToTarget(){
     if (!isMovingToTarget && !isRotatingToTarget){
     while(transform.position.x != target.x && transform.position.y != target.y)
     {
         isMovingToTarget = true;
         transform.position  = Vector2.MoveTowards(transform.position, target, Time.deltaTime*movementSpeed);

         yield return null;
     }
     this.isMovingToTarget = false;
 }
Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image LeftRight92 · Oct 20, 2014 at 03:05 AM 0
Share

Does "Can move to target" ever get printed?

avatar image Mr.Z · Oct 20, 2014 at 03:14 AM 0
Share

@LeftRight92 not as it is, no

4 Replies

· Add your reply
  • Sort: 
avatar image
4
Best Answer

Answer by Yword · Oct 20, 2014 at 03:52 AM

I think it is because the comparison statement (transform.rotation != rotationToTarget) always returns true.

It would be better to compare 2 Quaternions using Quaternion.Angle(). You can check out the explanation at http://docs.unity3d.com/ScriptReference/Quaternion-operator_eq.html

So, you can use Quaternion.Angle() to get the angle between 2 quaternions, then you can check whether the angle is below some small threshold.

 float rotationThreshold = 0.1f;
 bool isClosedToTargetRotation = Quaternion.Angle(transform.rotation, rotationToTarget) < rotationThreshold;


Also, due to floating point imprecision, it is not recommended to compare floats using the equal operator. So, to check whether 2 positions are same, it would be better to compare the distance between them.

 float distanceThreshold = 0.1f;
 bool isClosedToTargetPosition = Vector3.Distance(transform.position, target.position) < distanceThreshold;
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
3

Answer by Pyrian · Oct 20, 2014 at 04:01 AM

I wouldn't use a coroutine for any of this. All you need is, in Update:

 if (isRotatingToTarget) {
    transform.rotation = Quaternion.Lerp(transform.rotation, rotationToTarget , Time.deltaTime);
    isRotatingToTarget = (transform.rotation != rotationToTarget);
    if (!isRotatingToTarget) isMovingToTarget = true;
 } else if (isMovingToTarget) {
    transform.position  = Vector2.MoveTowards(transform.position, target, Time.deltaTime*movementSpeed);
    if (transform.position.x == target.x && transform.position.y == target.y) isMovingToTarget = false;
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

Answer by robertbu · Oct 20, 2014 at 05:52 AM

The code in your question starts the RotateToTarget coroutine on every Update(). This stacks up Coroutine calls. So if your movement script would take 5 seconds to complete and your app is running at 60 fps, you have 300 coroutine running that may be accelerating the process or fighting with each other, or simply taking resources. Coroutines work well when you want independent piece of work to run without interference. If the target will be moving so this code as to reacquire it, then consider using Update() as @Pyrian suggests.

As for not completing the rotation, the issue is this line:

 transform.rotation = Quaternion.Lerp(transform.rotation, rotationToTarget , Time.deltaTime);

The way this form of Lerp works is to walk the approximately the same fraction of the remaining distance to the goal each frame. Since the distance is shrinking, the same fraction represents smaller and smaller rotations. The result is an eased movement. But the problem is that it takes a very long time to complete...and only completes due to floating point imprecision and perhaps some slop built into rotation comparison. It like the old adage, "how many days will it take to reach my goal if I walk half the distance each day." The answer is infinite...you never reach your goal. A solution is the one @YWord suggests...to use an angle threshold.

Chaining coroutines or using flags will work for what you want here, but there is an alternate. You can use 'yield' with coroutines. Here a demonstration rewrite of your code. It may not be a perfect fit since I don't have all your code, nor the context where it will be used.

 using UnityEngine;
 using System.Collections; 
 
 public class Example : MonoBehaviour {
 
     public Transform target;
     public float rotationSpeed = 5.0f;
     public float movementSpeed = 2.25f;
 
     private bool isMoving = false;
 
     void Update () {
         if (Input.GetKeyDown (KeyCode.Space)) {
             StartCoroutine(RotateThenMove());
         }
     }
 
     IEnumerator RotateThenMove() {
         if (isMoving) {
             yield break;
         }
 
         isMoving = true;
         yield return StartCoroutine(RotateToTarget());
         yield return StartCoroutine(MoveToTarget());
         isMoving = false;
     }
 
     IEnumerator RotateToTarget(){
         Quaternion rotationToTarget = Quaternion.LookRotation(target.position - transform.position);
         while(Quaternion.Angle(transform.rotation,rotationToTarget) > 0.5f) {
             transform.rotation = Quaternion.Slerp(transform.rotation, rotationToTarget , Time.deltaTime * rotationSpeed);
             yield return null;                    
         }        
     }
     
     IEnumerator MoveToTarget() {
         while(transform.position != target.position) {
             transform.position  = Vector3.MoveTowards(transform.position, target.position, Time.deltaTime*movementSpeed);
             yield return null;
         }
     }
 }   


Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Mr.Z · Oct 20, 2014 at 07:04 PM 0
Share

Thanks, it works! The only problem I'm having is the ´is$$anonymous$$oving´ variable gets set to false long before the coroutines have finished so you can get the ship to move to another location by just clicking without the animations having finished. I've tried putting it inside ´$$anonymous$$oveToTarget()´ but there is no use. Also for some reason the ship takes forever to finish rotating (either that or there is some kind of pause inbetween rotating and movement).

avatar image Pyrian · Oct 20, 2014 at 07:12 PM 1
Share

Seriously, what's up with the coroutines? You are doing absolutely nothing that even suggests to me that "this is better with a coroutine" rather than just using the flags in the Update function.

avatar image Mr.Z · Oct 20, 2014 at 07:40 PM 0
Share

@Pyrian I'm just playing around with the engine thats all

avatar image
0

Answer by LeftRight92 · Oct 20, 2014 at 04:30 AM

Although starting the second coroutine in the first coroutine should work (I think), you can use a 'flag'. Note this will only work if "Can move to target" prints successfully. If it doesn't, see the paragraph at the end of this solution.

Firstly, add a new boolean to your script:

 private bool canMoveToTarget = false;

Add the following check to your update method:

 if(canMoveToTarget) StartCoroutine(MoveToTarget());

Then, set the boolean to true on the last line of RotateToTarget() and set it false on the first line of MoveToTarget().

That all said, you may want to check your vector maths. Due to the nature of Lerp, unless it receives a '1' as the final parameter, you're always going to be moving closer to the target, never equal to it. I recommend you set transform.Rotation equal to rotationToTarget once the difference between the two is small enough.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

30 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Flip over an object (smooth transition) 3 Answers

Smoothed Rotation with 2D Top-Down View 1 Answer

How to Address Texture2D Elements from a Sprite with Sprite Mode: Multiple, in Code? 1 Answer

Getting the rotation of an object does not return correct rotation 1 Answer

void update working under conditions 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges