• We are looking for a content creator to help market our assets and a future unannounced project. More details are on this page.

Shaped Grid button navigation issue

Egnech

New member
Hello!
I'm working on RE styled inventory, which player can control with keyboard, mouse, and gamepad.
Looks like there are no issues with mouse navigation, but something is not right with the buttons control (both keyboard and gamepad)

So, the issue is when I'm navigating inventory with buttons, I assume that it takes me 4 button presses to get from very left to the opsive logo at right.

Screenshot 2022-01-19 at 12.18.50.png

But in reality it takes 6, so once again I can assume that cursor is iterating on each view slot instead of the item itself. And it is very frustrating.
My question is, is it by design or I do smth wrong with settings? And how can I make it navigate by items?
 

Egnech

New member
I think I found another one strange issue, when I'm moving the item inside the inventory it visualise the item bounds (to drop) in a wrong way.
Check out this gif
 

Sangemdoko

Moderator
Staff member
The first issue is not really a bug, it works that way by design. Each slot is an ItemViewSlot, and moving in the grid moves per slot, not per item.
Changing this would require some changes to the GridNavigation system.
I will have a look at it and come back to you with potential solutions, but I can't promise to add this feature in the next update.
To be fair I'm not really sure it would be able to work in all scenarios, since for moving items for example, you'll want to have control over the exact slot. So the solution would require to have some sort of toggle to navigate per slot or per item.

The second one with the gif does seem to be a bug, I will need to investigate this in more detail. I'll let you know as soon as I find a solution.
 

Sangemdoko

Moderator
Staff member
It took me awhile to implement but I fixed the second issue and implemented the feature you requested for the first issue. Both will be part of the next update coming out very soon. I really hope it works like you want. You'll be able to test it out in Feature scene 7 Item Shape Grid once the update is out.
 

Egnech

New member
Hello! I've just checked the latest version, and yes this is exactly what I was looking for! Thank you!
But I think I have found a tiny bug. Check out the link below

https://jumpshare.com/v/vPpTgttyncRMrAtN6bDS
Unfortunately I can capture buttons press, but trust me I'm pressing corresponding buttons xD

As you can see I'm trying to move Sword Item to the left, but the space is occupied by the Flag, and as the condition is not passed, the Sword item return on it's original position, and after this I can't move cursor anyway excepts to the left. It only happen when I'm trying to navigate inventory with keyboard (or controller), everything looks good with mouse. I think it has something to do with the recent updates, because I don't remember it to happen before.
 

Sangemdoko

Moderator
Staff member
After many hours of trying to figure this out I realised I made a wrong assumption... I was sure that empty slots returned true when checking if they were anchors... turns out it wasn't

So here is the first change in the ItemShapeGridData script:

Code:
/// <summary>
/// Item Shape Grid Data.
/// </summary>
public class ItemShapeGridData : MonoBehaviour
{
    /// <summary>
    /// The struct for data of the the grid element.
    /// </summary>
    public struct GridElementData
    {
        public static GridElementData None => new GridElementData();
        private bool m_IsAnchor;
        
        public ItemInfo ItemInfo { get; private set; }
        public ItemStack ItemStack { get => ItemInfo.ItemStack;}
        public bool IsAnchor { get => m_IsAnchor || IsEmpty; }
        public bool IsEmpty => ItemStack == null;
        public bool IsOccupied => ItemStack != null;
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="itemStack">The item stack.</param>
        /// <param name="isAnchor">Is the element an anchor.</param>
        public GridElementData(ItemStack itemStack, bool isAnchor)
        {
            //Debug.Log(itemStack);
            ItemInfo = new ItemInfo(itemStack);
            m_IsAnchor = isAnchor;
            //Debug.Log(IsOccupied);
        }
        /// <summary>
        /// To string.
        /// </summary>
        /// <returns>The string.</returns>
        public override string ToString()
        {
            return $"Grid ElementData : [Is Anchor '{IsAnchor}' -> {ItemStack}]";
        }
    }

Then I remade the per item navigation because I rewrote 10 times trying to figure out why it wasn't working.

So in the ItemShapeGrid replaced/add those functions:
Code:
/// <summary>
/// Update the navigation.
/// </summary>
protected virtual void UpdateNavigation()
{
    if (m_NavigationType == ItemShapeNavigation.Custom) {
        return;
    }
    if (m_NavigationType == ItemShapeNavigation.PerSlot) {
        SetGridLayoutGroupNavigation(m_GridLayoutGroup, false);
        return;
    }
    if (m_NavigationType == ItemShapeNavigation.PerItem) {
        SetNavigationPerItem(m_GridLayoutGroup);
        return;
    }
}
/// <summary>
/// Set the grid layout group navigation.
/// </summary>
/// <param name="gridLayoutGroup">The grid layout group.</param>
/// <param name="gridWrap">Should the navigation wrap around?</param>
public void SetNavigationPerItem(GridLayoutGroup gridLayoutGroup)
{
    Vector2Int gridSize = Vector2Int.zero;
    if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.Flexible) {
        return;
    }
    var gridParent = gridLayoutGroup.transform;
    
    if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount) {
        gridSize = new Vector2Int(gridLayoutGroup.constraintCount,
            gridParent.childCount / gridLayoutGroup.constraintCount);
    }
    if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedRowCount) {
        gridSize = new Vector2Int(gridParent.childCount / gridLayoutGroup.constraintCount,
            gridLayoutGroup.constraintCount);
    }
    var horizontalStartAxis = gridLayoutGroup.startAxis == GridLayoutGroup.Axis.Horizontal;
    
    // Go through all the anchors first
    for (int i = 0; i < gridSize.y; i++) {
        for (int j = 0; j < gridSize.x; j++) {
          
            var index = horizontalStartAxis ?
                i * gridSize.x + j :
                j * gridSize.y + i;
            var currentItemViewSlot = m_ItemViewSlots[index];
            if (IsSlotAnchor(currentItemViewSlot) == false) { continue; }
            var navigation = currentItemViewSlot.navigation;
            navigation.mode = Navigation.Mode.Explicit;
            
            navigation.selectOnUp =  GetAnchorSlotInDirection(Vector2Int.up,  j, i, horizontalStartAxis, gridSize, currentItemViewSlot);
            navigation.selectOnDown =  GetAnchorSlotInDirection(Vector2Int.down, j, i, horizontalStartAxis, gridSize, currentItemViewSlot);
            navigation.selectOnLeft = GetAnchorSlotInDirection(Vector2Int.left, j, i, horizontalStartAxis, gridSize, currentItemViewSlot);
            navigation.selectOnRight = GetAnchorSlotInDirection(Vector2Int.right, j, i, horizontalStartAxis, gridSize, currentItemViewSlot);
            currentItemViewSlot.navigation = navigation;
        }
    }
    // Go through all the non-anchors and copy the anchors navigation
    for (int i = 0; i < gridSize.y; i++) {
        for (int j = 0; j < gridSize.x; j++) {
          
            var index = horizontalStartAxis ?
                i * gridSize.x + j :
                j * gridSize.y + i;
            
            var currentItemViewSlot = m_ItemViewSlots[index];
            var itemShapeView = currentItemViewSlot.ItemView.gameObject.GetCachedComponent<ItemShapeItemView>();
            if (itemShapeView.IsAnchor) { continue; }
            
            var anchorIndex = itemShapeView.AnchorIndex;
            if (anchorIndex < 0 || anchorIndex >= m_ItemViewSlots.Length) {
                continue;
            }
            var anchorItemViewSlot = m_ItemViewSlots[anchorIndex];
            currentItemViewSlot.navigation = anchorItemViewSlot.navigation;
        }
    }
}
protected static bool IsSlotAnchor(ItemViewSlot currentItemViewSlot)
{
    var itemShapeView = currentItemViewSlot.ItemView.gameObject.GetCachedComponent<ItemShapeItemView>();
    return itemShapeView.IsAnchor;
}
/// <summary>
/// Get the Anchor slot above the specified position.
/// </summary>
/// <param name="direction">The direction in which to search</param>
/// <param name="x">The x position.</param>
/// <param name="y">The y position.</param>
/// <param name="startHorizontal">Does the grid start in horizontal axis?</param>
/// <param name="gridSize">The grid size.</param>
/// <param name="exception">Ingore that item view slot.</param>
/// <returns>The anchor Item View Slot above the current position.</returns>
private ItemViewSlot GetAnchorSlotInDirection(Vector2Int direction, int x, int y, bool startHorizontal, Vector2Int gridSize, ItemViewSlot exception)
{
    if (direction == Vector2Int.zero) {
        Debug.LogWarning("Searching in not direction is not allowed");
        return null;
    }
    
    while ( y >= 0 && y < gridSize.y && x >= 0 && x < gridSize.x) {
        
        var index = startHorizontal?
            y * gridSize.x + x:
            x * gridSize.y + y;
        
        if (index < 0 || index >= m_ItemViewSlots.Length) {
            continue;
        }
        
        if (TryGetAnchorItemViewSlot(index, out var viewSlot) && viewSlot != exception) {
            return viewSlot;
        }
        if (startHorizontal) {
            x += direction.x;
            y -= direction.y;
        } else {
            x += direction.x;
            y -= direction.y;
        }
        
    }
    return null;
}

With this approach all itemviewslots can move to another.
The only issue I could find with this approach is if you have Non-rectangle shapes some items can end up surrounded by bigger weird shapes which can make the navigation skip the small item in the middle... Unfortunatl that's a design problem not the code. So I won't try to fix that.

Note that you can override the UpdateNavigation function of the ItemShapeGrid if you wish to code your own custom solution. This way if the current solution isn't versatile enough you can still use the ItemShapeGrid and only customise the navigation.
 

Egnech

New member
Thank you! Looks like this has fixed the issue, I will continue testing and let you know if anything does wrong!
Good for me I'm using only rectangle shapes.
 
Top