Search Unity

How to use the new StopCoroutine(IEnumerator)?

Discussion in 'Scripting' started by 5argon, Jun 3, 2014.

  1. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I started a coroutine that takes a Transform as one parameter. When I wanted to stop this using the new StopCoroutine what am I supposed to use as a parameter? I wanted to stop so the parameter isn't even relevant. But no matter what I do, the coroutine won't stop.

    This is my coroutine function. I planned to call StopCoroutine to break from that while(true) loop. The while true loop acts as an Update() function with controllable update frequency.

    StartCoroutine(LoseEscapeRoutine(transform));
    and so..
    StopCoroutine(LoseEscapeRoutine(????));

    Code (CSharp):
    1. IEnumerator LoseEscapeRoutine(Transform transform)
    2.     {
    3.         int i = transform.position.x > 0 ? 1 : -1;
    4.  
    5.         while(true)
    6.         {
    7.             Vector3 v = transform.position;
    8.             v.x += 0.1f * i;
    9.             transform.position = v;
    10.             yield return new WaitForSeconds(0.05f);
    11.         }
    12.     }
     
    rakkarage likes this.
  2. laurelhach

    laurelhach

    Joined:
    Dec 1, 2013
    Posts:
    229
    You need to call it that way :
    Code (csharp):
    1.  StopCoroutine("LoseEscapeRoutine");
    You can't stop the coroutine using your way. It has to be called using the method's string name.
    Here is the quote :
     
  3. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Yeah I did saw that.. so what is the point of the new StopCoroutine(IEnumerator) that came with 4.5? Can it only be used with parameter-less function? The documentation did not mention any...
     
  4. Deleted User

    Deleted User

    Guest

    Code (csharp):
    1.  
    2. // variable that holds reference to coroutine (IEnumerator)
    3. public IEnumerator routine;
    4.  
    5. // get IEnumerator from Coroutine and start
    6. routine = DoWork();
    7. StartCoroutine( routine );
    8.  
    9. // stop Coroutine
    10. StopCoroutine( routine );
    11.  
    12.  
     
    Novack, AbgaryanFX, rakkarage and 2 others like this.
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    You would think they would have added a StopCoroutine that accepted the 'Coroutine' object itself, as opposed to the enumerator that the coroutine operates on.

    I'm still sticking to my RadicalCoroutine system I set up that is cancellable, and also has a way to implement your own custom YieldInstructions.
     
  6. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Ah, that hits the spot!
    So the IEnumerator is a representation of yielded function. I always put the whole function in the StartCoroutine without knowing that. Thank you.
     
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    well... technically it is.

    A function that has yield commands in it is an 'iterator function'. All iterator functions are enumerable, so they must return an IEnumerator or IEnumerable.

    'iterator functions' and the yield keyword are a .Net/mono defined thing. Unity just uses them to iterate over once per frame.

    It's not that an IEnumerator is a representation of a yielded function [sic]... it's that a iterator function is compiled into an anonymous object that implements both IEnumerable and IEnumerator. It's syntax sugar.

    Oh, also note that when writing C# which is type safe, if your iterator function returns IEnumerable, you'll have to call 'GetEnumerator' or cast to IEnumerator on the enumerable that comes out. The object implements both, but will come out typed as only one, and may throw a compiler error for type mismatch.
     
    5argon likes this.
  8. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    This new way really is better. It's more like the way everyone else handles parallel code. In other words, coming from a programming background, this new way is how I assumed they worked back in 3.0.

    The idea is, don't think of the coroutine on the page. Think of each copy of it. Say you have `IEnumerator fancyHighlight(Transfom T);` which does some sort of glow and bob. You might StartCoroutine this multiple times to flash multiple objects. So you don't want to stop `fancyHighlight`, you want to stop the exact fancyHighlight on one particular object.

    It's really the same idea as `GameObject gg = Instantiate();`. When you spawn, you want to get a "handle" to what you just made, so you can control or destroy it. Likewise, when you spawn a coroutine, you want that same handle:

    IEnumerator flashA, flashB;
    flashA=fancyHighlight(thingA);
    flashB=fancyHighlight(thingB);
    ... (start them)
    StopCoroutine(flashA); // I can pick the exact copy of the coroutine.

    FlashA might even be running a different coroutune. This lets me say "whatever you ran to make A flash, stop it."
     
  9. Perceive

    Perceive

    Joined:
    Aug 2, 2013
    Posts:
    10
    Just to let you know, this is why StopCoroutine(Coroutine routine) was added. Instead of making an IEnumerator instance for each coroutine, you just save the reference to each Coroutine that you start.
     
  10. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Can i use this to "yield return StartCoroutine(IEnumerator routine)"?

    I am building a little Coroutine-StateMachine but i have trouble controlling the coroutines. If i stop the Coroutine from outside, the yield will never return and the statemachine is basically stuck... See the Code for better explanation, basically i want that "1" stops curStateR from anywhere and the FSM should return from "yield return StartCoroutine(curStateR)" and print "Ended1"

    Here is a working Testcode of my FSM, dumbed down to the core issue:

    Code (csharp):
    1.  
    2. public enum States { State1, State2, None }
    3. private    States curState = States.State1;//2;    //current State
    4. private    IEnumerator curStateR;                    //current Coroutine
    5.  
    6. void Update () {  
    7.     //State1 Abort, doesn't print "Ended1"
    8.     if(Input.GetKeyDown("1")){
    9.         print ("aborted1 from outside");
    10.         curState = States.None;          
    11.  
    12.         StopCoroutine(curStateR);
    13.     }
    14.  
    15.     //State2 Abort, doesn't print "Ended2"
    16.     if(Input.GetKeyDown("4")){
    17.         print ("aborted2 from outside");
    18.         curState = States.None;
    19.      
    20.         StopCoroutine("Coroutine2");
    21.     }  
    22. }
    23.  
    24. private    IEnumerator FSM(){  
    25.     while (true){
    26.         //print("FSM-Iteration");
    27.         switch (curState) {                  
    28.             case States.State1:
    29.                 curStateR = Coroutine1();
    30.                 yield return StartCoroutine(curStateR);
    31.                 print("Ended1");
    32.             break;
    33.              
    34.             case States.State2:
    35.                 yield return StartCoroutine("Coroutine2");
    36.                 print("Ended2");
    37.             break;
    38.          
    39.             default:
    40.                 yield return null;
    41.             break;
    42.         }
    43.         yield return null;//Wait one frame for Input to reset
    44.     }
    45. }
    46.  
    47. private    IEnumerator Coroutine1(){              
    48.     while(true){      
    49.         print ("1 running");
    50.      
    51.         //those two will stop the Coroutine from within, "Ended1" will printed only for "2"
    52.         if(Input.GetKeyDown("2")){ curState = States.None; yield break;                }
    53.         if(Input.GetKeyDown("3")){ curState = States.None; StopCoroutine(curStateR);}
    54.         yield return null;
    55.     }
    56. }
    57.  
    58. private    IEnumerator Coroutine2(){              
    59.     while(true){
    60.         print ("2 running");
    61.      
    62.         //those two will stop the Coroutine from within, "Ended2" will printed only for "2"
    63.         if(Input.GetKeyDown("2")){ curState = States.None; yield break;                    }
    64.         if(Input.GetKeyDown("3")){ curState = States.None; StopCoroutine("Coroutine2");    }
    65.         yield return null;
    66.     }
    67. }
    68.  
    This guy shows the same problem:
    https://gist.github.com/michaelbartnett/58959d03a61de982d166
     
    Last edited: Jun 11, 2015
  11. UnityDevGuy

    UnityDevGuy

    Joined:
    Aug 18, 2015
    Posts:
    1
    In your code, the only place you ever instantiate the curStateR variable is within the "FSM" method, but there is no call to FSM, so your curStateR variable will always be null. Use the "Start" method within MonoBehavior to call FSM. And, as you have it written above, FSM should probably have a "void" return value.
     
  12. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    If you are looking for an alternative to such hybrid system. I would suggest to have a look at Behaviour Tree. It's really worth it. It could be a nice alternative to both FSM and coroutine, since it is tool to define logics that unfold over long period of time (several frames).

    Have a look at Panda BT (http://www.pandabehaviour.com/), it's a script-based Behaviour Tree framework.

    The advantages over FSM are:
    Flexibility: No need to define explicit transitions.
    Scalability: You can organized your logic into a tree of actions and sub-actions.
    Reusability: You can reuse part of whole of a BT to define other BTs.

    As a couroutine, a task (an action) runs over several frames.

    Adavantages of BT other coroutine are:
    Simplicity: No IEnumator, No yield instruction, No Start/Stop coroutine.
    Configurable: A BT tree can run on Update, FixedUpdate, LateUpdate or at your custom tick rate. Whereas coroutine runs only on Update.

    Also, with Panda BT, you can visualized the tree as it is running, which gives you detailed information about what's is actually going on with your system (extremely valuable for debugging).

    Please contact me if you have any question about this tool.
     
  13. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    The above example really was a dumbed down thing to show that "yield return StartCoroutine(curStateR)" never ever continues if "curStateR" is cancelled from outside itself, i think i carelessly removed the Start() before posting because i had some unrelated stuff in there, so it won't really work as posted, sorry

    Thanks, but i have overhauled my Statemachine to a pretty satisfying state. It also features a Queue so you can either switch state directly or just enqueue the next thing you want to do after your current state sequence has terminated into idle. I can post it if you like to see it.
     
  14. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    @Marrt
    I was not comparing BT to your specific implementation of FSM, but I was comparing it to FSM in general.

    Great that you've come up with a system that you're pretty satisfied with. Though, I would still advice to keep BT on your radar, it is really a great tool.

    I know that you are making an implementation to fit your needs, so I can understand that you are not applying a strict definition of FSM. But I must say that I am uneasy calling it an FSM, since, by definition, there is no such thing as a queue or switching state directly; an FSM is just a set of states and a set of transitions. But I am curious to see your implementation. I would be even more interested to see how you use it. Let's say, how would you implement a simple FSM like this one?
     
    Last edited: Jun 9, 2016
  15. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    didn't read anything else but you need to start to the coroutine with a string and then to stop it, stop it with a string. It's stupid you can't pass through the actual routine object.
     
  16. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    This is how i would implement above statemachine with my current expertise, my TryActions&Queues(see below) are not needed here because all transition logic is located within the states, and not driven by external input when implemented.

    StateMachinePacman.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. using System;//Action
    6.  
    7. public    enum PacmanState        { Wander, Chase, Flee, Return};
    8.  
    9. public class StateMachinePacman : MonoBehaviour {
    10.              
    11. //STATEMACHINE
    12.     [HideInInspector]    public    PacmanState state = PacmanState.Wander;
    13.  
    14.     public    PacmanState stateSetter{
    15.         get {    return state;    }
    16.         set {
    17.             ExitState();
    18.             if(state != value){ if(stateChanged != null){ stateChanged(value); }}
    19.             state = value;
    20.             EnterState(state);
    21.         }
    22.     }
    23.  
    24.     private    IEnumerator curCoroutine;
    25.  
    26.     private    void ExitState(){
    27.         if(curCoroutine != null){    StopCoroutine(curCoroutine);    }
    28.  
    29.         //stuff that needs to be done before changing state
    30.         switch(state){
    31.             case PacmanState.Wander:        break;
    32.             case PacmanState.Chase:            break;
    33.             case PacmanState.Flee:            break;
    34.             case PacmanState.Return:        break;  
    35.         }
    36.         print ("exiting:\t"+System.Enum.GetName(typeof(PacmanState), state)+"\t at"+Time.time);  
    37.     }
    38.  
    39.     private    void EnterState(PacmanState newState){
    40.         print ("entering:\t"+System.Enum.GetName(typeof(PacmanState), state)+"\t at"+Time.time);
    41.         switch(newState){
    42.             case PacmanState.Wander:    curCoroutine    = PerformWander();    StartCoroutine( curCoroutine );        break;
    43.             case PacmanState.Chase:        curCoroutine    = PerformChase();    StartCoroutine( curCoroutine );        break;
    44.             case PacmanState.Flee:        curCoroutine    = PerformFlee();    StartCoroutine( curCoroutine );        break;
    45.             case PacmanState.Return:    curCoroutine    = PerformReturn();    StartCoroutine( curCoroutine );        break;
    46.         }
    47.     }
    48. //
    49.  
    50. //NOTIFIERS
    51.     public    event Action<PacmanState> stateChanged;
    52.     public    event Action died;
    53. //
    54.      
    55.     void Start(){  
    56.         stateSetter = PacmanState.Wander;
    57.     }
    58.  
    59.  
    60.     #region PerformActions
    61.     private    IEnumerator PerformWander(){          
    62.         while(true){
    63.             //DO STUFF
    64.                  
    65.             //normally we would yield at the end, but we use keyinput here, so we have to yield before checking keys, otherwise the input would be still be valid for new state
    66.             yield return null;
    67.             //state transition event simulated by keyInput:
    68.             if(Input.GetKeyDown("1")){    print("X");        }
    69.             if(Input.GetKeyDown("2")){    print("->chase");    stateSetter = PacmanState.Chase;    }  
    70.             if(Input.GetKeyDown("3")){    print("->flee");    stateSetter = PacmanState.Flee;        }  
    71.             if(Input.GetKeyDown("4")){    print("X");        }      
    72.         }
    73.     }
    74.     private    IEnumerator PerformChase(){          
    75.         while(true){
    76.             //DO STUFF
    77.      
    78.             yield return null;
    79.             if(Input.GetKeyDown("1")){    print("->wander");    stateSetter = PacmanState.Wander;    }
    80.             if(Input.GetKeyDown("2")){    print("X");        }  
    81.             if(Input.GetKeyDown("3")){    print("->flee");    stateSetter = PacmanState.Flee;        }  
    82.             if(Input.GetKeyDown("4")){    print("-X");    }
    83.         }
    84.     }
    85.     private    IEnumerator PerformFlee(){          
    86.         while(true){
    87.             //DO STUFF
    88.      
    89.             yield return null;
    90.             if(Input.GetKeyDown("1")){    print("->wander");    stateSetter = PacmanState.Wander;    }
    91.             if(Input.GetKeyDown("2")){    print("->chase");    stateSetter = PacmanState.Chase;    }  
    92.             if(Input.GetKeyDown("3")){    print("X");        }  
    93.             if(Input.GetKeyDown("4")){    print("->return");    stateSetter = PacmanState.Return;    }
    94.         }
    95.     }
    96.     private    IEnumerator PerformReturn(){          
    97.         while(true){
    98.             //DO STUFF
    99.      
    100.             yield return null;
    101.             if(Input.GetKeyDown("1")){    print("->wander");    stateSetter = PacmanState.Wander;    }
    102.             if(Input.GetKeyDown("2")){    print("X");        }  
    103.             if(Input.GetKeyDown("3")){    print("X");        }  
    104.             if(Input.GetKeyDown("4")){    print("X");        }
    105.         }
    106.     }
    107.  
    108.     #endregion
    109. }
    110.  

    Adding Queues:
    After your remarks i am afraid my contraption it is not really an FSM. It controls a subject in the game and has states and commands:
    - States are Coroutines that the subject currently performs, so i named them with the prefix "Perform" like "private IEnumerator PerformJump".
    - Commands are ways for ordering the subject to do smth, but letting the Subject check for itself if it is possible. They return a bool and are named with the prefix "Try" like "public bool TryJump"

    You can control my StateMachine by:
    • setting the variable "stateSetter" to any MachineState (this is an enum) OR
    • using one of the try-functions like TryMelee to command the subject to perform a MeleeAttack. These try-functions determine if the action of state-switching will be enqueued, performed immediately or is forbidden (which returns false)
    This way i can make a normal state-machine where every transition is programmed right into each state. Or i can manually ask for a state change from the outside by using the try-functions which contain more general rules to check if the subject is currently allowed to enter a state.

    This is the barebone version of my current script, stripped of all special functionality used for my Coded Abilities that require other stuff from my project like Melee, Spells and MovementForces and { Jump, Focus, Melee, Aim, Shoot, Stagger, Stun, Dead} and grounded-checks. Also changed some things like the Hit function which normally takes an attack instance instead of raw dmg

    StateMachine.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;//List
    5.  
    6. using System;//Action
    7. //controls actor by inputs like PerformAttack or move and sets the corresponding animations, must have no link to the Game-UI, status indication is on-object
    8.  
    9.  
    10. //Add Statechange Events for player character that are catched by Main-UI (OnFocus...)
    11.  
    12. public    enum MachineState        { None, Idle, Jump, Dash, Block, Cast, Melee, Stagger, Dead};
    13.  
    14. //Actor Controller, driven by Input through TryXXXX() functions and steerVector
    15. //Inconsistency: is that Input is gathered in Update, and Timers run in Fixed Update, so first executionFrame is done in Update, following in Fixed
    16. //    - this could be changed by adding a WaitForFixedUpdate() before starting execution but that adds at least one render frame delay until input can be seen
    17.  
    18. public class StateMachine : MonoBehaviour {
    19.              
    20. //STATEMACHINE
    21.     [HideInInspector]    public    MachineState state = MachineState.None;
    22.  
    23.     public    MachineState stateSetter{
    24.         get {    return state;    }
    25.         set {
    26.             ExitState();
    27.             if(state != value){ if(stateChanged != null){ stateChanged(value); }}
    28.             state = value;
    29.             EnterState(state);
    30.         }
    31.     }
    32.  
    33.     private    IEnumerator curCoroutine;
    34.  
    35.     private    void ExitState(){
    36.         if(curCoroutine != null){    StopCoroutine(curCoroutine);    }
    37.  
    38.         //stuff that needs to be done before changing state
    39.         switch(state){
    40.             case MachineState.None:        break;
    41.             case MachineState.Idle:        break;
    42.             case MachineState.Jump:        break;
    43.             case MachineState.Dash:        break;
    44.             case MachineState.Block:    break;
    45.             case MachineState.Cast:        break;
    46.             case MachineState.Melee:    break;
    47.             case MachineState.Stagger:    break;
    48.             case MachineState.Dead:        break;      
    49.         }
    50.         print ("exiting:\t"+System.Enum.GetName(typeof(MachineState), state)+"\t at"+Time.time);  
    51.     }
    52.  
    53.     private    void EnterState(MachineState newState){
    54.         print ("entering:\t"+System.Enum.GetName(typeof(MachineState), state)+"\t at"+Time.time);
    55.         switch(newState){
    56.             case MachineState.None:        break;
    57.             case MachineState.Idle:        curCoroutine    = PerformIdle();    StartCoroutine( curCoroutine );    break;
    58.             case MachineState.Jump:        break;
    59.             case MachineState.Dash:        break;
    60.             case MachineState.Block:    break;
    61.             case MachineState.Cast:        break;
    62.             case MachineState.Melee:    break;
    63.             case MachineState.Stagger:    actionQueue.Clear();    break;
    64.             case MachineState.Dead:        actionQueue.Clear();    break;
    65.         }  
    66.     }
    67. //
    68.  
    69. //NOTIFIERS
    70.     public    event Action<MachineState> stateChanged;
    71.     public    event Action died;
    72. //
    73.  
    74. //ACTION QUEUE
    75.     //Rules:
    76.     //    Dash and Block interrupt all Actions even Dash itself
    77.     //    Melee, Casts should have a shared timeout, preventing high speed Melee->Dash->Melee->Dash... attack speeds caused by mutually interruptable states
    78.  
    79.     private    int    maxQueueSize = 1;    //increase due to preference, for user input controlled machine keep the queue size 1 and always overwrite the contained item with the last user input if the stack is not empty
    80.     private    Queue<QueueAbleAction> actionQueue = new Queue<QueueAbleAction>();    //Action Queue
    81.  
    82.     public    class QueueAbleAction{
    83.         public    MachineState    type;
    84.         public    Action                action;
    85.         public    QueueAbleAction(MachineState type, Action action){
    86.             this.type = type;
    87.             this.action = action;
    88.         }
    89.     }
    90. //
    91.  
    92. //    void Awake(){
    93. //    }
    94.  
    95.     void Start(){
    96.  
    97.         stateSetter = MachineState.Idle;
    98.  
    99. //        TickManager.instance.lateUpdateTick1    += new System.Action(LateTick1);
    100. //        TickManager.instance.lateUpdateTick2    += new System.Action(LateTick2);
    101.     }
    102.  
    103.     void OnDestroy(){
    104. //        TickManager.instance.lateUpdateTick1    -= new System.Action(LateTick1);
    105. //        TickManager.instance.lateUpdateTick2    -= new System.Action(LateTick2);
    106.     }
    107.  
    108.     void Update(){          
    109.         if(Input.GetKeyDown("1")){    print("Trying Dash");    TryDash(Vector3.forward);    }  
    110.         if(Input.GetKeyDown("2")){    print("Trying Block");    TryBlock(Vector3.forward);    }  
    111.         if(Input.GetKeyDown("3")){    print("Trying Melee");    TryMelee(Vector3.forward);    }  
    112.         if(Input.GetKeyDown("4")){    print("Dealing 1DMG");    Hit(1,Vector3.forward);        }
    113.     }
    114.  
    115. //    void LateUpdate(){  
    116. //    }
    117. //    void LateTick1(){  
    118. //    }
    119. //    void LateTick2(){  
    120. //    }
    121.  
    122.     private    int hp = 3;
    123.  
    124.     //this gets called when subject is hit
    125.     public    bool Hit( int dmg, Vector3 dir, Vector3? sourceDir = null){
    126.         if(state != MachineState.Dead){
    127.      
    128.             if(state != MachineState.Block){
    129.          
    130.                 hp -= dmg;
    131.          
    132.                 if(hp < 1){
    133.                     stateSetter        = MachineState.Dead;
    134.                     if(died!=null){died();}                          
    135.                     return true;
    136.                 }else{              
    137.                     stateSetter        = MachineState.Stagger;
    138.                     return false;
    139.                 }
    140.          
    141.             }
    142.      
    143.         }
    144.  
    145.         return false;
    146.     }
    147.  
    148.     private    IEnumerator PerformIdle(){
    149.         while(true){      
    150.             //check for Actions in Queue
    151.             if(actionQueue.Count>0){          
    152.                 MachineState type = actionQueue.Peek().type;
    153.          
    154.                 //you can perform special checks like time delay between melee attacks or such things before dequeuing here -> if( type == MachineState.Melee && meleeCastTimeOutRest != 0)...
    155.                          
    156.                 print ("dequeued "+System.Enum.GetName(typeof(MachineState), type)+" from Idle() at "+Time.time);          
    157.                 actionQueue.Dequeue().action.Invoke();      
    158.             }      
    159.             yield return new WaitForFixedUpdate();
    160.         }
    161.     }
    162.  
    163.     #region TryActions
    164.          
    165. //DASH
    166.     public    bool TryDash(Vector3 dir){
    167.          
    168.         if( state == MachineState.Dead){    return false;    }
    169.  
    170.         bool interrupt = true;    //Dash interrupts all actions
    171.  
    172.         if(interrupt){    actionQueue.Clear();    }
    173.          
    174.         actionQueue.Enqueue(
    175.             new QueueAbleAction( MachineState.Dash,
    176.                 ()=>{
    177.                     stateSetter        = MachineState.Dash;
    178.                     curCoroutine    = PerformDash(dir);
    179.                     StartCoroutine( curCoroutine );
    180.                 }
    181.             )
    182.         );  
    183.  
    184.         //interrupt to Idle, Idle() checks for Dequeue, so this happens this very frame, not next frame
    185.         if(interrupt){        stateSetter        = MachineState.Idle;}
    186.      
    187.         return false;
    188.     }
    189.  
    190. //BLOCK
    191.     public    bool TryBlock(Vector3 dir){
    192.         if( state == MachineState.Dead)            {    return false;    }
    193.          
    194.         bool interrupt = true;    //Block interrupts all actions  
    195.  
    196.         if(interrupt){    actionQueue.Clear();    }
    197.          
    198.         actionQueue.Enqueue(
    199.             new QueueAbleAction( MachineState.Block,  
    200.                 ()=>{
    201.                     stateSetter        = MachineState.Block;    //stops curCoroutine via set function
    202.                     curCoroutine    = PerformBlock(dir);
    203.                     StartCoroutine( curCoroutine );
    204.                 }
    205.             )
    206.         );  
    207.          
    208.         if(interrupt){        stateSetter        = MachineState.Idle;}
    209.          
    210.         return true;
    211.     }
    212.  
    213.  
    214. //MELEE
    215.     public    bool TryMelee(Vector3 dir){
    216.  
    217.         if( state == MachineState.Dead)            {    return false;    }
    218.  
    219.         bool interrupt = false;
    220.          
    221.         interrupt = state == MachineState.Dash || state == MachineState.Block;//can interrupt Block and Dash, otherwise queues
    222.         if(interrupt){        actionQueue.Clear();    }
    223.  
    224.         if(actionQueue.Count == maxQueueSize){    return false;    }
    225.  
    226.         actionQueue.Enqueue(
    227.             new QueueAbleAction( MachineState.Melee,  
    228.                 ()=>{
    229.                     stateSetter        = MachineState.Melee;    //stops curCoroutine via set function
    230.                     curCoroutine    = PerformMelee(dir);
    231.                     StartCoroutine( curCoroutine );
    232.                 }
    233.             )
    234.         );
    235.          
    236.         //interrupt PerformChargeAction
    237.         if(interrupt){        stateSetter        = MachineState.Idle;}
    238.          
    239.         return true;
    240.     }
    241.  
    242.  
    243. //CAST
    244.     public    bool TryCast(Vector3 dir){
    245.  
    246.         if( state == MachineState.Dead )        {    return false;    }
    247.  
    248.         bool interrupt = state == MachineState.Dash || state == MachineState.Block;//can interrupt Block and Dash
    249.  
    250.         if(interrupt){    actionQueue.Clear();    }  
    251.  
    252.         if(actionQueue.Count == maxQueueSize){    return false;    }
    253.  
    254.         actionQueue.Enqueue(
    255.             new QueueAbleAction( MachineState.Cast,  
    256.                 ()=>{
    257.                     stateSetter        = MachineState.Cast;    //stops curCoroutine via set function
    258.                     curCoroutine    = PerformCast(dir/*, attack*/);
    259.                     StartCoroutine( curCoroutine );
    260.                 }
    261.             )
    262.         );
    263.  
    264.         //interrupt PerformChargeAction
    265.         if(interrupt){        stateSetter        = MachineState.Idle;}
    266.  
    267.         return true;
    268.     }
    269.  
    270.     #endregion
    271.  
    272.     #region PerformActions
    273.  
    274.     //ACTUAL JUMP (currently no manual jump, its to unhandy for touch)
    275.     private    IEnumerator PerformJump(Vector3 dir){
    276.          
    277.         //Do a Jump e.g:
    278.             //this.GetComponent<RigidBody>().AddForce(Vector3.up * intensity, ForceMode.Impulse);  
    279.             //while(!grounded){        yield return new WaitForFixedUpdate();        }
    280.         yield return new WaitForFixedUpdate();
    281.         //at the end transition to Idle;
    282.         stateSetter = MachineState.Idle;
    283.  
    284.  
    285.     }
    286.  
    287. //BLOCK
    288.     private    IEnumerator PerformBlock(Vector3 dir){
    289.  
    290.         //Do a block for certain duration, rotate a shieldObject or whatever  
    291.         for(int i = 0; i<25; i++){    //0.5s
    292.             //shieldTrans.Rotate(new Vector3(-900F*Time.deltaTime,0F,0F));
    293.             yield return new WaitForFixedUpdate();
    294.         }          
    295.         stateSetter        = MachineState.Idle;
    296.     }
    297.  
    298. //DASH
    299.     private    IEnumerator PerformDash(Vector3 dir){
    300.         int dur = 50;
    301.         //Dash
    302.         for(int i = 0; i<dur; i++){      
    303.             //steerVector = dir.normalized *36F;                  
    304.             yield return new WaitForFixedUpdate();
    305.      
    306.         }
    307.  
    308.         stateSetter        = MachineState.Idle;
    309.     }
    310.  
    311. //MELEE
    312.     private    IEnumerator PerformMelee(Vector3 dir/*, Attack attack*/){
    313.  
    314.             //Do Attack
    315.  
    316.         //    attack.Invoke(dir);  
    317.             int dur = 50;//attack.duration;  
    318.             for(int i = 0; i<dur; i++){          
    319.                 yield return new WaitForFixedUpdate();
    320.             }
    321.  
    322.         stateSetter = MachineState.Idle;
    323.     }
    324.      
    325.     //SPELL
    326.     private    IEnumerator PerformCast(Vector3 dir/*, Attack attack*/){
    327.  
    328.             //Do Attack
    329.  
    330.         //    attack.Invoke(dir);  
    331.             int dur = 50;//attack.duration;  
    332.             for(int i = 0; i<dur; i++){          
    333.                 yield return new WaitForFixedUpdate();
    334.             }
    335.  
    336.         stateSetter = MachineState.Idle;
    337.     }
    338.      
    339.     #endregion
    340. }
    341.  

    i hope it is understandable, since i really tinkered a lot after the queue addition, i run it in FixedUpdate() but i am aware that user Inputs arrive in Update() meaning that the first iteration of any perform loop will occur in Update, but then continue in FixedUpdate(). I could add a WaitForFixedUpdate() at the begin of every Perform, but this would increase the input-lag by up to a frameLength

    Here is a small taste of what i am actually doing with it:
    aaaaaagif.gif
    and my actual file consisting out of 2k5 lines of tinker mayhem
     

    Attached Files:

    Last edited: Jun 9, 2016
    FaberVi likes this.