Expanding Action Bar Functionality

@Brian_Trotter

Bouncing around here… so I don’t know if this was changed in a lecture before, but there is no Github link for the changes to the Player Controller… namely looking for the action keys in Update and the special keys function…Not sure how to implement that so I wanted to study @sampattuzzi 's code

This was the closest commit I could find:

Bonus: Make the buttons clickable:

Make the following changes to ActionSlotUI
Add IPointerClickHandler to the list of Interfaces.
This will require a new method OnPointerClick:

public void OnPointerClick(PointerEventData eventData)
{
     store.Use(index, player);
}

This was on my to do list, thank you!

You seem up to challenges…
Bonus Challenge: Cooldowns. Make it so that once a button is clicked, it can’t be clicked again until the cooldown is expired.
Hint: You’re probably going to have to edit the original GameDev.tv scripts for this.
Hint2: You have to account for both the keyboard calling Actions AND the button click handler.

You’ve played World of Warcraft… GCD was on my to do list as well!

1 Like

Clickable Action Slots - got that one done… but I’m not sure where I’m supposed put code that overrides the virtual method Use depending on what the action item is… do I just add it ActionItem.cs where the virtual one is? Not sure I grasp virtual methods…

I started watching some vids on unity learn about overrides… I added some things to ActionItem.cs

using System;
using UnityEngine;

namespace GameDevTV.Inventories
{
    /// <summary>
    /// An inventory item that can be placed in the action bar and "Used".
    /// </summary>
    /// <remarks>
    /// This class should be used as a base. Subclasses must implement the `Use`
    /// method.
    /// </remarks>
    [CreateAssetMenu(menuName = ("GameDevTV/GameDevTV.UI.InventorySystem/Action Item"))]
    public class ActionItem : InventoryItem
    {
        // CONFIG DATA
        [Tooltip("Does an instance of this item get consumed every time it's used.")]
        [SerializeField] bool consumable = false;
        [SerializeField] bool health;
        [SerializeField] bool mana;
        [SerializeField] float amountToReplenish;

        // PUBLIC

        /// <summary>
        /// Trigger the use of this item. Override to provide functionality.
        /// </summary>
        /// <param name="user">The character that is using this action.</param>
        public virtual void Use(GameObject user)
        {
            Debug.Log("Using action: " + this);
        }

        public bool isConsumable()
        {
            return consumable;
        }
    }
}

basically so the item can identify itself as a “replenishable” and what to replenish and how much… and started scripting a Health Potion ActionItem… something that has an error (that amount to heal) because the param isn’t in Use()…

using System.Collections;
using System.Collections.Generic;
using RPG.Attributes;
using UnityEngine;

namespace GameDevTV.Inventories
{
    public class HealthPotion : ActionItem
    {
        Health player;

        private void Awake()
        {
            player = GameObject.FindGameObjectWithTag("Player").GetComponent<Health>();
        }

        public override void Use(GameObject user)
        {
            player.Heal(amountToHeal);
        }
    }
}

Not quite grasping how to wrangle this… or how I would call this potion script when store.item.Use(player) is called… how would anything know it’s supposed to call use on the potion script and not on the ActionItem.cs version of Use()?

public class ActionItem : InventoryItem
{
// CONFIG DATA
[Tooltip(“Does an instance of this item get consumed every time it’s used.”)]
[SerializeField] bool consumable = false;
[SerializeField] float IHealingAmount = 0;

    public virtual void Use(GameObject user)
    {
        if (IHealingAmount > 0)
        {
            GameObject.FindWithTag("Player").GetComponent<Health>().Heal(IHealingAmount);
        }
    }

    public bool isConsumable()
    {
        return consumable;
    }
}

I just added this to the ActionItem for the moment to see if I can get it to work and it does, I’m thinking for the override you may not have to call an Awake and just get the Player and health component. I plan to try the override too soon. I hope this helps or gives some ideas.

I see the clip didn’t come out right sorry!!

Sorry, it at computer atm
Hint
Leave ActionItem unchanged
Create ActionPotion.cs, make it inherit from ActionItem. Put all that stuff you added in there
Override Use

lol, I didn’t know to add the CreatAssetMenu also to be able to make the item I’m having override specifically. That did not cross my mind, I got it to work now thanks for the hint.

Yes, you’ll need a new CreateAssetMenu on each one.
Basically each “Action” will need it’s own ActionItem descendant. You can do clever things like you really only need one ActionPotion.cs, just changing the amount of healing with each ActionPotion you create.

circling back to this now after doing some audio work, did you figure out a structure for where overrides live and how to call them?

@Brian_Trotter
in fact, I still don’t get it… if this action script lives on every action item we create, why do we need a virtual method, and where is supposed to be overridden from?.. the script is on the item. I’m not understanding the concept of what the intended structure is if the script with the virtual method lives on the item.

This is how I did one, what was missing was the CreateAssetMenu part of the code. Seems the same as the original ActionItem code without the isConsumable and tooltip and the param so the original carry all the stuff we don’t want to touch.
Guessing you can add to the new override as you see fit with what you want the new item to do. I have one for Healing and this one for Spirit in the game.

namespace GameDevTV.Inventories
{
[CreateAssetMenu(menuName = (“GameDevTV/GameDevTV.UI.InventorySystem/Spirit Item”))]
public class SpiritItem : ActionItem
{
[SerializeField] float ISpiritAmount = 0;
public override void Use(GameObject user)
{
if (ISpiritAmount > 0)
{
GameObject.FindWithTag(“Player”).GetComponent().GainSpirit(ISpiritAmount);
}
}
}
}

Heath, the ActionItem.cs script shouldn’t be on the item in the inspector, only a script derived from it.

Public class MyActualAction: ActionItem

This new class will act as the ActionItem. Then, since you have used
Public override void Use(GameObject user)
The system automatically knows to call your Use method.

So ActionItem.cs is the common parent of all actual Action scripts. By using a parent like this, all the UI needs to know is that this is something that can be used when the button is activated. It doesn’t need to know it is a healing potion or Explosive Wave spell or a shield spell or whatever. All it cares about is Use(). The magic of C# handles the rest.

Here is a great link on overriding

@RanMan @Brian_Trotter
ok guys… thank you! It took me some failed scripts with errors and your patience to understand that whatever variables fields I declare in the ActionItem script CARRIES over to say HealthPotion.cs, and not to really put anything on there except what I want to access during the override…

For those of you who are also confused and not following how the ActionItem script works…

As long as you house bools for health, mana, damage, whatever in the ActionItem script, and find whatever components may be needed (Health, BaseStats, etc.) on the ActionItem script, we will have access to it on the ActionItem subclass (health potion, mana potion, damage increase, etc.) without needing to build a reference to it… for example, lets look at what I added to my ActionItem script to prep it for making a potions subclass.

ActionItem.cs
using System;
using RPG.Attributes;
using UnityEngine;

namespace GameDevTV.Inventories
{
    /// <summary>
    /// An inventory item that can be placed in the action bar and "Used".
    /// </summary>
    /// <remarks>
    /// This class should be used as a base. Subclasses must implement the `Use`
    /// method.
    /// </remarks>
    [CreateAssetMenu(menuName = ("GameDevTV/GameDevTV.UI.InventorySystem/Action Item"))]
    public class ActionItem : InventoryItem
    {
        // CONFIG DATA
        [Tooltip("Does an instance of this item get consumed every time it's used.")]
        [SerializeField] bool consumable;
        [SerializeField] bool health;
        [SerializeField] bool mana;
        [SerializeField] float amountToReplenish;
        Health player;


        // PUBLIC

        /// <summary>
        /// Trigger the use of this item. Override to provide functionality.
        /// </summary>
        /// <param name="user">The character that is using this action.</param>
        public virtual void Use(GameObject user)
        {
            Debug.Log("Using action: " + this);
        }

        public bool IsConsumable()
        {
            return consumable;
        }

        public float AmountToReplenish()
        {
            return amountToReplenish;
        }

        public Health GetPlayerHealth()
        {
            return GameObject.FindGameObjectWithTag("Player").GetComponent<Health>();
        }
    }
}

You can see I’ve added bools for whether or not I’m making a potion for health or mana and a float for how much I want to replenish when the subclass is actually used in the action bar. You can see I also want to be able to call a reference to the players Health script, so I can call a method I have set up on the Health script called Heal(), so that script needs to be in the variable declarations as well.

Now, let’s make a script for a health potion and call it HealthPotion. Because everything you might want on your health potion is already in the base class ActionItem, all the bools and floats you include in there will show up in this when you right click in the assets panel to make a new potion in Unity, EVEN THOUGH they are nowhere to be found in this script, save the reference to the base class in the class declaration… note it doesn’t say Monobehaviour, it says ActionItem… really short script… here we go

using UnityEngine;

namespace GameDevTV.Inventories
{
    [CreateAssetMenu(menuName = ("GameDevTV/GameDevTV.UI.InventorySystem/Action Item/Health Potion"))]
    public class HealthPotion : ActionItem
    {
        public override void Use(GameObject user)
        {
            GetPlayerHealth().Heal(AmountToReplenish());
        }
    }
}

You can see I’m calling the GetPlayerHealth() that I prepped in the ActionItem script, and it does not need a reference built to access it since we inherited it, any method you put in action item will be usable in your sub class without needing to build a reference to it, it just knows you mean to call it on ActionItem…

now when I right click in unity to make a health potion, you will see it in the menu in the action item because of how the CreateAssetMenu name has the slash Action Item/HealthPotion.

and in the inspector you can see that all the prep I did in ActionItem is there ready for me to access…

Screen Shot 2020-06-03 at 3.51.46 PM

and finally, the override from HealthPotion.cs calls the GetPlayerHealth() on the ActionItem, which gets me the player’s health script, and then it calls Heal() with whatever AmountToReplenish() returns (which doesn’t need a reference, it just knows you mean AmountToReplenish() from the ActionItem script) to pass as the argument that method requires…

public void Heal(float amountToHeal)
        {
            print("i'm supposed to heal " + healthPoints + " for " + amountToHeal);
            healthPoints = Mathf.Min(baseStatsScript.GetStat(Stat.Health), healthPoints + amountToHeal);
            print("healed, now my healthPoints =  " + healthPoints);
        }

as you can see I have some print statements in there for debugging which can be removed…

and when it’s all said and done, you won’t have to bother @RanMan and @Brian_Trotter anymore… you’ll have an action item that heals your player all day long… or adds mana… or whatever… I’m gonna try a damage potion for a nice challenge.

@Brian_Trotter correct me on any terminology I might be misusing so I can correct it in that post

Privacy & Terms