Forum rules - please read before posting.

[QUESTION] Working in "Choose Hotspot Then Interaction" mode with Multiple interactions

edited December 2020 in Technical Q&A

Ok, I'm getting frustrated and I can't make this work.

My aim is to make an inventory which behaves like this:
1- If you hover on an item slot, you see a selection ring and a text label telling you the name of the item you're hovering on (that is, I'm giving some kind of visual feedback to let the player know that an item is being selected). These two items disappear if you stop hovering on that slot.
2- If you click on the item slot, the selection ring and the text label don't disappear anymore, and the interaction menu is shown, in which there should be 3 options available: Look / Use / Combine
3a- If you click Look, a textbox with the item description is shown. Neither the inventory menu or the interaction menu closes, to give the player the chance to read the description.
3b- If you click Use, the cursor changes to the item. The inventory menu closes, and you can use the item on one of the hotspots shown in the scene.
3c- If you click Combine, a blur panel appears that covers the whole scene. The inventory menu does not close, but the interaction menu does. The selection ring and the text label don't disappear, and you can choose one of the other items in the inventory to attempt a combine. If a combination is successful, both the items are removed and the resulting item is added to the inventory. The blur panel is turned off, and the resulting item is in the condition described in N.2 of this list.

I've tried so many different combinations that I've lost track on what I've tried and what I haven't.

Currently the Combine menu element in the interaction menu is defined as Button, and here's my first doubt: what's the difference between making a menu element of type Interaction (and adding some extra effects in the OnClick() event of the button component of the Unity prefab) instead of making it a simple menu element of type Button and having the click type perform an actionlist which creates some side effects and then starts the combine interaction?

Second doubt: I've tried using in my script both KickStarter.runtimeInventory.SelectItem(), KickStarter.runtimeInventory.SelectItemByID() and slotItem.Select() (in the latter the variable slotItem contains the item shown in the inventory box slot), but none of these options caused an update in the other menu element, that I've made as "DisplaySelected" inventory box just to check if things were working. What I mean is, if I use the Combine Interaction menu element on my item, the current selection is shown in the inventory box. But if I select the item via script, it doesn't. Why is that? What I'm doing wrong?

Apologies for the wall of text, but I've been banging my head on the wall for a couple of weeks now and I'm starting to lose my temper... I'm sure this tool is going to help me speed up things in the long run, but I'm struggling to getting things started. Any help would be deeply appreciated, although I think I need some functional clarification instead of a direct solution to follow, otherwise I won't understand the underlying logic.

Regards,
Willy

EDIT: corrected some messy explanations

Comments

  • My apologies for your frustration.

    Just so that I'm on the same level, though - how far through your list have you gotten? As most of your post relates to the Combine side of things, is it correct to assume that 1 -> 3b are running OK (aside from the Interaction menu disappearing in 3A, as discussed elsewhere)?

    Even if not, though, it sounds like the Combine issue is the major one.

    To clarify on the functional aspect: built-in, the combining of inventory items is possible in two ways:

    1. Including an InventoryBox in your Interaction menu, that displays a list of other items you can combine an item with upon clicking it. This is similar to the 2D Demo (/demo/2d-demo) scene, except that only has such behaviour for Hotspots (e.g. the Bench with the Worm).
    2. Selecting an Inventory item, and then clicking another item while that item is active (i.e. the cursor has changed its graphic to reflect this).

    At least so far as I understand it, it sounds like you're looking something closer to 1), only with the second InventoryBox placed in a separate Menu. You should be able to achieve that with a little bit of custom scripting.

    When an Interaction menu is opened, the Menu records the Hotspot or Inventory Item that it's been opened for. An InventoryBox element of the type Hotspot Based present in this Menu will then refer to that when working out what Items to populate itself with (and how to behave when such items are clicked).

    To have these items be in a separate Menu, it should just be a case of telling the Menu which Inventory item it should associate itself with before it gets turned on. This can be done by hooking into your "Combine" button's click event.

    There's a couple of ways to do that: you can either add an event to the Button's OnClick event, or hook into AC's OnMenuElementClick event. With the former, you'll just have to also keep a record of the UI Canvas so that you can extract the associated Menu.

    Something like this should do it:

    using UnityEngine;
    using AC;
    
    public class ItemCombineHelper : MonoBehaviour
    {
    
        public Canvas canvas;
    
        public void OnClickCombine ()
        {
            Menu menu = KickStarter.playerMenus.GetMenuWithCanvas (canvas);
            InvInstance clickedItemInstance = menu.TargetInvInstance;
            if (InvInstance.IsValid (clickedItemInstance))
            {
                Menu combineMenu = PlayerMenus.GetMenuWithName ("Combine");
                combineMenu.SetHotspot (null, clickedItemInstance);
                combineMenu.TurnOn ();
            }
        }
    
    }
    

    If you attach this to your UI prefab, hook up your UI Button's OnClick handler to include its OnClickCombine function, and assign the "Canvas" field in its Inspector, then when clicked it will work out what Inventory item the Interaction menu is currently associated with, map it to a menu named "Combine", and turn it on.

    If you then go an create new Menu named Combine, and give it an InventoryBox set to "Hotspot Based", you should then find that it becomes populated with the other items carried by the Player - and clicking one results in that item being combined.

    A side-note: unless you check Only show items referenced in Interactions? in this new InventoryBox's properties box, then all items will show - including the item that you selected to open this Menu. I shall see if this can be excluded in an update.

    To create a combine Interaction that results in the two items being replaced by a third item, use a pair of Inventory: Add or remove Actions - one to remove the first item, another to replace the second with the third.

    To have the Combine Menu turn off when a combine Interaction is run, you can either incorporate it into the Interaction directly with Menu: Change state, or automate it through script by hooking into the OnInventoryCombine event:

    void OnEnable () { EventManager.OnInventoryCombine += OnInventoryCombine; }
    void OnDisable () { EventManager.OnInventoryCombine -= OnInventoryCombine; }
    
    void OnInventoryCombine (InvItem itemA, InvItem itemB)
    {
        PlayerMenus.GetMenuWithName ("Combine").TurnOff ();
    }
    

    That should, hopefully, be the bulk of the problem sorted. I would expect there may be a few other teething errors to get things exactly right, but see how far this takes you first. A lot of the smaller, custom behaviour can be tweaked through similarly simple scripts, but it sounds like the major issue here is that of the Combine menu, so I'd recommend we sort that out first before the others that may surround it.

    To answer your doubts:

    what's the difference between making a menu element of type Interaction (and adding some extra effects in the OnClick() event of the button component of the Unity prefab) instead of making it a simple menu element of type Button and having the click type perform an actionlist which creates some side effects and then starts the combine interaction?

    Clicking an Interaction element will run the appropriate Interaction ActionList associated for a given item, whearas a Button will only run the same thing each time. If you're scripting the click-handling, the difference becomes much less - though using an Interaction element does give you access to the OnInventoryInteract custom event which can be used to more easily determine which item was involved.

    I've tried using in my script both KickStarter.runtimeInventory.SelectItem(), KickStarter.runtimeInventory.SelectItemByID() and slotItem.Select() (in the latter the variable slotItem contains the item shown in the inventory box slot), but none of these options caused an update in the other menu element, that I've made as "DisplaySelected" inventory box just to check if things were working. What I mean is, if I use the Combine Interaction menu element on my item, the current selection is shown in the inventory box. But if I select the item via script, it doesn't. Why is that? What I'm doing wrong?

    I'd have to know the actual code you're using, as well as when/how it's being triggered, but it could be that the item is somehow becoming deselected momentarily after being selected it. It's hard to say why exactly this would occur - it may be because AC is detecting a left-click in the same frame and de-selecting it. There's ways around it, but the first thing would be to learn why. To do so, you can either hook into the OnInventoryDeselect event, or paste a simple Debug.Log statement in the RuntimeInventory script's SetNull function, which is where the item-deselection code is.

  • edited December 2020

    Hello Chris,
    and first of all thank you for your support. As per my frustration, it's not your fault, it's just my temper I guess.

    To clarify on the functional aspect: built-in, the combining of inventory items is possible in two ways:
    1- Including an InventoryBox in your Interaction menu, that displays a list of other items you can combine an item with upon clicking it. This is similar to the 2D Demo (/demo/2d-demo) scene, except that only has such behaviour for Hotspots (e.g. the Bench with the Worm).
    2- Selecting an Inventory item, and then clicking another item while that item is active (i.e. the cursor has changed its graphic to reflect this).

    Actually, I think that my needs have more resemblance on scenario 2, since I'd like to keep the inventory menu open and "combine" the selected item on any other item in my inventory by clicking on them. But I'll try your suggestion just to get a hang on things.

    There's a couple of ways to do that: you can either add an event to the Button's OnClick event, or hook into AC's OnMenuElementClick event. With the former, you'll just have to also keep a record of the UI Canvas so that you can extract the associated Menu.

    From this piece of code I infer that you can use menu.TargetInvInstance to obtain the item instance you clicked in the previous menu. I wonder if I can use it even if I don't open a different menu (I'll give it a shot).

    Clicking an Interaction element will run the appropriate Interaction ActionList associated for a given item, whearas a Button will only run the same thing each time. If you're scripting the click-handling, the difference becomes much less - though using an Interaction element does give you access to the OnInventoryInteract custom event which can be used to more easily determine which item was involved.

    Ok, I've just tried using this method within my inventory script. Nothing fancy, just a Debug.Log triggered whenever I click on one of my buttons in the Inventory box. The log appears only when I choose one of the interactions in the interaction menu that pops out after the first click, so I guess it's triggered only when you actually choose an interaction, not before (i.e., I can't use it to update the text label that I want to show, since triggering the interaction would cause the menu to turn off). Anyway, in either case the inventory box marked as "Display Selected" gets updated only in case I've defined an interaction for that particular Icon, which is expected.

    So, supposing I need to make something more than simply changing the cursor (i.e. show the blur panel), I can either change the Menu Element to Button with custom script (and tinker with the script until I get the behaviour I want) or add the side effect on the button OnClick and let the Combine interaction handle the rest.

  • edited December 2020

    Hi Chris,
    just a quick update: I've succeeded in making my inventory behave as intended up to point 2 of my original list. What happens, though, is that if I don't select any interaction and hover on another slot of my inventory box the interaction menu turns off. It's a pretty strange behaviour, considering that I've set the "Close Interactions with" option as "Via Script Only". Anyway, putting some debug.log here and there I've seen that the method that calls "CloseInteractionMenus" is the UpdateElements method within PlayersMenu. I haven't dug deeper in the logics, but I guess it shouldn't happen with just hovering if you choose to close the interaction menu via script only. For the time being I'll just work around the problem, changing the menu appear type to Manual and handling things via script.

    EDIT: I've managed to turn off and on the menu via script, problem is that if I don't set it up with an appearType == OnInteraction it doesn't have information about the item that was clicked, so it cannot enable/disable buttons based on the handled interactions... Sigh.

    Regards
    Willy

  • From this piece of code I infer that you can use menu.TargetInvInstance to obtain the item instance you clicked in the previous menu.

    That's correct. TargetInvInstance refers to the item that a menu is associated with. For an Interaction menu, this is set automatically to the item that was clicked on to open it, but it can be set manually via the SetHotspot function.

    You can find a list of all public functions and variables for the Menu class here in the scripting guide.

    Nothing fancy, just a Debug.Log triggered whenever I click on one of my buttons in the Inventory box. The log appears only when I choose one of the interactions in the interaction menu that pops out after the first click

    So this is on the "regular" Inventory menu? Any code you assign it should run instantly - not waiting for anything else.

    A couple of things to be aware of regarding this:

    1) A more generic way to insert custom code into Menu clicks is to instead hook into the OnMenuElementClick event. This is actually my preference over adding click handlers directly to a Button, as it ensures the various click events (ie. built in vs custom) are run in the intended order. This event provides the Menu and Element as parameters, which you can drill down to determine what was clicked, i.e.:

    void OnEnable () { EventManager.OnMenuElementClick += OnMenuElementClick; }
    void OnDisable () { EventManager.OnMenuElementClick -= OnMenuElementClick; }
    
    void OnMenuElementClick (AC.Menu menu, MenuElement element, int _slot, int buttonPressed)
    {
        if (menu.title == "Inventory" && element.title == "InventoryBox")
        {
            MenuInventoryBox myInventoryBox = element as MenuInventoryBox;
            // ..
        }
    }
    

    2) If you're looking to update a Menu's appearance as it turns on, you can similarly hook into the OnMenuTurnOn event:

    void OnEnable () { EventManager.OnMenuTurnOn += OnMenuTurnOn; }
    void OnDisable () { EventManager.OnMenuTurnOn -= OnMenuTurnOn; }
    
    void OnMenuTurnOn (AC.Menu menu, bool isInstant)
    {
        if (menu.title == "Interaction")
        {
            // ..
        }
    }
    

    For a list of all Inventory- and Menu-related event hooks, see the Manual's "Inventory scripting" and "Menu scripting" chapters.

    So, supposing I need to make something more than simply changing the cursor (i.e. show the blur panel)

    Am I not right in thinking your "blur panel" is a separate menu? That's what I was assuming with my earlier example code. It may be worth sharing some mock-up screenshots so that we're not getting different ideas on the intended interface.

    if I don't select any interaction and hover on another slot of my inventory box the interaction menu turns off.

    If you've set your Settings Manager's See interactions with to Cursor Over Hotspot, then that will override the Close interactions with property in the event of hovering over a new item.

    I've managed to turn off and on the menu via script, problem is that if I don't set it up with an appearType == OnInteraction it doesn't have information about the item that was clicked, so it cannot enable/disable buttons based on the handled interactions...

    All you need to do is manually connect the clicked Item Instance to the Menu before turning it on. It's the SetHotspot function I mention above.

    You're welcome to post your script if you'd like me to give more specific advice when it comes to working with AC through scripting.

  • edited December 2020

    Good Morning Chris :smile:

    here's my mockup screen (due to NDA, I can't share real images, which is a hassle, but please bear with me) : https://imgur.com/a/ftwNpac

    So this is on the "regular" Inventory menu? Any code you assign it should run instantly - not waiting for anything else.

    Yes, it's a regular Inventory menu.

    As you can see, the blur panel is just an object within the inventory menu which I turn off and on as needed; I don't think it needs a different menu since it can block any raycast and should prevent the player from clicking anywhere else aside from the inventory box slots.

    If you've set your Settings Manager's See interactions with to Cursor Over Hotspot, then that will override the Close interactions with property in the event of hovering over a new item.

    Unfortunately, this is not the case, since my option is set as "Clicking On Hotspot"

    All you need to do is manually connect the clicked Item Instance to the Menu before turning it on. It's the SetHotspot function I mention above.

    I've already tried this, but it didn't work at first. However, by adding a call to MatchInteractions right before turning the menu on seems does the trick (but I haven't tested it out yet).

    As per my code, it's currently like this (I haven't refactored and removed the unused test code yet):

    Here's the Script attached to the root object of the Inventory Canvas

    `using System.Collections.Generic;
    using AC;
    using MyGame.DialogueSystem;
    using UnityEngine;
    using UnityEngine.UI;

    public class UI_Inventory : MonoBehaviour
    {
    [SerializeField] List slots = new List();
    [SerializeField] Image characterSprite = default;

    private Canvas linkedCanvas;
    
    private InvInstance item;
    
    public UI_InventorySlot CurrentSelect { get; internal set; }
    public Canvas LinkedCanvas { get => linkedCanvas; private set => linkedCanvas = value; }
    
    private void OnEnable()
    {
        EventManager.OnMenuTurnOn += OnMenuTurnOn;
        EventManager.OnInventoryInteract += ItemInteracted;
        EventManager.OnInventoryDeselect += ItemDeselected;
        EventManager.OnInventorySelect += ItemSelected;
        EventManager.OnMenuElementClick += ElementClicked;
    }
    
    private void ItemSelected(InvItem invItem)
    {
        Debug.Log($"Selected Item {invItem.label}");
    }
    
    private void ItemDeselected(InvItem invItem)
    {
        Debug.Log("Item deselected!");
    }
    
    private void OnDisable()
    {
        EventManager.OnMenuTurnOn -= OnMenuTurnOn;
        EventManager.OnInventoryInteract -= ItemInteracted;
        EventManager.OnInventoryDeselect -= ItemDeselected;
        EventManager.OnInventorySelect -= ItemSelected;
        EventManager.OnMenuElementClick -= ElementClicked;
    }
    
    private void Awake()
    {
        LinkedCanvas = GetComponentInParent<Canvas>();
    }
    
    void Update()
    {
        if (Input.GetButtonDown("InteractionB"))
        {
            if (CurrentSelect != null)
            {
                slots[slots.IndexOf(CurrentSelect)].ShowLabelAndRing(false);
                CurrentSelect = null;
            }
            else
            {
                KickStarter.playerMenus.GetMenuWithCanvas(LinkedCanvas).TurnOff();
                //PlayerMenus.GetMenuWithName("Inventory").TurnOff();
                Debug.Log("Turning menu off");
            }
        }
    }
    
    public void SetCharacterSprite()
    {
        var characterName = KickStarter.sceneSettings.GetAttribute(0).TextValue; // N2H: get rid of magic number
        var character = DialogueUtils.GetCharacterByName(characterName);
        characterSprite.sprite = character.activeIcon;
    }
    
    
    private void OnMenuTurnOn(Menu turnedOnMenu, bool isInstant)
    {
        if (turnedOnMenu == KickStarter.playerMenus.GetMenuWithCanvas(LinkedCanvas))
        {
            SetCharacterSprite();
        }
    }
    
    private void ItemInteracted(InvItem invItem, int amount)
    {
        Debug.Log($"Interacted with Item: {invItem.label}");
        // it gets called only after choosing a handled interaction
    }
    
    private void ElementClicked(Menu clickedMenu, MenuElement clickedElement, int _slot, int buttonPressed)
    {
        if (clickedMenu == KickStarter.playerMenus.GetMenuWithCanvas(LinkedCanvas) && clickedElement.title == "InventoryBox")
        {
            var inventoryBox = clickedElement as MenuInventoryBox;
            item = inventoryBox.GetInstance(_slot);
            Debug.Log($"Pressed button {buttonPressed} on element {_slot}, containing item {item.InvItem.label}");
            SelectItem(_slot);
        }
    }
    
    private void SelectItem(int slot)
    {
        if (CurrentSelect!= null)
        {
            CurrentSelect.ShowLabelAndRing(false);
            PlayerMenus.GetMenuWithName("Inventory Interaction").TurnOff();
        }
        CurrentSelect = slots[slot];
        CurrentSelect.ShowLabelAndRing(true);
    
        PlayerMenus.GetMenuWithName("Inventory Interaction").SetHotspot(null, item);
        PlayerMenus.GetMenuWithName("Inventory Interaction").MatchInteractions(item, false);
        PlayerMenus.GetMenuWithName("Inventory Interaction").TurnOn();
    }
    

    }`

    This is the simple script attached to every inventory slot button

    `using AC;
    using TMPro;
    using UnityEngine;
    using UnityEngine.EventSystems;

    public class UI_InventorySlot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
    {
    [SerializeField] private GameObject selectionRing = default;
    [SerializeField] private GameObject itemLabel = default;
    [SerializeField] private int index;

    private UI_Inventory inventoryHandler;
    private TextMeshProUGUI labelText;
    
    private void Start()
    {
        inventoryHandler = GetComponentInParent<UI_Inventory>();
        labelText = itemLabel.GetComponentInChildren<TextMeshProUGUI>();
    }
    
    public void OnPointerEnter(PointerEventData eventData)
    {
        if (KickStarter.runtimeInventory.localItems.Count > index)
        {
            var hoverItem = KickStarter.runtimeInventory.localItems[index];
            labelText.text = hoverItem?.label;
        }
        if (inventoryHandler.CurrentSelect == null)
        {
            ShowLabelAndRing(true);
        }
    }
    
    public void OnPointerExit(PointerEventData eventData)
    {
        if (inventoryHandler.CurrentSelect == null || inventoryHandler.CurrentSelect != this)
        {
            ShowLabelAndRing(false);
        }
    }
    
    public void ShowLabelAndRing(bool value)
    {
        selectionRing.SetActive(value);
        itemLabel.SetActive(value);
    }
    

    }`

    EDIT: Code embedding doesn't like some of the lines... Sorry

  • As you can see, the blur panel is just an object within the inventory menu which I turn off and on as needed;

    I'm afraid I don't see - which part is the "blur" panel? I only see one item icon, so I don't know which is your regular Inventory list vs the Combine list.

    Unfortunately, this is not the case, since my option is set as "Clicking On Hotspot"

    Can you share a shot of your Settings Manager, as well as the full stacktrace of your Debug.Log test?

    I've already tried this, but it didn't work at first. However, by adding a call to MatchInteractions right before turning the menu on seems does the trick (but I haven't tested it out yet).

    Apologies - yes, you're right. MatchInteractions will do the job of SetHotspot, but will also update the Elements within the Menu to reflect the linking.

    Code embedding doesn't like some of the lines... Sorry

    Code just needs an extra tab indent added to each line.

    This is the simple script attached to every inventory slot button

    You're reading the Player's inventory directly, rather than the list of items within the InventoryBox. These two are normally equivalent, but it's safer to read the latter as this will account for offsetting due to "Shift Left/Right" buttons:

    using AC;
    using TMPro;
    using UnityEngine;
    using UnityEngine.EventSystems;
    
    public class UI_InventorySlot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
    {
        [SerializeField] private GameObject selectionRing = default;
        [SerializeField] private GameObject itemLabel = default;
        [SerializeField] private int index;
    
        private UI_Inventory inventoryHandler;
        private TextMeshProUGUI labelText;
        private MenuInventoryBox inventoryBox;
    
        private void Start()
        {
            inventoryHandler = GetComponentInParent<UI_Inventory>();
            labelText = itemLabel.GetComponentInChildren<TextMeshProUGUI>();
        }
    
        public void OnPointerEnter(PointerEventData eventData)
        {
            if (inventoryBox == null)
            {
                // Rename these strings to match your menus
                inventoryBox = PlayerMenus.GetElementWithName ("Inventory", "InventoryBox") as MenuInventoryBox;
            }
    
            InvItem hoverItem = inventoryBox.GetItem (index);
            if (item != null)
            {
                labelText.text = hoverItem.label;
            }
            if (inventoryHandler.CurrentSelect == null)
            {
                ShowLabelAndRing(true);
            }
        }
    
        public void OnPointerExit(PointerEventData eventData)
        {
            if (inventoryHandler.CurrentSelect == null || inventoryHandler.CurrentSelect != this)
            {
                ShowLabelAndRing(false);
            }
        }
    
        public void ShowLabelAndRing(bool value)
        {
            selectionRing.SetActive(value);
            itemLabel.SetActive(value);
        }
    
    }
    
  • edited December 2020

    Thank you for your suggestion on the UI_InventorySlot code, I'll change it right away.

    Here are my current settings (set aside the fact that the Inventory Interaction Menu appear type is set as Manual, instead of "ON INTERACTION" like in the sceenshot).

    https://imgur.com/a/uyUncMH

    (I forgot to select the Blur Gameobject in my second screenshot, but you can see it as the first child gameobject of the InventoryUI prefab.. It's really just a Image with a material, though).

    As per showing you the full console log, I've made a quick video which you can take from here: https://we.tl/t-HjsEhaFxsE

    EDIT: Just for clarification, after clicking on the backpack Icon and on the Bird Inventory slot, I didn't click on the Camera, just hovered on it

  • I'm afraid I may have misunderstood your intent. I was thinking the "blur" panel would be an overlay showing a second set of inventory items - i.e. those that you can combine with the given item.

    If you're looking to rely on the same InventoryBox element to handle the combine as well, then there's no need for a "Hotspot Based" InventoryBox. Forget my mention of it above. It hopefully shouldn't have to involve so much in the way of scripting.

    Instead, you could try simply selecting the chosen item, showing the Blur panel, and amending the Cursor Manager's When inventory selected field to prevent the item from showing up as a new cursor.

    To change any Manager field at runtime, right-click its label to get an API reference to it.

    Then, go to your Inventory Interaction menu's Combine Button element and unset the ActionList asset - we'll handle everything through script:

    void OnEnable () { EventManager.OnMenuElementClick += OnMenuElementClick; }
    void OnDisable () { EventManager.OnMenuElementClick -= OnMenuElementClick; }
    
    void OnMenuElementClick (AC.Menu menu, MenuElement element, int _slot, int buttonPressed)
    {
        if (menu.title == "Inventory Interaction" && element.title == "Combine Button")
        {
            KickStarter.cursorManager.inventoryHandling = InventoryHandling.ChangeHotspotLabel;
            menu.TargetInvInstance.Select ();
            KickStarter.playerMenus.GetElementWithName ("Inventory", "Blur Panel").IsVisible = false;
            menu.TurnOff ();
        }
    }
    

    That will select the item (but prevent the cursor from changing) show the Blur panel, and turn off the Inventory Interaction menu. Clicking another item should then result in that item becoming combined.

    You can set up a similar button to select the item without changing the cursor, and instead close the Inventory menu, in order to use items on Hotspots in the scene.

    You can de-select the item, and revert your Cursor Manager to show inventory icons as cursors again, when appropriate:

    KickStarter.runtimeInventory.SetNull ();
    KickStarter.cursorManager.inventoryHandling = InventoryHandling.ChangeCursor;
    

    It's also worth mentioning that you can also script the click behaviour of an InventoryBox element entirely by setting its type to "Custom Script". Hooking into the OnMenuElementClick event in a similar fashion to the above can then let you control exactly what happens for each click / situation. For example, you could read the state of the Blur Panel element's IsVisible property to determine if you're in "Combine" vs "Select" mode, so that clicking an item results in either a combine or a selection accordingly.

    As per showing you the full console log, I've made a quick video which you can take from here:

    Sorry, but I'd need the log in full - stacktrace included. The video is only showing the first line.

  • Hi Chris,
    sorry for being late in my response, and happy new year!. I've managed to fix all the problems on the inventory in the meantime, and I thank you again for your support. As usual, your code does give me insight on some aspects of your tool that will definitely come in handy later on in the development. My code now looks like this:

    `

    using System.Collections.Generic;
    using AC;
    using Deodato.DialogueSystem;
    using TMPro;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class UI_InventoryMenu : MonoBehaviour
    {
    private const string INVENTORY_INTERACTION_MENU = "Inventory Interaction";
    private const string INVENTORY_BOX_ELEMENT = "InventoryBox";
    private const string OBSERVE_PANEL_ELEMENT = "Observe Panel";
    private const string BLUR_PANEL_ELEMENT = "Blur Panel";
    [SerializeField] List<UI_InventorySlot> slots = new List<UI_InventorySlot>();
    [SerializeField] Image characterSprite = default;
    [SerializeField] TextMeshProUGUI characterNameLabel = default;
    
    private Canvas linkedCanvas;
    
    private InvInstance item;
    private Menu thisMenu, inventoryInteractionMenu;
    private MenuInventoryBox inventoryBox;
    private MenuGraphic blurPanel;
    private bool canQuitMenu = true;
    
    public UI_InventorySlot CurrentSelect { get; internal set; }
    public Canvas LinkedCanvas { get => linkedCanvas; private set => linkedCanvas = value; }
    
    private void OnEnable()
    {
        EventManager.OnMenuTurnOn += MenuTurnedOn;
        //EventManager.OnInventoryInteract += ItemInteracted;
        EventManager.OnInventoryCombine += OnCombineAttempt;
        EventManager.OnMenuElementClick += ElementClicked;
        EventManager.OnMenuElementShow += DisableInteractionsWithMenu;
        EventManager.OnMenuElementHide += EnableInteractionsWithMenu;
    }
    
    private void OnDisable()
    {
        EventManager.OnMenuTurnOn -= MenuTurnedOn;
        //EventManager.OnInventoryInteract -= ItemInteracted;
        EventManager.OnMenuElementClick -= ElementClicked;
        EventManager.OnMenuElementShow -= DisableInteractionsWithMenu;
        EventManager.OnMenuElementHide -= EnableInteractionsWithMenu;
    }
    
    private void Awake()
    {
        LinkedCanvas = GetComponentInParent<Canvas>();
    }
    
    private void Start()
    {
        inventoryInteractionMenu = PlayerMenus.GetMenuWithName(INVENTORY_INTERACTION_MENU);
        thisMenu = KickStarter.playerMenus.GetMenuWithCanvas(LinkedCanvas);
        inventoryBox = thisMenu.GetElementWithName(INVENTORY_BOX_ELEMENT) as MenuInventoryBox;
        blurPanel = thisMenu.GetElementWithName(BLUR_PANEL_ELEMENT) as MenuGraphic;
        foreach (var slot in slots)
        {
            slot.InventoryBox = inventoryBox;
            slot.InventoryHandler = this;
        }
    }
    
    private void DisableInteractionsWithMenu(MenuElement elementShown)
    {
        if (elementShown.title == OBSERVE_PANEL_ELEMENT)
        {
            canQuitMenu = false;
            slots.ForEach(x => x.IsEnabled = false);
            inventoryBox.preventInteractions = true;
            inventoryBox.preventSelection = true;
        }
    }
    
    private void EnableInteractionsWithMenu(MenuElement elementHidden)
    {
        if (elementHidden.title == OBSERVE_PANEL_ELEMENT)
        {
            canQuitMenu = true;
            slots.ForEach(x => x.IsEnabled = true);
            inventoryBox.preventInteractions = false;
            inventoryBox.preventSelection = false;
        }
    }
    
    
    void Update()
    {
        if (!canQuitMenu)
        {
            return;
        }
    
        if (Input.GetButtonDown("InteractionB"))
        {
            if (CurrentSelect != null)
            {
                slots[slots.IndexOf(CurrentSelect)].ShowLabelAndRing(false);
                CurrentSelect = null;
            }
            else
            {
                KickStarter.playerMenus.GetMenuWithCanvas(LinkedCanvas).TurnOff();
                Debug.Log("Turning menu off");
            }
        }
    }
    
    private void MenuTurnedOn(Menu turnedOnMenu, bool isInstant)
    {
        if (turnedOnMenu == KickStarter.playerMenus.GetMenuWithCanvas(LinkedCanvas))
        {
            SetCharacter();
        }
    }
    
    public void SetCharacter()
    {
        var characterName = KickStarter.sceneSettings.GetAttribute(0).TextValue; // N2H: get rid of magic number
        var character = DialogueUtils.GetCharacterByName(characterName);
        characterSprite.sprite = character.activeIcon;
        characterNameLabel.text = characterName;
    }
    
    private void ItemInteracted(InvItem invItem, int amount)
    {
        Debug.Log($"Interacted with Item: {invItem.label}");
        // it gets called only after choosing a handled interaction
        DeselectSlot();
    }
    
    private void ElementClicked(Menu clickedMenu, MenuElement clickedElement, int clickedSlot, int buttonPressed)
    {
        if (clickedMenu == thisMenu && clickedElement.ID == inventoryBox.ID)
        {
            item = inventoryBox.GetInstance(clickedSlot);
            //Debug.Log($"Pressed button {buttonPressed} on element {clickedSlot}, containing item {item.InvItem.label}");
            if (item != null)
            {
                SelectSlot(clickedSlot);
            }
        }
    }
    
    private void OnCombineAttempt(InvItem invItem, InvItem invItem2)
    {
        // TO DO: differentiate between a succeded combine and a failed one
        blurPanel.IsVisible = false;
        DeselectSlot();
    }
    
    
    private void SelectSlot(int slot)
    {
        if (CurrentSelect != null)
        {
            CurrentSelect.ShowLabelAndRing(false);
            inventoryInteractionMenu.TurnOff();
        }
        CurrentSelect = slots[slot];
        CurrentSelect.ShowLabelAndRing(true);
    
        inventoryInteractionMenu.SetHotspot(null, item);
        inventoryInteractionMenu.MatchInteractions(item, false);
        //var rectTransform = CurrentSelect.transform as RectTransform;
        //inventoryInteractionMenu.SetCentre(rectTransform.anchoredPosition, true);
        inventoryInteractionMenu.TurnOn();
    }
    
    public void DeselectSlot()
    {
        if (CurrentSelect != null)
        {
            CurrentSelect.ShowLabelAndRing(false);
            CurrentSelect = null;
        }
    }
    
    }
    

    `

    For the moment I'm happy with the results, apart maybe one little doubt that deserves its own post, to be easily found out by others in my situation,.

    Thank you!

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.