Forum rules - please read before posting.

Manipulate and select inventory slots with buttons

Hi Everyone,

I wondered if its possible with AC's default menu functionality to manipulate inventory slots via UI buttons?

The above image is a how the inventory is designed.

This is how I want it to behave:

  1. The slots can be navigated by clicking either the up or down arrow buttons.
  2. Clicking either arrow button, steps through the slots one at a time.
  3. When the relevant slot is reached, clicking select will select that slot and run the standard interactions - use: actionlist

I have a Unity in scene menu currently setup with **slots **and the relevant arrow **buttons **for navigation. At the moment I'm using the Offset Element Slot: Click type to move through the slots but it only shifts them rather that allow me to step through them individually.

Any pointers would be very much appreciated :-)

Cheers,
Tim

Comments

  • edited April 2020

    Tricky one. This'll need custom scripting, but mostly it's a case of working with Unity's EventSystem rather than AC.

    I'll have a think and see if I can share some sample code.

    PS: Apologies for your posts briefly entering the spam queue - I've since verified your account so it shouldn't happen again.

  • Morning Chris,

    Tricky one. This'll need custom scripting, but mostly it's a case of working with Unity's EventSystem rather than AC.

    I thought as much, any examples would be much appreciated and no worries re the spam queue ;)

    Cheers,
    Tim

  • OK. Here's how to do it.

    Create an InventoryBox element in your Menu, and link it to the item buttons in your UI. Create the other three buttons (select, up and down) in the UI, but don't link them to the Menu.

    Give the item buttons a shared parent and attach a Canvas Group to this parent. Uncheck "Block Raycasts".

    Then attach the following script to your Canvas:

    using UnityEngine;
    using AC;
    
    public class IndirectlySelectItems : MonoBehaviour
    {
    
        private int selectedIndex = -1;
        private MenuInventoryBox inventoryBox;
    
        [SerializeField] private Color normalColor = Color.white;
        [SerializeField] private Color highlightColor = Color.blue;
    
        private void OnEnable ()
        {
            Menu thisMenu = KickStarter.playerMenus.GetMenuWithCanvas (GetComponent <Canvas>());
            inventoryBox = thisMenu.GetElementWithName ("InventoryBox") as MenuInventoryBox;
    
            SetSelectedIndex (0);
        }
    
        public void OnClick_Select ()
        {
            InvItem selectedItem = inventoryBox.GetItem (selectedIndex);
            if (selectedItem != null)
            {
                selectedItem.RunUseInteraction ();
            }
        }
    
        public void OnClick_Up ()
        {
            int newSelectedIndex = selectedIndex - 1;
            if (newSelectedIndex < 0)
            {
                newSelectedIndex = 0;
                inventoryBox.Shift (AC_ShiftInventory.ShiftPrevious, 1);
            }
            SetSelectedIndex (newSelectedIndex);
        }
    
        public void OnClick_Down ()
        {
            int newSelectedIndex = selectedIndex + 1;;
            if (newSelectedIndex >= inventoryBox.GetNumSlots ())
            {
                newSelectedIndex = inventoryBox.GetNumSlots () - 1;
                inventoryBox.Shift (AC_ShiftInventory.ShiftNext, 1);
            }
            SetSelectedIndex (newSelectedIndex);
        }
    
        private void SetSelectedIndex (int value)
        {
            if (selectedIndex != value)
            {
                if (selectedIndex >= 0 && selectedIndex < inventoryBox.GetNumSlots ())
                {
                    inventoryBox.uiSlots[selectedIndex].SetColour (normalColor);
                }
                if (value >= 0 && value < inventoryBox.GetNumSlots ())
                {
                    inventoryBox.uiSlots[value].SetColour (highlightColor);;
                }
    
                selectedIndex = value;
            }
        }
    
    }
    

    For the Select, Up and Down UI button, create OnClick events and map them to this component's OnClick_Select, OnClick_Up and OnClick_Down functions respectively.

  • Hey Chris,

    Thanks so much for this! That's done the trick I think, I just tested a clean menu and a new Unity UI and its working just as I hoped.

    I do get a Null Reference Exception when I run the scene:

    NullReferenceException: Object reference not set to an instance of an object
    IndirectlySelectItems.OnEnable () (at Assets/_Los/Scripts/UI/IndirectlySelectItems.cs:15)

    Could it be due to the menu being disabled until its called? It all looks to function properly though. Is it also possible to extend this to add normal and highlight color to the buttons text field too?

    Cheers again!

  • Could it be due to the menu being disabled until its called?

    More likely that it's enabled (for a frame) before AC has initilised. Try this:

    using UnityEngine;
    using AC;
    
    public class IndirectlySelectItems : MonoBehaviour
    {
    
        private int selectedIndex = -1;
        private MenuInventoryBox inventoryBox;
    
        [SerializeField] private Color normalColor = Color.white;
        [SerializeField] private Color highlightColor = Color.blue;
    
        private void OnEnable ()
        {
            if (KickStarter.playerMenus != null)
            {
                Menu thisMenu = KickStarter.playerMenus.GetMenuWithCanvas (GetComponent <Canvas>());
                inventoryBox = thisMenu.GetElementWithName ("InventoryBox") as MenuInventoryBox;
                SetSelectedIndex (0);
            }
        }
    
        public void OnClick_Select ()
        {
            InvItem selectedItem = inventoryBox.GetItem (selectedIndex);
            if (selectedItem != null)
            {
                selectedItem.RunUseInteraction ();
            }
        }
    
        public void OnClick_Up ()
        {
            int newSelectedIndex = selectedIndex - 1;
            if (newSelectedIndex < 0)
            {
                newSelectedIndex = 0;
                inventoryBox.Shift (AC_ShiftInventory.ShiftPrevious, 1);
            }
            SetSelectedIndex (newSelectedIndex);
        }
    
        public void OnClick_Down ()
        {
            int newSelectedIndex = selectedIndex + 1;;
            if (newSelectedIndex >= inventoryBox.GetNumSlots ())
            {
                newSelectedIndex = inventoryBox.GetNumSlots () - 1;
                inventoryBox.Shift (AC_ShiftInventory.ShiftNext, 1);
            }
            SetSelectedIndex (newSelectedIndex);
        }
    
        private void SetSelectedIndex (int value)
        {
            if (selectedIndex != value)
            {
                if (selectedIndex >= 0 && selectedIndex < inventoryBox.GetNumSlots ())
                {
                    inventoryBox.uiSlots[selectedIndex].SetColour (normalColor);
                }
                if (value >= 0 && value < inventoryBox.GetNumSlots ())
                {
                    inventoryBox.uiSlots[value].SetColour (highlightColor);;
                }
    
                selectedIndex = value;
            }
        }
    
    }
    

    Is it also possible to extend this to add normal and highlight color to the buttons text field too?

    You can adapt the script if necessary, but the color tinting is just set to affect whatever's set in your Button's "Target Graphic" field. If you set that to your text instead of the image, it'll tint that instead.

  • Morning Chris,

    More likely that it's enabled (for a frame) before AC has initilised.

    Ah of course, makes sense...yep, that's cleared it.

    You can adapt the script if necessary, but the color tinting is just set to affect whatever's set in your Button's "Target Graphic" field.

    Great, I'll have a tinker. Thanks again for your help, its really appreciated.

    Cheers,
    Tim

  • Hi everyone,

    This is probably a long shot but I've been revisiting this script that Chris kindly helped me with a while back.

    I updated to AC 1.74.2 and ran into this error however:

    IndirectlySelectItems.cs(60,53): error CS7036: There is no argument given that corresponds to the required formal parameter 'newHighlightedColour' of 'UISlot.SetColour(Color, Color)'

    Can anyone possibly point me in the right direction?

    Cheers,
    Tim

  • edited October 2021

    SetColour has since been replaced with SetColours, which allows for both the normal and highlighted colours to be set as a pair.

    Try this replacement for the SetSelectedIndex function:

    private void SetSelectedIndex (int value)
    {
        if (selectedIndex != value)
        {
            if (selectedIndex >= 0 && selectedIndex < inventoryBox.GetNumSlots ())
            {
                inventoryBox.uiSlots[selectedIndex].SetColours (normalColor, normalColor);
            }
            if (value >= 0 && value < inventoryBox.GetNumSlots ())
            {
                inventoryBox.uiSlots[value].SetColours (highlightColor, highlightColor);
            }
    
            selectedIndex = value;
        }
    }
    
  • Hi Chris,

    That's fantastic, thanks. Its cleared the error.

    I tried changing it myself but got into trouble as it threw another error, now I relaise I only passed (normalColor) and not (normalColor, normalColor) .

    Cheers,
    Tim

  • Sorry to ressurect this thread - I found it very helpful, but one thing I couldn't figure out is how to trigger an inventory slot's active graphic? When I hover/select an item with the mouse, it switches to a different sprite I defined, but I couldn't deduce a way to trigger that when I force a selection via code in this way.

  • The SetImage function can be used to update an element slot's texture.

    However, you'll also need to prevent AC from automatically setting this by itself. Try adding the following, but also setting the InventoryBox element's Display type to Text Only.

    private void Update ()
    {
        for (int i = 0; i < inventoryBox.uiSlots.Length; i++)
        {
            InvInstance invInstance = inventoryBox.GetInstance (i);
            if (!InvInstance.IsValid (invInstance))
            {
                inventoryBox.uiSlots[i].SetImage (null);
                continue;
            }
    
            Texture2D texture = invInstance.Tex;
            if (i == selectedIndex && invInstance.SelectedTex)
            {
                texture = invInstance.SelectedTex;
            }
            inventoryBox.uiSlots[i].SetImage (texture);
        }
    }
    
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.