Search Unity

Coroutines and Lag / low FPS

Discussion in 'Scripting' started by Azmar, Jun 27, 2015.

  1. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    Hello, I have this code that works perfectly the way I want it to with coroutines. Pretty much its a monster's skill and the skill has the possibility to have anywhere from 1+ attacks on it. I use the coroutine to delay when the first attack is activated and till the next attack (Example. attack 1 launches 0.1 second, attack 2 launches 0.2 second, attack 3 launches in 1 second).

    Code:
    Code (CSharp):
    1. IEnumerator GetEachAttack(GameObject newTarget, float delayTime)
    2.     {
    3.         yield return new WaitForSeconds(delayTime);
    4.         // Now do your thing here
    5.         MonsterSkills tempSkills;
    6.         MonsterData tempAttacker = attacker;
    7.         int placeNum = tempAttacker.placeNum;
    8.      
    9.         foreach (MonsterSkills.AttackInfo attackSkill in tempSkills.attacks){
    10.  
    11.             //This timer on StartCoroutine will be the time till it starts to activate the FIRST ATTACK AND ONLY FIRST ATTACK!
    12.             StartCoroutine(SpawnProjectile(attackSkill, tempSkills.firstAttTime));
    13.             //Wait for next Attack (example 0.1f till next attack)!
    14.             yield return new WaitForSeconds (attackSkill.nextAttTime);
    15.         }
    16.     }
    This is my "attack simulation" where later I can attach prefabs that will SYNC with these attacks, so my animations will be dependent on everything timing correctly every time and launching correctly. Example I would play a "tornado" spell and I will spawn these projectiles at the times to seem like the tornado hit me as a "attack simulation", but they are in code 2 separate things.

    Now here is my concern, I am doing it on an app and I cannot guarantee constant FPS and there is a possibility I may get 1 FPS during an attack, while going back to 60fps after. I don't want it to totally destroy my app if I get a fps spike...I read on the forums from past threads that it is fps dependent and I have stuff running on Time.Deltatime etc.

    If I got 1fps the tornado spell and my attacks would be out of Sync and messed up..

    How do I fix this so the game doesn't get messed up when I possibly get 1fps, 10fps, 30fps, 60fps?
    Thanks!
     
  2. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    If you get 1fps or 10fps you have more problems than attacks being out of sync - how about the game being unplayable? Input not registering etc... Anything higher than that and your coroutine will run on schedule just fine.

    What are you doing that can cause a 59fps drop? That is what you need to worry about.


    But, I will add this very important note

    Premature optimization is the root of all evil

    Seriously, write your code. Run it, see how it performs. You cant optimize something that might not need optimization. You will waste time, so much time.

    Of course you can optimize simple things as you go along, and the more experience you have the more of these simple things you will find.
     
  3. Pharan

    Pharan

    Joined:
    Oct 28, 2013
    Posts:
    102
    This is a classic problem but it was solved decades ago and we reap the benefits of the resulting paradigms today.

    If you have precise, game-relevant mechanics, time them according to Fixed Timestep.

    Remember that any logic that you time with Update() or with WaitForSeconds use Variable Timestep (an update loop that varies the delta time per update, only according to how long stuff took to process). This makes logic Framerate Dependent, which causes the problems you worry about.

    Unity's physics is framerate independent because it uses the fixed timestep loop. This gives it reliable, deterministic results whether your frame rate is 6 or 60 or whatever. Google "Fixed Timestep Game Loop" to learn more about it (not just in Unity's context).

    For coroutines, the yield instruction you want is WaitForFixedUpdate, which doesn't have an equivalent float time parameter so you have to implement that kinda manually. You can eventually make some helper methods and stuff but the idea kinda looks like this:

    Code (CSharp):
    1. IEnumerator FixedTimeCoroutineThing ()
    2. {
    3.      YieldInstruction waitForFixedUpdate = new WaitForFixedUpdate();
    4.      while (true) {
    5.  
    6.           // Your per-update logic goes here
    7.  
    8.           // This for loop is equivalent to WaitForSeconds
    9.           // but uses fixed/physics timestep instead of
    10.           // the variable one used for non-essential/loose
    11.           // game elements and graphics.
    12.           for (float duration = 1f; duration > 0; duration -= Time.fixedDeltaTime) {
    13.                yield return waitForFixedUpdate;
    14.           }
    15.      }
    16. }
     
    Last edited: Jun 27, 2015
  4. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    I actually don't have any current fps problems...it runs at a constant rate. I expect later to add networking etc and even playing these professional apps on the play store sometimes it CAN lag on their games and I don't want my game to get absolutely messed up when there is a possibility of it lagging. If it's just lagged attacks with the lagged animation but they appear to still be in sync then that is ok with me. But if I lag and lets say a tornado attack is playing and the coroutine enables 3 attacks AFTER the tornado because of lag that is not acceptable.
     
  5. Pharan

    Pharan

    Joined:
    Oct 28, 2013
    Posts:
    102
    Reading again, if both things were started in the same method and are running on the same Time.deltaTime, they actually won't end up out of sync.
    But you may have other timing issues, especially for things that are sequential and/or have set durations.

    To be super deterministic, fixed update is the way to go.
     
  6. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    Thank you that was very useful! I will implement this on Monday when I get back home :) I was trying to find some more examples on what you said as references and there doesn't seem to be much out there that I can find. Do you have any links to assist me with the helper methods?

    Thanks a ton!
     
  7. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    Hello again, I have a problem with the script above:

    Code (CSharp):
    1. IEnumerator FixedTimeCoroutineThing ()
    2.     {
    3.  
    4.         // Now do your thing here
    5.         MonsterSkills tempSkills;
    6.         MonsterData tempAttacker = attacker;
    7.         int placeNum = tempAttacker.placeNum;
    8.      
    9.      
    10.         Debug.Log ("HI");
    11.  
    12.         YieldInstruction waitForFixedUpdate = new WaitForFixedUpdate();
    13.         while (true) {
    14.          
    15.             // Your per-update logic goes here
    16.          
    17.             // This for loop is equivalent to WaitForSeconds
    18.             // but uses fixed/physics timestep instead of
    19.             // the variable one used for non-essential/loose
    20.             // game elements and graphics.
    21.             Debug.Log ("INSIDE WAIT for fiX UPDATE");
    22.  
    23.             for (float duration = 1f; duration > 0; duration -= Time.fixedDeltaTime) {
    24.                 yield return waitForFixedUpdate;
    25.             }
    26.         }
    27.     }
    It keeps looping infinitely in the second debug.log every 1 second, am I doing something wrong here? I am not understanding how to convert this over properly.
     
  8. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Eh? That is exactly what the code is written to do. It is a deterministic loop that will do the stuff in the while loop above the for loop once every second. Even if the fps is going all over the place.

    Look at the for loop. duration starts at 1 then it is set to duration - fixedDeltaTime until it is 0 or less. The for loop just uses a WaitForFixedUpdate to wait for the next physics update before looping in the for loop

    If you set duration = 2 then your "per-update" logic will be executed once every two seconds.

    What do you expect it to do?
     
  9. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    I just want it to launch after 1 second only once and not be fps dependent which is why I am changing to waitForFixedUpdate.
     
  10. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    ........ This is why just copying and pasting code without actually looking at it never works.

    If you read the code, you would understand that you need to get rid of the while loop. Put the for loop at the top of the method and write the code you want to happen after the for loop.

    Which will make it do the following:

    Corotuine starts -> waits for 1 second (using a more detremistic way. Using fixed update) -> your code runs -> corotine finishes.
     
  11. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    You are 100% correct and it works perfectly now. Embarrassing mistake of not reading code :( Makes sense on why it loops when there is a while loop...

    Last question does it makes sense to run a battle system around timers? Just looking for some small advice.
    Ex.

    THEORY BELOW
    Read Attack function(){
    - Timer for first attack to be called
    - Timer for next attacks to be called
    - Timer for monster to play his animation
    - Timer for monster to stop playing his animation

    - Timer till next monster starts it's attack and will come back to start of this


    ( All timer's would be coroutines that would read variables and start at times from the variables and may just call a function )
    }

    I don't know how if this idea would be good or not long term, and if user would "pause" the game, or being an app would minimize it to play other apps and come back later and if it would continue on properly, or work under lag properly..
     
  12. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    I think its a good system. I have something similar - but for realtime combat where the monster has a list of actions (move, attack, reload, hide, run etc) and the AI just updates the list of action to do but the loop looks exactly the same - picking stuff off the list in order, executing it and waiting until animations, execution etc is finished.

    I suppose the only advice I could really add would be - dont code the animation time in the attack function. Read it out of the animations if possible, or use animation states and function calls from the animator (it is bit of an advanced topic but it allows the animation to invoke methods on your objects at points in the animation. The animaton defines the method signiture and your assign a method to it. kinda like the UI onclick stuff works) are you using the mechanim for animatoon? with animator controllers etc?

    Pausing is a different beast altogether. Im not sure if coroutines are effected by setting simulation time to 0. Probably worth another thread about pausing! There are lots of techniques to it.
     
  13. Azmar

    Azmar

    Joined:
    Feb 23, 2015
    Posts:
    246
    Hey thanks again for reply, you been very helpful :) I feel much better now lol. I do use the animator and it does seem like a good idea that I will most likely have to move everything over to that later, but it is an advanced topic for me right now and I still lack any models ( using cubes for everything ). I will have to do more research on pausing and what that does.