Search Unity

Ability Cooldowns?

Discussion in 'Scripting' started by Cooper37, May 3, 2017.

  1. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Hi, Unity Community! My question today is not too much of "what" to program, but a specific way of "how" to program. In the game I'm working on, the player will have abilities and ultimates that'll have cooldowns within them such as games like Heroes of the Storm. Right now, in my script, I have multiple timers and timeStamps for each ability, and this gets my code a bit confusing. I figured that there has to be some simpler "object-oriented" way.

    Lets say I have the following variables:
    Code (csharp):
    1.  
    2. abilityTime = 4f;    //The time the ability last
    3. abilityCooldown = 12f;   //The time you'll have to wait until you use the ability again
    4.  
    Right now I have Couroutines and IEnumerators everywhere which makes it a mess. I'd like to use functions in simple ways like:
    Code (csharp):
    1.  
    2. Ability(); //Use ability
    3. Cooldown(abilityTime); //Ability timer starts
    4. Cooldown(abilityCooldown); //When timer ends, the cooldown starts
    5. //From here, the player will be able to loop back up and use Ability() again
    6.  
    Depending on the character/ability, the cooldown may start as soon as the player uses the ability
    Code (csharp):
    1.  
    2. Ability(); //Use ability
    3. Cooldown(abilityCooldown); //Start cooldown for next time
    4.  
    Ultimates or special power abilities may work a bit differently. Unlike abilities, when the game starts, players will have to wait a certain amount of time before using the Power for the first time.
    Code (csharp):
    1.  
    2. ultTime = 8f; //The time the power last
    3. ultCooldown = 90f; //The time you'll have to wait until you can use the power
    4.  
    5. //When the game starts...
    6. Cooldown(ultCooldown);
    7. UltimateAbility();
    8. Cooldown(ultTime);
    9. //From here, it'll go back up and wait ultCooldown
    10.  
    Is there anyone who can guide me in structuring the functions like this, or what to have in the cooldown functions? It's pretty tough in C# since you have to use Couroutines and then continue the abilities within those. It gets messy and confusing. I get that this question is pretty vague, so please reply with questions so I can provide more clarity. Thank you for any help!!
     
  2. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Here's how I'd do it.

    Have a class that serves as a container for abilities instances. For your particular case, It should hold the ability's index, its initial cooldown time(for ults), its regular cool down time and then whatever the hell you want. Duration time possibly? You stated you might want that.

    Anyways, Start your coroutine. Feed it the container instance of the ability that's cooling down, along with an enum parameter, or whatever design you fancy, as long as you're able to differentiate what cooldown type it should fetch.

    At launch, I'd just feed all the abilities to the coroutine. All of those that have an initial cooldown time of 0 will refresh right away and the ultimates that have an initial cooldown will trigger it.
    Code (CSharp):
    1. //Enum to pass to the coroutine so it knows which cooldown type it should fetch
    2. enum CooldownType
    3. {
    4.     Initial,
    5.     Regular
    6. }
    7.  
    8. IEnumerator CooldownAbility (Ability instance, CooldownType cooldownType)
    9. {
    10.     DeactivateAbility (instance);
    11.     //Wait for the proper cooldown then reactivate the ability
    12.     yield return new WaitForSeconds (GetCooldownTime(instance, cooldownType));
    13.     ActivateAbility (instance);
    14. }
    15.  
    16. float GetCooldownTime (Ability instance, CooldownType cooldownType) {
    17.     switch (cooldownType) {
    18.     case CooldownType.Initial:
    19.         //Get the initial cooldown time that you set in this specific ability's container
    20.         return instance.GetInitialCooldownTime ();
    21.     default :
    22.         //Get the normal cooldown time that you set in this specific ability's container
    23.         return instance.GetCooldownTime ();
    24.     }
    25. }
    26.  
    27. void DeactivateAbility (Ability instance)
    28. {
    29.     //Deactivate ability based on its index stored in the Ability class
    30. }
    31. void ActivateAbility (Ability instance)
    32. {
    33.     //Activate ability based on its index stored in the Ability class
    34. }

    Edit : Additionally, you should label the types of abilities to differentiate which ones the cd should start as the ability is activated, which ones should wait for the ability duration before cooling down, whatever else you want them to do. Then segment your coroutine to be able to process different logics based on what the label of the ability is.

    e.g. In a case where you need to wait for the ability's duration before cooling down, your coroutine should get that information from the ability's container, then proceed to wait for the duration time conveniently stored in the ability's container, then trigger the cooldown.

    Basically, you want a coroutine that can process multiple logics based on the information stocked in the abilities you feed it.
     
    Last edited: May 4, 2017
    astracat111 likes this.
  3. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    I would actually use state machines for abilities. You don't HAVE to use couroutines in C#, it's just that most people do.

    Code (CSharp):
    1. public class Player_Base() {
    2.  
    3. public State { Idle, ActivateAbility }
    4. public State state;
    5.  
    6.    switch (state) {
    7.  
    8.    case Idle:
    9.       //wait for user input
    10.    break;
    11.  
    12.    case ActivateAbility:
    13.       this.gameObject.AddComponent(Abilities_FlameThrowerAttack);
    14.       state = State.Idle;
    15.    break;
    16.  
    17.    }
    18.  
    19. }
    Code (CSharp):
    1. public class Abilities_FlameThrowerAttack() {
    2.  
    3. public State { Start, Run, Cooldown, End }
    4. public State state;
    5. public int cooldownTimer;
    6.  
    7. void Update() {
    8.  
    9.   switch (state) {
    10.  
    11.   case Start:
    12.      cooldownTimer = something compared to players stats
    13.      state = State.Run;
    14.   break;
    15.  
    16.   case Run:
    17.      //Start Animation Here
    18.      //Animation itself will do
    19.      //Abilities_FlamThrowerAttack.state = State.Cooldown;
    20.    break;
    21.  
    22.   case Cooldown:
    23.      if (cooldownTimer <= 0) {
    24.         state = State.End;
    25.      }
    26.      cooldownTimer--;
    27.   break;
    28.  
    29.   case End:
    30.      Destroy(this) //destroys the script
    31.   break;
    32.   }
    33.  
    34. }
    And for the cooldown timer you're gonna want to multiply by Time.deltaTime. I THINK that means you do like:

    cooldownTimer = cooldownTimer - (1 * Time.deltaTime)......
     
    Last edited: May 4, 2017
  4. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Thank you guys for your help! I'll be trying some of these out and report back what I have.
     
  5. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    +1 for state machines. You could also use a mix of state machine and coroutines, where you use coroutines to "fire and forget" stuff like particles, etc.
     
    astracat111 likes this.
  6. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    Just remember when you use AddComponent, you can add an infinite amount of instances of the script to the game object. It creates a copy of your script and attaches it to your object. Using addcomponent then you can make scripts that get depleted or count down or whatever, just make sure it destroys itself aftewards if it's something you're gonna have an infinite amount of instances of.