Ability Cooldowns

Hello,

I am trying to add a cooldown image for my roll ability to my UI but I am having trouble. I have two scripts PlayerControls and RollColldownUI. The roll cool down currently works but I cannot get the Canvas to display the cooldown.

Question 1: Is this the best way to handle an ability cooldown or is there a better way to do it?

Question 2: How can I get the UI cooldown to work by referencing PlayerControls?

9890976--1427685--counterIMG.PNG
9890976--1427688--counterSetUp.PNG

public class PlayerControl : MonoBehaviour
{
    private RollCooldownUI rollCooldownUI;

    [HideInInspector] public bool isPlayerRolling = false;
    [SerializeField] public float rollCoolDown = 3f;
    private float nextRollFireTime = 0;

private void Awake()
{
    // Load Component
    rollCooldownUI = GetComponent<RollCooldownUI>();
}

private void Update()
{
    if (rollCoolDown > 0)
    {
        rollCoolDown -= Time.deltaTime;
    }
}

public void Roll(InputAction.CallbackContext context)
{
    if (context.performed)
    {
        if (rollCoolDown <= 0)
        {
            rollCoolDown = 3f;

            isPlayerRolling = true;

            if (moveVector.x != 0 || moveVector.y != 0)
            {
                animator.SetBool("IsRolling", true);
                state = State.DodgeRollSliding;
                slideDir = moveVector;
                rollSpeed = movementDetails.rollSpeed;

                //if (staminaBar != null)
                // {
                //Debug.Log("Stamina Roll");
                //StaminaBar.instance.UseStamina(rollSpeed);
                // }
            }
        }
    }
    else
    {
        isPlayerRolling = false;
    }
}
}
using MoreMountains.Tools;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[DisallowMultipleComponent]
public class RollCooldownUI : MonoBehaviour
{
    private PlayerControl playerControl;
    public Image imageCooldown;
    public TextMeshPro countDownText;



    private void Awake()
    {
        playerControl = GetComponent<PlayerControl>();
    }

    private void Start()
    {
        imageCooldown.fillAmount = 0.0f;
    }

    private void Update()
    {
        //ApplyCooldown();
    }

    public void ApplyCooldown()
    {
        Debug.Log("ApplyCooldown Accessed");
        imageCooldown.fillAmount = 1;
        /*
        if (playerControl.rollCoolDownTime < 0.0f)
            {
                Debug.Log("rollCoolDown < 0");
                playerControl.isPlayerRolling = false;
                imageCooldown.fillAmount = 0.0f;
            }
        else
            {
                Debug.Log("rollCoolDown active");
                imageCooldown.fillAmount = playerControl.rollCoolDownTime / playerControl.rollCoolDownTime;
            }*/
       
    }
}

Doing cooldowns with just a simple float in a small object above is perfect.

You probably just want to make an API for It and make all the guts private.

Things the API might say are:

  • reset cooldown (start of level)
  • set cooldown (eg, spell has been cast)
  • am I cooled? (eg, can I cast?)
  • what fraction of cooldown am I? (0 to 1 so you can display a bar)

And then since it is a MonoBehaviour, it should just decrement itself. Don’t splatter code to update it internally all over the place, ESPECIALLY not in UI. UI code should just present stuff, certainly never modify anything.

ALSO: don’t have a bool. Just make it part of your API, perhaps a property that informs external users about the internal state of the timer, for instance:

public bool IsCooling { get { return coolTimer > 0; } }

As to why what you have doesn’t work, it sounds like you probably just have a bug. Time to start debugging!

By debugging you can find out exactly what your program is doing so you can fix it.

Use the above techniques to get the information you need in order to reason about what the problem is.

You can also use Debug.Log(...); statements to find out if any of your code is even running. Don’t assume it is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

Cooldown timers, gun bullet intervals, shot spacing, rate of fire:

Thanks, I made some progress. I didn’t understand how to build an API using Unity so I started messing around with the RollCooldownUI.cs. It seems to work except I get a,
NullReferenceException: Object reference not set to an instance of an object
RollCooldownUI.Update, This is happening because my RollCooldownUI is refrencing the PlayerNew Player controls and not the clone that is created when the game is launched. How can I make the RollCooldownUI look at the clone being created?

using MoreMountains.Tools;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[DisallowMultipleComponent]
public class RollCooldownUI : MonoBehaviour
{
    public PlayerControl playerControl;
    [SerializeField] private Image imageCooldown;
    //public TextMeshPro countDownText;
    private float waitTime = 3f;


    private void Awake()
    {
        playerControl = GetComponent<PlayerControl>();
    }

    private void Start()
    {
        imageCooldown.fillAmount = 0.0f;
    }

    private void Update()
    {
        if (playerControl.isPlayerRolling == true)
        {
            Debug.Log("playerControl Accessed by RollCooldownUI");
            imageCooldown.fillAmount = 1;
            // reduce image fill
            imageCooldown.fillAmount -= 1.0f / waitTime * Time.deltaTime;
        }            
    }

    public void ApplyCooldown()
    {
        imageCooldown.fillAmount = 1f;
        //imageCooldown.fillAmount = playerControl.rollCoolDown;
        Debug.Log(playerControl.rollCoolDown);

        /*
        if (playerControl != null)
        {
            imageCooldown.fillAmount = playerControl.rollCoolDown / playerControl.nextRollFireTime;
            Debug.Log(imageCooldown.fillAmount);
        }*/

        /*
        if (playerControl.rollCoolDownTime < 0.0f)
            {
                Debug.Log("rollCoolDown < 0");
                playerControl.isPlayerRolling = false;
                imageCooldown.fillAmount = 0.0f;
            }
        else
            {
                Debug.Log("rollCoolDown active");
                imageCooldown.fillAmount = playerControl.rollCoolDownTime / playerControl.rollCoolDownTime;
            }
        */
    }
}

9897036--1429137--Capture.PNG
9897036--1429140--knightclone.PNG
9897036--1429143--playerNew.PNG
9897036--1429146--playerselections.PNG

The reference is returned by Instantiate();

Keep the reference and use it.

Remember:

The answer is always the same… ALWAYS!

How to fix a NullReferenceException error

https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

Three steps to success:

  • Identify what is null ← any other action taken before this step is WASTED TIME
  • Identify why it is null
  • Fix that

Thanks for this. Unfortunately being new to this, I am at a loss on how to reference the clone maybe I set it up incorrectly. The clone is coming from my Game Manager script.

public class GameManager : SingletonMonobehaviour<GameManager>
{
private PlayerDetailsSO playerDetails;
private PlayerControl playerControl;
private Player player;

protected override void Awake()
{
    base.Awake();

    playerDetails = GameResources.Instance.currentPlayer.playerDetails;

    input = new CustomInputs();

    InstantiatePlayer();
}

private void InstantiatePlayer()
{
    GameObject playerGameObject = Instantiate(playerDetails.playerPrefab);

    player = playerGameObject.GetComponent<Player>();

    player.Initialize(playerDetails);
}
}

This is using my PlayerDetailsSO script to get the current Player selected.

public class PlayerDetailsSO : ScriptableObject
{
   public GameObject playerPrefab;
}

My PlayerControl script references the PlayerDetails but the isPlayerRolling bool is in PlayerControls not PlayerDetailsSO. So I am wondering if I need to move that into PLayerDetailsSO to be able to access the bool. As you can see I am lost.

9900525--1429974--PCPD.PNG

Whether you have a one-script or a ten billion layer GameManager nightmare going on, the same three steps apply.

Maybe the null isn’t even anything related to your manager. Find out!

Maybe your manager isn’t even running! Find out!

But you ALWAYS have to start with finding what is null, otherwise you’re chasing ghosts.

How to fix a NullReferenceException error

https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

Three steps to success:

  • Identify what is null ← any other action taken before this step is WASTED TIME
  • Identify why it is null
  • Fix that