Expanding Action Bar Functionality

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

Following up with this healingAction. I was planning on adding the sound and animation that goes with triggering a heal.
what is the best practice for doing that? should i spawn the effects from The action(or its child) script or should the spawning be dealt by the health class and get called by the action script?

You can do it either way, though bear in mind that the SO can’t have an Animator or an AudioSource attached, so it’s going to need to grab those references from the player… It might be better to have the sound attached to a GameObject under the player and let Health get a reference to it. As to the Animation, it needs to be in the Animator’s state tree, so again, I’d probably reference that through Health.

Thanks for the respond!
so after reading your response what i think ill do is, let the health component handle spawning the effect & sound, But let the SO have reference to which animation and sound to be used(to allow different potions to feel different

1 Like

I would probably create similar (but noticably different) particle systems to spawn as well, prefabs of which would of course be stored in the SO

1 Like

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

Hi Brian, again… sorry hah:)
tried implementing this though it doesn’t get the index, what am I missing?
tried:

GetComponent<ActionSlotUI|>().index

Have you implemented this in the ActionSlotUI script? index should be one of the variables. It doesn’t make much sense to put the IOnPointerClickHandler anywhere else.

I did the same But i still can’t use Mana Potion after its on action Bar , pressing 0, 1 should increase my health but doesnt not do,

Privacy & Terms