How to enable UI collision while timeScale is 0?

I’m currently working on an inventory menu for my game where you use the directional keys to move a cursor and select objects (like in pretty much all of the Resident Evil games, for example). In particular I’m making a grid/“tetris” inventory system. In order to determine whether a given grid slot is available (and therefore selectable), I basically gave both my slot objects and my item objects rigidbodies and box colliders, and check for OnTriggerStay. For hours I couldn’t figure out why it wasn’t working until I realized that I pause the game while the inventory menu is open by setting Time.timeScale to 0, and collisions don’t calculate while the time is 0.

I’ve done some internet research and I keep seeing people sing the praises of using timeScale to pause your game, only to ignore people pointing out the physics problem, which in my view is a pretty serious oversight unless there’s some obvious better way to do this than what I’ve come up with. Is there a way to enable particular colliders while timeScale is 0, or is timeScale not actually a very good way to handle pausing the game, contrary to what I’ve seen so many people claim? Or, alternatively, am I overlooking a better method for checking for overlap between two UI objects that doesn’t involve using Unity’s physics system?

Thanks in advance for any assistance. I don’t think my question should require sharing any of my code but if there’s something you need to see to answer the question please let me know!

151400-untitled.png

Consider you have the following:

public class Inventory {
    public InventorySlot[,] slots = new InventorySlot[7, 5];
}

public class InventorySlot {

    InventoryItem currentOccupier;

    public bool IsFreeFor(InventoryItem item) {
        return currentOccupier == null || currentOccupier == item;
    }
}

public class InventoryItem {
    public Vector3[] layoutPositions; // filled with yellow boxes (and the blue one)
}

So, your item is centered in the green box with the yellow and blue boxes being its layout.
It also has a Vector3[] layoutPositions array containing all the yellow (and blue) boxes
(they’re all local positions which means they’re related to the center. that’s why the blue box has the values it does. It’s all relative to the center of the item)

it is currently located at position [3, 2] in your inventory.

If I want to move it up I have to check the red cross positions. You can get those by moving all your points up one step resulting in:

Inventory perspective:

green [3, 2] → [3, 3]

blue [1, 1] → [1, 2]

and you can get those coordinates by calculating

Vector3 newPosition = item.transform.position + Vector3.up;

itemCoordX = Mathf.RoundToInt(newPosition.x);
itemCoordY = Mathf.RoundToInt(newPosition.y);

Vector3 blueBoxPosition = newPosition + layoutPositions[blueBoxIndex];

blueBoxCoordX = Mathf.RoundToInt(blueBoxPosition.x);
blueBoxCoordY = Mathf.RoundToInt(blueBoxPosition.y);

// and do this for all the yellow boxes as well

now all you have to do is to do is check whether or not those positions in the inventory are already taken up by something:

bool inventorySlotEmpty =
    slots[itemCoordX, itemCoordY].IsFreeFor(item) && 
    slots[blueBoxCoordX,blueBoxCoordY].IsFreeFor(item) ;

NOTE: you also calculate the array positions of all your yellow boxes and check if their new positions are empty as well. that’s easy to do using a for loop or whatnot.

Now if **inventorySlotEmpty ** is true, you can move the item saying item.transform.position = item.transform.position + Vector3.up;

when it comes down to rotating those positions, there are several ways but the easiest way I can think of that doesnt require too much math is doing this for each layoutPositions point:

layoutPositions *= new Vector3(*

item.transform.right * layoutPositions*.x,*
item.transform.forward * layoutPositions*.y*
);

I’m sorry, but this is a terrible way to handle this. Physics are made for physics, not UI.


(If you really wanted to do this, you could create a Physics Scene for your UI, that is independent from the rest of the game. But please, be sensible.)


Here’s an idea of a script that would work for you:

You have an array that represents your whole inventory.

Every UI element representing a slot has an index matching that array.

You then implement a dragging script using the standard unity UI tools, and get on which object/slot the item is dropped, for example using a graphics raycaster (a raycast for UI elements).

If your raycast got a slot object, get the script, get the index, check if the array position is already occupied, and if it is not, update the array. Otherwise, put the item back in its original position

An example of such a script: Unity Inventory: UI Drag & Drop Tutorial - YouTube