H. Idris / Game UI/UX Designer

Inventories, shops and leaderboards: fast lists in Unity

The inventory opens and the game stutters. Five hundred items became five hundred GameObjects, each with a layout group, all instantiated in one frame. Long lists are the most common performance and usability failure in game interfaces, and both halves are fixable at design time.

Written by Hadjoudj Idris, Game UI/UX Designer. Six years of menus, HUDs and full interface systems, including a title shipped on Steam.

Hire me

Every game ends up with a long list: an inventory, a shop, a leaderboard, a quest log, a settings page, a mod list, a save browser. They look like the simplest screen in the game and they are usually the one that stutters, loses the player's place, and cannot be navigated with a pad.

Why a long list is not just a long screen

A static screen is built once. A list is built repeatedly, from data that changes, at a length nobody knows in advance, while the player scrolls it.

  • Instantiation cost. Five hundred rows means five hundred objects created, each with its own components, in whichever frame the screen opens.
  • Layout cost. If each row has a layout group and the container has a content size fitter, opening the screen recalculates the whole thing, sometimes more than once.
  • Rebuild cost. Everything is on one canvas, so any change to any row dirties all of it, for the reasons in why your Unity menus tank the frame rate.
  • Memory and texture cost. Five hundred item icons is five hundred sprites, and if they are loaded individually rather than atlased, that is where your load spike is.

The player, meanwhile, does not care about any of that. They care that scrolling is smooth, that they can find one item, and that the list does not lose their position when they come back to it.

Pooling: build ten rows, not five hundred

The standard answer is a recycled scroll rect: create only as many row objects as fit on screen plus a couple of spares, and as a row leaves the top, move it to the bottom and rebind it to the next piece of data. Ten objects serve a list of any length.

  1. Rows are containers, data is content. A row does not belong to an item, it displays whichever item it is currently pointed at.
  2. Rebinding must be complete. Every field on the row is set on every rebind, including the ones that are usually empty. A row that shows a leftover badge from the previous item is the classic pooling bug.
  3. Reset state as well as content. Selection, focus, animation progress, tween state, expanded or collapsed. If a row was mid-animation when it recycled, it must not arrive mid-animation.
  4. Fixed-height rows are dramatically easier. Variable heights mean the system cannot know the scroll extent without measuring everything, which is solvable and costs real complexity.
  5. Position by index, not by layout group. With uniform rows, position is index multiplied by height. No layout group, no fitter, no recalculation.

Design the row so it can be pooled

This is where interface design meets implementation, and where a design that ignored it makes pooling impossible.

  • Uniform height, or a small number of fixed heights. A design where every row is a different height because the description wraps is a design decision with a large engineering bill attached.
  • No per-row layout groups. Anchor the elements inside the row. The row's internal layout is known and fixed, so it does not need calculating.
  • Every element always present. Rows show and hide elements rather than adding and removing them, so the object graph is identical for every row.
  • Design the longest content case. The longest item name, the biggest number, the most badges. Rows cannot grow to fit.
  • Keep the row shallow. A row with thirty nested objects, multiplied by the pool, is your instantiation cost.
  • One atlas for row art. Icons drawn from six different atlases break batching on every row, and a list is where that multiplies.

The states nobody puts on the screen list

A list is not one screen, it is at least six, and the missing ones are found by QA in the last month or by players in the first week.

StateWhat it needs
EmptyAn explanation and a way out, not a blank panel. "No items yet" plus the action that gets one
LoadingA skeleton or a spinner in the list's own shape, so the layout does not jump when data arrives
Error or offlineWhat failed, and a retry. Common for leaderboards and stores
Filtered to nothingDifferent from empty: say which filter is hiding everything and offer to clear it
One itemLayouts designed around a full grid often look broken with a single entry
Very longScrollbar, jump-to-letter or section headers, and a count so the player knows the scale

These are cheap to design alongside the main state and expensive to retrofit into a finished screen, which is why they belong on the screen list from the start. The brief checklist has them in the list of screens teams routinely undercount.

Controller focus inside a scroll view

This is the part that gets discovered on the console build, two weeks before submission. A scroll view driven by a stick has rules a mouse never needed.

  • The view follows focus, not the other way round. Moving focus down past the visible area scrolls the list so the focused row is fully visible, with a little margin, not clipped at the edge.
  • Scroll in whole rows. Landing focus on a half-visible row reads as broken.
  • Give the pad a fast path. Shoulder buttons for page jumps or category jumps, because holding a stick through four hundred items is not navigation.
  • Decide the wrap rule and keep it. Wrapping from the last row to the first is convenient in short lists and disorienting in long ones. Pick per list length and be consistent.
  • Sticky headers must not take focus. They are decoration; focus passes through them to the next row.
  • Restore focus when the player returns. Coming back from an item detail should land on the item they were looking at, not at the top of the list.
  • Pooled rows must not hold focus. When a focused row recycles, focus has to move with the data, not with the object. This is the single most common pooling-plus-controller bug.

The general rules for focus, prompts and navigation maps are in designing game UI for controllers; the list is where they are hardest.

Sorting, filtering and search

  • Show the active filters. A player who forgot they filtered by rarity will report a bug about missing items.
  • Show the count. "24 of 312" tells the player what the filter did.
  • Keep the position where it makes sense. After sorting, the selected item should stay selected and stay in view.
  • One tap to clear. Every filter set needs a reset that is not a tour of six dropdowns.
  • Search needs a controller answer. A text field means the platform keyboard, so offer category jumps or letter jumps as the primary path and keep search as the secondary one.

Grids, comparison and drag

Inventories are usually grids rather than lists, and grids add their own problems, all of which are worse on a pad.

  • The ragged last row. Define what pressing down from the last full row does when the row beneath is half empty. Left unspecified, the engine's guess will be wrong.
  • Comparison is the point of an inventory. Showing the equipped item next to the highlighted one, with the difference marked, is the feature players remember. It is also the layout that decides your grid width.
  • Replace drag and drop with pick up, move, place. Two presses, works with every input device, and removes an entire category of dropped-item bugs.
  • Stack counts, rarity and equipped state all need a non-colour signal, for the reasons in game UI accessibility.
  • The Stellar Despair inventory was built around exactly this: a grid that reads as a grid, a detail panel that does the comparing, and a state for every way an item can be.

Icons, thumbnails and the load spike

  • Do not load every icon when the screen opens. Load what is visible, plus a screen ahead, and release what scrolled away.
  • Always have a placeholder. An icon that pops in late is fine; a hole in the layout is not.
  • Atlas by category, so a filtered view pulls from one atlas rather than forty.
  • Size the source art to the display size. A 512-pixel icon rendered at 64 costs memory and gains nothing.
  • Watch remote images especially, in leaderboards and stores, where every avatar is a network request and a decode.

If you are on UI Toolkit

This is one of the places UI Toolkit is genuinely stronger. ListView virtualises by default, so recycling is a built-in behaviour rather than something you build or buy, and it has bind and unbind hooks that map exactly onto the rebinding discipline above. Unity 6 also added runtime data binding, which removes much of the boilerplate between a data source and the rows.

  • Use ListView rather than a scroll view full of elements. That is the whole feature.
  • Implement unbind, not just bind. The reset discipline is the same and it is where leftover state comes from.
  • Fixed item height is still the fast path, and dynamic height is still the expensive one.
  • Keep the item template shallow, for the same reasons as a pooled prefab.

If your game is built mostly of lists and data screens, this is one of the strongest arguments in the uGUI or UI Toolkit decision.

A list is the only screen in your game where the player will notice one dropped frame, because they are the one moving it.

A test to run on your own list

  1. Fill it with the largest number of items your game can produce, not a test set of ten.
  2. Open the screen while watching the profiler. Note the spike on the frame it opens.
  3. Scroll fast, on the target device. Look for hitching as rows rebind.
  4. Scroll to the middle, open an item, come back. Are you where you were?
  5. Filter to nothing. Is there a state for that, and does it say which filter did it?
  6. Empty the inventory completely. Is there a state for that?
  7. Unplug the mouse. Navigate the entire list, sort it, filter it and use one item with the pad alone.
  8. Focus a row, then scroll it off screen with the stick. Does focus stay with the item?
  9. Check the last row of a grid, pressing down and right from every position in it.
  10. Look at the frame debugger while the list is on screen and count the draw calls per row.
At what list length do I need pooling?

Roughly fifty rows is where it starts to be worth it, and a few hundred is where it becomes necessary, though the real trigger is row complexity rather than count. Fifty simple rows are fine; fifty rows with nested layout groups, several sprites and an Animator each are not.

Should I write my own recycled scroll rect or buy one?

For a standard vertical list of uniform rows, the asset store versions are mature and save days. Write your own when you need something unusual, such as mixed row types, variable heights with sticky headers, or grid plus list modes in one view. On UI Toolkit, use ListView and write nothing.

Why do my list rows show the wrong data when I scroll fast?

Incomplete rebinding. A row is being reused with a field that was set for a previous item and not reset, most often an optional badge, a highlight or an animation state. The fix is a rebind that sets every field unconditionally, including clearing the ones that do not apply.

How do I keep the scroll position when returning to a list?

Store the index of the item that was focused or centred, not the pixel offset, since the pixel offset is meaningless if the list has been filtered or sorted since. Restore by scrolling that index into view. It is a small feature and it is one of the things players notice immediately when it is missing.

Do variable-height rows really cost that much?

They cost enough to be a deliberate decision rather than a design accident. The system cannot know the total scroll extent without measuring every row, so either you measure them all upfront, estimate and correct as you go, or cap the content so heights are predictable. Designing to a fixed height, with truncation and a detail panel, is usually the better trade.

How should long lists work on mobile?

Fixed-height rows, larger touch targets than a desktop list, momentum scrolling that feels native, and sections or a jump control instead of expecting long swipes. Keep destructive actions off the row itself, since a mis-tap while scrolling is the most common way players delete something they wanted. See mobile game UI design.

Hadjoudj Idris

Game UI/UX Designer, Remote, worldwide

Need this done properly on your game?

Menus, HUDs, a full UI system, or one screen that isn't working. Tell me what you're building and I'll tell you honestly whether I'm the right fit, and what it would cost.

UpworkTop Rated100% Job Success

Contact

Have a game that needs an interface?

Menus, HUDs, full UI systems or a single screen that isn't working. Tell me what you're building and I'll tell you honestly whether I'm the right fit.

Email
Discord
Résumé Download PDF
Based Remote, worldwide