Forum rules - please read before posting.

New Item in Inventory 'Pop', and Flash Hotspots questions

Hi, hoping I can please get some help here, I've tried myself but I'm just not cracking it:

  1. I'd like any new item that's added to the inventory bar to 'pop' ie scale up a bit quickly and then back down to normal, so the player realises something has happened. I've scoured the forums and tried adapting this AC tutorial with no luck:
    https://adventurecreator.org/tutorials/creating-you-have-found-item-window
    And also asked ChatGPT, and it seems like I'll need to change the Inventory Menu source from 'Adventure Creator' to 'Unity UI Prefab' so I can grab the individual game objects from the hierarchy to attach 'pop' scripts to, but my game breaks when I use UI Prefab and I'm not familiar enough with Unity's Canvas Prefabs yet, so I'd like to try get this to work with AC's native inventory source. I've got as far as the 'Add Inventory' event being able to run an ActionList, but can't seem to target the 'InventoryBox' element in the Inventory Menu to make it do eg a once-off scale animation. And I can't attach a script to anything as inventory items aren't GameObjects in the hierarchy. I'd also like to be able to have items removed from the inventory play a kind of 'dust' animation, but one thing at a time.

  2. I'm struggling to figure out how to Flash All HotSpots. I know the function exists in AC because it's in the AC manual, and have tried poking around in Unity's Project Settings and Input Settings, but can't seem to find any way to find that function nor how to link it to a keyboard button, or a button in the UI (if I could link a keyboard button to make all hotspots flash, then I could make a new button next to settings with 'simulator input' to mimic that, but I have no idea how to even get the function working in the first place.)

Thank you, from a tired newbie.

Comments

  • edited December 2024

    1) You wouldn't involve a new Menu for this - if you use Unity UI for your Inventory menu's Source, you can attach an Animator and a script to each of the Buttons that display items, and have an animation play if the item represented is the one that was added.

    Create an Animator component, add an empty Default state, and a separate state that handles the "pop", to each Button (the same Animator Controller can be used for each). Use a Trigger parameter named e.g. "Pop" that invokes this animation with a transition.

    Next, attach this script (PopItem.cs) to each Button, assigning the "slot index" field in the Inspector to the slot it's mapped to in your Menu's InventoryBox element:

    using UnityEngine;
    using AC;
    
    public class PopItem : MonoBehaviour
    {
    
        public int slotIndex;
        public string popTrigger;
    
        void OnEnable () { EventManager.OnInventoryAdd_Alt += OnInventoryAdd; }
        void OnDisable () { EventManager.OnInventoryAdd_Alt -= OnInventoryAdd; }
    
        private void OnInventoryAdd (InvCollection invCollection, InvInstance invInstance, int amount)
        {
            var menu = KickStarter.playerMenus.GetMenuWithCanvas (GetComponentInParent<Canvas> ();)
            var element = menu.GetElementWithGameObject (gameObject);
            var inventoryBox = element as MenuInventoryBox;
    
            int newItemSlot = inventoryBox.GetItemSlot (invInstance);
            if (newItemSlot == slotIndex)
            {
                GetComponent<Animator> ().SetTrigger (popTrigger);
            }
        }
    
    }
    

    2) Invoking an input named FlashHotspots will flash all Hotspots. Through code, you can do this manually with:

    foreach (Hotspot hotspot in KickStarter.stateHandler.Hotspots)
    {
        if (hotspot.highlight && hotspot.IsOn ())
            {
            hotspot.Flash ();
        }
    }
    
  • Hi Chris, thanks so much for the reply.

    1. This is going to be super useful when I make another game, thanks, might try Unity UI for Inventory, and that makes a lot of sense.
      For some reason in my current project, selecting Unity UI freezes things so I'm keen to keep using Adventure Creator as the Inventory source, and wondered if there was any way to get the same 'pop' effect without having to use Unity UI, I saw there was an 'Inventory Add' event but wasn't able to find a way to hook into it. No probs if this isn't possible, I'll leave it out of this project and try for the next one.

    2. Thank you. I have the same question I had in the other thread:
      https://adventurecreator.org/forum/discussion/15569/displaying-speedrun-timer-question#latest
      Just not entirely sure where to add the custom code, whether in a new script on an empty GameObject or if the script doesn't need to be attached to anything, and how in an ActionList to trigger code/script.
      Related: May I ask where in Unity's Input Manager one would find an entry for FlashHotspots, or would it more be about making a new input entry and then adding the code that calls the FlashHotspots function?

    1. Afraid not - you'd need the use of Unity UI for any custom animation. Do any error messages appear when using Unity UI?
    2. You don't need custom code if invoking the input is enough, but if you did - create a new C# script, and place the code in a custom public function. You can then attach it to a prefab and call it with the Object: Call event Action - see this tutorial for details.
  • edited December 2024

    2 - ah, thanks. Was easier than I thought! For anyone other newbies wondering how this works, in Edit > Project Settings > Input Manager, duplicate one of the axis and rename it to FlashHotspots and set a key to trigger it. Then, like me if you want a button in the eg Inventory to trigger it on mouse click, duplicate a button and set the axis to trigger FlashHotspots. That's it. Pressing on it or pressing f will briefly flash all hotspots in the scene, assuming they've been set to flash on mouse-over or similar.

  • edited January 5

    Hello Chris :)

    Happy new year !!

    I'm struggling to trigger the state of the animator's animation attached to the inventory menu button when an item is added to the inventory. At the same time, I'm trying to turn on the menu because I have 'Mouse Over' in the Appear Type for the inventory menu (I read in a forum topic to change it to 'Manual', turn it on, turn it off, then change it back to 'Mouse Over').

    The event is not triggering I don't know why.

    Thanks a lot ;)

    using System.Collections;
    using AC;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class InventoryAdd : MonoBehaviour
    {
        public int slotIndex;
    
        void OnEnable() { EventManager.OnInventoryAdd_Alt += OnInventoryAdd; }
        void OnDisable() { EventManager.OnInventoryAdd_Alt -= OnInventoryAdd; }
    
        private void OnInventoryAdd(InvCollection invCollection, InvInstance invInstance, int amount)
        {
            Debug.Log("HERE");
            var menu = KickStarter.playerMenus.GetMenuWithCanvas(GetComponentInParent<Canvas>());
            var element = menu.GetElementWithGameObject(gameObject);
            var inventoryBox = element as MenuInventoryBox;
    
            int newItemSlot = inventoryBox.GetItemSlot(invInstance);
            if (newItemSlot == slotIndex)
            {
    
                menu.appearType = AppearType.Manual;
                menu.TurnOn();
    
                LaunchePulseAndWaitTheEnd();
    
                menu.TurnOff();
                menu.appearType = AppearType.MouseOver;
    
            }
    
        }
    
        private IEnumerator LaunchePulseAndWaitTheEnd()
        {
            GetComponent<Animator>().SetTrigger("Pulse");
            GetComponent<Animator>().Play("Pulse");
    
            while (true)
            {
                AnimatorStateInfo stateInfo = GetComponent<Animator>().GetCurrentAnimatorStateInfo(0);
    
                if (stateInfo.IsName("Pulse") && stateInfo.normalizedTime >= 1.0f)
                {
                    break; 
                }
    
                yield return null;
            }
        }
    
    }
    
    

    Edit : I checked if event was subscribed and the answer is yes :

    https://ibb.co/2njscBs

  • This is the configuration of the inventory menu of my previous message :
    https://imgur.com/a/U3BstVM

  • edited January 6

    Happy new year @Kinteros!

    The main issue I can see is that you're listening to the event within the UI object itself. A UI Canvas is disabled when its Menu is turned off, meaning it's not able to listen out for the event.

    You'd need to move this script onto a separate GameObject within the scene, so that it's always enabled. Rather than referencing the Menu and slot components by GameObject, use the PlayerMenus GetMenuWithName / GetElementWIthName functions, i.e.:

        var menu = PlayerMenus.GetMenuWithName ("Inventory");
    

    and:

    var inventoryBox = menu.GetElementWithName ("InventoryBox") as MenuInventoryBox;
    int index = inventoryBox.GetItemSlot (invInstance);
    GameObject slot = inventoryBox.uiSlots[index].uiButton.gameObject;
    
  • Many thanks to you, @ChrisIceBox , for your prompt reply, which put me on the right track!

    I had to add a boolean to prevent the OnDisable from triggering before the end of the animation.

    Here's the final code in case anyone's interested :

    using System.Collections;
    using AC;
    using UnityEngine;
    
    public class InventoryAdd : MonoBehaviour
    {
    
        private Menu menu;
        private bool isProcessing = false;
    
    
        public void Start()
        {
            menu = PlayerMenus.GetMenuWithName("Inventory");
        }
    
        private void OnEnable()
        {
            EventManager.OnInventoryAdd_Alt += OnInventoryAdd;
        }
    
        private void OnDisable()
        {
            if (!isProcessing)
            {
                EventManager.OnInventoryAdd_Alt -= OnInventoryAdd;
            }
        }
    
        private void OnInventoryAdd(InvCollection invCollection, InvInstance invInstance, int amount)
        {
            if (menu != null) { 
                var inventoryBox = menu.GetElementWithName("InventoryBox") as MenuInventoryBox;
                int index = inventoryBox.GetItemSlot(invInstance);
                GameObject slot = inventoryBox.uiSlots[index].uiButton.gameObject;
    
                if (slot != null)
                {
                    StartCoroutine(LaunchePulseAndWaitTheEnd(slot.GetComponent<Animator>()));
                }
            }
    
        }
    
        private IEnumerator LaunchePulseAndWaitTheEnd(Animator animator)
        {
            menu.appearType = AppearType.Manual;
            menu.TurnOn();
    
            animator.SetTrigger("Pulse");
    
            while (true)
            {
                AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
    
                if (stateInfo.IsName("Pulse") && stateInfo.normalizedTime >= 1.0f)
                {
                    break; 
                }
    
                yield return null;
            }
    
            menu.TurnOff();
            menu.appearType = AppearType.MouseOver;
    
            isProcessing = false;
    
            EventManager.OnInventoryAdd_Alt -= OnInventoryAdd;
        }
    
    }
    
    

    Thanks again, have a nice day !
    See you ;)

  • Hi @Kinteros , hope all well. Thanks for sharing your workings. May I ask how you used the script, please? ie, was it attached to a blank gameobject within a scene, and if so, did you create new gameobjects with the script in each scene you wanted the new inventory item pulse to appear in? And what else was involved, ie did you need to create a new animation controller or animations? How did you set up the event in the Events Editor window? Any more info to get a fuller picture would be great please, thank you.

  • Resurfacting an old thread - thanks Kinteros, your script helped me. It didn't work out the box for me, I had an LLM adapt it. This is what I've got, will paste below.

    Chris - this is working well for adding new items to the inventory. The script is on the PersistentEngine, and works when I add new items. However, I'd like to make it so when I add new journal pages to the journal, the journal in the inventory pops the icon again. The script only works for any new items that are added, but I can't seem to get the existing journal button icon to re-pop.

    I tried making the journal icon a regular button in the Inventory Prefab menu that isn't an inventory slot, but I can't seem to access the button and its animator from an ActionList to run the script.

    So, at the end of the day I'd dig to trigger the 'pop' animation when a new page is added to the journal, either automatically doing it or manually in an ActionList.
    I could make the journal a normal inventory item and I've tried (unsuccessfully) to have an LLM adapt the existing script so that not only new inventory items trigger the InventoryAddPop.cs script, but even if the item exists in the inventory, to still trigger it.
    Or I could make the journal a button that's still in the InventoryUI prefab, but isn't a 'linked button' in the AC menu.

    I suspect I'd need to have a script sit on the PersistentEngine just for the Journal, or maybe adapt the existing InventoryAddPop.cs script to pop the item in the inventory even if it already exists.

    I wondered if you had any inkling please of the best way to go about this?

    using System.Collections;
    using AC;
    using UnityEngine;
    
    public class InventoryAddPop : MonoBehaviour
    {
        private Menu menu;
        private bool isProcessing = false;
    
        private void Start()
        {
            // Replace "Inventory" with your AC menu name if different
            menu = PlayerMenus.GetMenuWithName("Inventory");
    
            if (menu == null)
                Debug.LogWarning("[InventoryAddPop] Could not find Menu named 'Inventory'. Check the name in PlayerMenus.");
        }
    
        private void OnEnable()
        {
            EventManager.OnInventoryAdd_Alt += OnInventoryAdd;
        }
    
        private void OnDisable()
        {
            EventManager.OnInventoryAdd_Alt -= OnInventoryAdd;
        }
    
        private void OnInventoryAdd(InvCollection invCollection, InvInstance invInstance, int amount)
        {
            if (menu == null)
            {
                Debug.LogWarning("[InventoryAddPop] Menu is null in OnInventoryAdd.");
                return;
            }
    
            // Your element is called "Items"
            var inventoryBox = menu.GetElementWithName("Items") as MenuInventoryBox;
            if (inventoryBox == null)
            {
                Debug.LogWarning("[InventoryAddPop] Could not find element 'Items' in menu '" + menu.title + "'.");
                return;
            }
    
            int index = inventoryBox.GetItemSlot(invInstance);
            if (index < 0 || inventoryBox.uiSlots == null || index >= inventoryBox.uiSlots.Length)
            {
                Debug.LogWarning("[InventoryAddPop] Invalid slot index.");
                return;
            }
    
            GameObject slot = inventoryBox.uiSlots[index].uiButton?.gameObject;
            if (slot == null)
            {
                Debug.LogWarning("[InventoryAddPop] Slot GameObject is null.");
                return;
            }
    
            Animator anim = slot.GetComponent<Animator>();
            if (anim == null)
            {
                Debug.LogWarning("[InventoryAddPop] No Animator on slot '" + slot.name + "'.");
                return;
            }
    
            StartCoroutine(PlayPopAnimation(anim));
        }
    
        private IEnumerator PlayPopAnimation(Animator animator)
        {
            isProcessing = true;
    
            // Save how the menu normally appears
            AppearType originalAppearType = menu.appearType;
    
            // Temporarily make the menu visible
            menu.appearType = AppearType.Manual;
            menu.TurnOn();
    
            // Trigger the pop animation
            animator.ResetTrigger("Pop");
            animator.SetTrigger("Pop");
            Debug.Log("[InventoryAddPop] Triggered Pop animation on " + animator.gameObject.name);
    
            // Wait dynamically for animation to finish or timeout after 2 seconds
            float timeout = 2f;
            float timer = 0f;
            bool done = false;
    
            while (!done && timer < timeout)
            {
                AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(0);
                if (info.normalizedTime >= 1f && !animator.IsInTransition(0))
                {
                    done = true;
                }
    
                timer += Time.deltaTime;
                yield return null;
            }
    
            // Small extra delay for polish
            yield return new WaitForSeconds(0.1f);
    
            // Hide menu again
            menu.TurnOff();
            menu.isLocked = false;
            menu.appearType = originalAppearType;
    
            // Refresh AC menus
            KickStarter.playerMenus.RecalculateAll();
    
            Debug.Log("[InventoryAddPop] Menu hidden again, restored to appearType: " + originalAppearType);
    
            isProcessing = false;
        }
    }
    
  • This function will call your script's OnInventoryAdd function, affecting the journal only:

    public int journalItemID = 10;
    public void PopJournal()
    {
        var invInstance = KickStarter.runtimeInventory.PlayerInvCollection.GetFirstInstance(journalItemID);
        OnInventoryAdd(KickStarter.runtimeInventory.PlayerInvCollection, invInstance, 1);
    }
    

    Add it to your script, and replace "10" with the ID of your Journal item.

    You can then call the public PopJournal function manually to trigger the effect.

  • Huzzah! That worked perfectly, thanks. I got stuck for a bit until I realised the script addition isn't referencing Slot1 in the inventory grid but rather the Journal's Item ID in the AC Inventory Manager. Bril.

    In a regular ActionList I'm able to use the 'Object: Send Message' to send the PopJournal function to the PersistentEngine on which the script is placed. Sharing this for future me / anyone else who this may be useful for:

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.