Overview

8 Separating asset data and logic

This chapter explains how to separate asset data from logic so menus in a Unity game become easier to build, easier to debug, and faster to iterate on. Instead of forcing engineers and artists to repeatedly pass prefabs back and forth, the chapter proposes a workflow where artists prepare UI prefabs once and mark the dynamic elements that code needs to control. That approach reduces dependency on renaming objects, avoids slow and fragile lookup methods, and keeps menus more self-contained.

To make this practical, the chapter introduces a reusable reference component that lets UI creators assign stable keys to GameObjects, text fields, and buttons. Engineers can then fetch those references directly without relying on expensive scene searches or overexposing prefabs through public fields. The chapter also separates persistent menu-related information into its own metadata structure, such as best time and current menu state, and stores it independently from core gameplay data.

The chapter then walks through applying this design to several menus: the main menu, game-over menu, pause menu, and in-game UI. Each menu follows a simple pattern of initializing references once, showing or updating data when needed, and hiding the UI when it is no longer active. A small static helper is used for shared formatting logic, but otherwise each menu remains independent. The result is a data-oriented menu system that supports future expansion, keeps code readable, and avoids unnecessary reuse that could create bugs or make debugging harder.

A typical menu development flow, where assets need to be passed back and forth between art and engineering due to missing assets and code.
An ideal development flow, where assets pass smoothly from art to engineering.
Our main menu has two UI components: the title and a button. The title is a static component because it does not change. As engineers, we don't care about the main menu title because we don't modify it in code. The 'Start' button, on the other hand, is a dynamic component. It calls one of our functions, and ideally, we want to set which function it calls in our script.
Our proposed menu changes.
The main menu and the data needed to show it.
The main menu prefab, including the UI objects we need to access using code.
The GUIRef script, with keys and references assigned to the UI components we need access to. This GUIRef script lets UI artists assign multiple components, including GameObjects, 3D TextMeshPro objects, 2D TextMeshProUGUI objects, Images, RawImages, Buttons, Sliders, and even GameObjects with an Animation component attached. In this example, the Unity 2D TextMeshProUGUI component “BestTime Value”, used to show the best time string in the menu, is assigned to the GUI Ref Text GUI array with the key “BestTime”. The buttons “New Game” and “Continue Game” are assigned to the GUI Ref Buttons array with the keys “Start” and “Continue”.
Here, renaming the UI object 'BestTime Value' to 'Highscore value' doesn’t affect our reference because the key remains the same.
Separating our main menu script from the main menu prefab allows us to take back control from Unity on when our script runs.
Our main menu script, MainMenuVisual, allocates the UI GameObject using the AssetManager. It has functions to show and hide the main menu. The UI GameObject holds the GUIRef component that references all the dynamic UI elements.
The game over menu and the data needed to show it.
The GUIRef script for our game over menu.
StartGame() initializes the values in GameData so a new game can start. ContinueGame() loads the saved GameData values so the game can continue. When we set the menu state to IN_GAME, the game begins according to the values set in GameData. If the values are set for a new game, the player plays a new game. If they are set to previously saved values, the player continues their game.
A shop menu, with a top bar showing our virtual currency, a bottom navigation bar, a container showing items for sale, and a container to show our inventory.
Our shop menu can be broken down into multiple nested prefabs, and each one can have its own GUIRef script.
The ideal, straightforward development flow, which does not require any back and forth between art and engineering.

Summary

  • We want our game development flow to be as linear as possible, with as little back-and-forth as possible.
  • Dynamic components in assets should be easily accessible to engineers.
  • Separating asset data from the logic reduces code complexity by giving us a single, straightforward way to access the necessary data.
  • The GUIRef script is a good example of a single script that makes it easy for artists to mark which components of a menu are dynamic and require handling by engineers.
  • The GUIRef script enables us to share assets with third-party developers without exposing our entire codebase.
  • The GUIRef script separates implementations by data type to help the debugger identify problems.
  • The MetaData is data that exists outside of the core gameplay. For example, virtual currency, unlocked characters, or, in the case of our survivor game, the best time reached so far.
  • Every menu should have its own implementation. This reduces code complexity because changing one menu does not affect other menus, making it easier for QA to test what has changed, and makes it less likely that bugs will be introduced in other menus.
  • Only code that is single-purpose and can be moved to a static function should be shared between menus.
  • A state machine is an easy way to control our menu system and makes it easy to know which menu we are on during debugging.
  • To build complex menus, we can use nested prefabs and, in turn, nested GUIRefs.

FAQ

Why should asset data be separated from logic in Unity menus?Separating asset data from logic reduces code complexity, makes menus easier to modify, and speeds up development by letting artists prepare prefabs without constant back-and-forth with engineers.
What is the main idea behind using data-oriented design for menus?With data-oriented design, you focus on the specific data each menu needs and implement each menu separately instead of building one generic system that tries to handle everything.
What kinds of UI elements are considered dynamic instead of static?Dynamic UI elements are the ones that must be changed in code, such as button actions, text values like best time or gold, and UI elements that need to be shown or hidden based on game state.
Why does the chapter recommend a GUIRef script for prefabs?GUIRef lets artists mark important UI objects with stable keys, so engineers can access them in code without using slow, fragile methods like GameObject.Find() or exposing the whole prefab to the codebase.
Why use arrays of key/value structs instead of a Dictionary for GUIRef?The chapter uses arrays because the menus have very little data and references are usually retrieved only once during initialization. A Dictionary would add extra memory use and hashing overhead without much benefit.
Why are separate functions like GetButton() and GetTextGUI() used instead of a generic GetComponent chain?Separate functions avoid unnecessary GetComponent() calls, improve performance, and make debugging clearer because each function handles one specific type of UI component.
Why is MetaData used for best time and menu state?These values belong to the meta layer, meaning they exist outside the core gameplay. Best time and menu state are not part of the game simulation itself, so they are stored separately from GameData.
Why does the game use a menu state machine?A menu state machine tracks which menu is active and controls hiding and showing menus in a clear way, making it easy to switch between main menu, in-game, pause, and game-over states.
Why does the main menu need an InGame boolean in GameData?The InGame boolean tells the game whether a save file represents an unfinished run. That way, the Continue button is only enabled when there is actually a game in progress to resume.
Why should shared UI code only be moved into static helper functions when it is truly identical?Because menus tend to evolve differently, shared logic can become complex and bug-prone. The chapter recommends sharing only identical operations through static helpers so each menu stays independent and easier to maintain.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • High Performance Unity Game Development ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • High Performance Unity Game Development ebook for free