A complete game shell built on Luna + the CupkekGames sibling packages — a save-aware main menu, a persistent in-game HUD with reactive currency/XP chips, a session-scoped pause menu, a two-area scene loop with a small combat encounter, an inventory + equipment screen with drag-and-drop and a drop-table loot loop, a save/load screen, and a tabbed settings panel with input rebinding — all wired through Luna's navigation graphs, the Sequencer, and Newtonsoft JSON saves.
This is the "what does a real Luna game look like?" demo. Heavier than Showcase — it requires the full CupkekGames sibling-package set, installed via the Package Manager window.
Lives at Samples~/GameFull/ in com.cupkekgames.luna.
Four steps after importing Luna from the Asset Store. The order matters: packages first, sample import second (the warning below explains why).
Window > Package Manager, choose + > Install package from git URL and paste https://github.com/Cupkek-Games/CupkekGames-PackageManager.git.Tools > CupkekGames > Package Manager and click Install GameFull Packages. It adds the CupkekGames UPM scoped registry (https://www.docs.cupkek.games/upm) to your Packages/manifest.json and installs the required CupkekGames sibling packages atomically (one manifest write, one domain reload). From then on, the window auto-opens on project load if any sibling packages are missing.Window > Package Manager > In Project > LunaUI > Samples. GameFull's UXML references Essentials sample assets (theme, fonts, Panel Settings, prefab LunaUIManager.prefab, UXML primitives, audio) via sibling-relative paths, so both samples must land as siblings under Assets/Samples/LunaUI/<version>/.00_LunaUIDemoFullInitialization.unity (at the GameFull sample root, sorts first) and press Play. The Sequencer registers services, then loads the main menu; New Game / Continue / Load then transitions into the game scene.⚠️ Do not import the GameFull sample before step 2. GameFull's scripts compile against the sibling packages; importing the sample into a project that doesn't have them fills the Console with compile errors. Unity then refuses to load any newly installed tooling (including the Package Manager window itself) until the errors are gone, and reopening the project lands in Safe Mode. Recovery is simple: delete
Assets/Samples/LunaUI/<version>/GameFull, do steps 1 and 2, then re-import. See Troubleshoot.
The Tools > CupkekGames > LunaUI Panel window auto-opens on sample import. Its "Extra: GameFull" card tracks this whole flow: a live count of the sibling packages, a warning if the sample was imported before installing them, and one-click Add/Remove of the demo scenes in Build Settings once everything is present.

The GameFull feature packages are not transitively pulled by the Luna dependency graph — com.cupkekgames.luna only declares its foundation/data deps (data, graphs, services, singletons, pool, fadeables, keyvaluedatabases, prefabloaders, assetfinder, editorui, editorinspector, input). The game-feature packages below are installed by the Package Manager window's Install GameFull Packages button (which also adds the CupkekGames scoped registry); the window itself is a separate bootstrap package installed via git URL (step 1 above).
| Required | What it provides |
|---|---|
Luna's bundled deps (data, graphs, services, singletons, pool, fadeables, keyvaluedatabases, prefabloaders, assetfinder, editorui, editorinspector, input) | bundled with Luna (declared + transitive) |
com.cupkekgames.packagemanager | the Tools > CupkekGames > Package Manager window — the bootstrap installer itself, added via git URL (step 1 above) |
com.cupkekgames.luna | the UI toolkit itself — views, navigation, HUD, transitions |
com.cupkekgames.resources | Wallet (currencies) + ExperienceTracker (XP/level) — the reactive save fields |
com.cupkekgames.gamesave + com.cupkekgames.gamesave.luna | save manager + save-list / AutoSave UI |
com.cupkekgames.newtonsoft | Newtonsoft JSON adapter (SerializationManager / IDataSerializer) |
com.cupkekgames.inventory | Inventory / InventoryItem / IInventoryItemDatabase + the full inventory/equipment UI (InventoryViewPagination, EquipmentRackController, drag-drop, tooltips) — part of the save payload and the on-screen inventory screen |
com.cupkekgames.rpgstats | equipment stat module (IEquipableFeatureRpgModule) + attribute definitions (Attack/Health/Defense) — drives the live equip stat comparison |
com.cupkekgames.data (Data.DropTable) | DropTableSO / DropResult — the loot table rolled by the Find Loot button |
com.cupkekgames.addressableassets | addressable prefab/asset loading used by save data + menus |
com.cupkekgames.scenemanagement | SceneLoader + SceneTransition (scene loads + fade/circle transitions) |
com.cupkekgames.sequencer | the boot pipeline — every scene runs a SequenceRunner |
com.cupkekgames.settings | settings persistence + settings UI controls |
Plus Unity registry packages: com.unity.nuget.newtonsoft-json, com.unity.localization, com.unity.addressables, com.unity.inputsystem. These install transitively with the CupkekGames packages above (newtonsoft, addressableassets, and settings declare them), so no manual step is needed.
The scene-index convention used throughout the scripts: scene 1 = main menu, scene 2 = gameplay.
| Order | Scene | What it does |
|---|---|---|
| 1 | 00_LunaUIDemoFullInitialization.unity (sample root) | Boot scene. Runs the Sequencer to register services, then loads the main menu (start scene index 1). Sorts first in the Hierarchy so build settings pick it up. |
| 2 | Scenes/01_LunaUIDemoFullMainMenu.unity | Main menu — Continue, New Game, Load, Credits, Settings, Quit. |
| 3 | Scenes/02_LunaUIDemoFullBase.unity | Area 1 — the in-game HUD scene; hosts the GameShell screen (HUD bar + a "Save Summary" card). |
| 4 | Scenes/03_LunaUIDemoFullArea2.unity | Area 2 — a second in-game area with a small combat encounter. |
Boot and scene loads run the sequencer-driven loading screen: the startup load is deferred, then in the loaded scene's sequence Boot Nav Graphs → Activate Game Object → Reveal Loading Screen boot the nav hosts and fade the loader out once they report ready (the demo uses a press-to-continue FadeWithInput transition).
GameFull's UI is organized into four navigation graphs. A NavGraphSO asset carries no channel string — it's a forest of NavNode destinations; channels are derived at runtime from node ids, and a node with NavChildMode.Switched acts as a tab container. Each graph is mounted onto a layer by a NavHost, and all mounted hosts merge into a single navigable index, so a "global" graph mounted by an always-loaded host is reachable from any scene.
| Graph | Mounted by | Destinations | Scope |
|---|---|---|---|
0_...Global | LunaUIDemoFullDependencies.prefab (always-loaded, DontDestroyOnLoad) | saveload, settings (Switched container → settings.game / .video / .audio / .controls), confirm (the shared choice popup) | Global — reachable from the menu and in-game |
1_...MainMenu | the main-menu scene | mainmenu (boot-visible), mainmenu.credits, mainmenu.quit | Menu-only |
2_...Global_InGame | GameScope.prefab (in-game scope host) | pause (Switched container) + inventory — both Dim-backdrop overlays, hidden until pushed | In-game-only overlays |
3_...GameScene | the game scene | gamescene (boot-visible GameShell) | In-game scene content |
Graph prefabs live under Navigation/<graph>/. Because settings and saveload sit in the global graph, the pause menu can push them in-game with the same destination keys the main menu uses.
The Area-1 screen is a single GameShell.prefab (Navigation/3_...GameScene/) — one PanelRenderer hosting UXML/Screens/GameHud.uxml, with four cooperating MonoBehaviours that resolve elements by name against the same rendered tree:
GameHudView (Scripts/UI/GameHudView.cs, a UIViewComponent) — the structural/navigation controller. It sets the AreaName label, wires the PauseButton to LunaNavigation.Push(_pauseDest) (destination pause), the InventoryButton to LunaNavigation.Push(_inventoryDest) (destination inventory), and the NextAreaButton to SceneLoader.LoadScene(_nextSceneName, …). The Next-Area button hides itself when _nextSceneName is empty.ResourceChipBinder (Scripts/UI/ResourceChipBinder.cs) — the reactive top-bar binding. It reads the active GameFullSaveData and subscribes to Wallet.OnChanged (Gold/Gem chips roll to the new value via Label.PlayCountTo()) and ExperienceTracker.OnExperienceChanged / OnLevelUp (the XP luna:ProgressBar self-animates via PlayTo(); a level-up plays a scale-punch + shine burst on the level chip). Like the other HUD binders here (SaveSummaryBinder, LootCardController), it extends Luna's PanelRendererBinder, which owns the async panel-delivery lifecycle.SaveSummaryBinder (Scripts/UI/SaveSummaryBinder.cs) — a one-shot binder for the Area-1 center card, showing item count (Inventory.Items.Count), equipped count (EquippedItems.Count), and the save date/version from Metadata.LootCardController (Scripts/UI/Loot/LootCardController.cs) — the loot pipeline. Its optional LootCard (Find Loot) button + LastDropLabel live on Area 1's Loot content card, not the top bar (reward particles fly content → header, never header → header); Area 2 triggers the same pipeline from enemy kills via RollAndGrant. Detailed under Inventory, equipment & loot below.The shared top bar itself is UXML/Shared/HudBar.uxml, instanced into both GameHud.uxml (Area 1) and Area2.uxml (Area 2). It exposes the element-name contract the binders query: GoldAmount, GemAmount, HeroLevel, HeroLevelChip, HeroXpBar (luna:ProgressBar), PlayerName, AreaName, PauseButton, NextAreaButton, InventoryButton. The GoldChip / GemChip carry UIAttractorTargets so loot currency can fly into them, and InventoryButton carries the Items target for item drops. (The loot UI itself — LootCard + LastDropLabel — lives in GameHud.uxml's content card, not the bar.) Styling for the bar, chips, the emerald XP bar and red HP bar, the clay panels, and responsive breakpoints all live in UXML/Screens/GameHud.uss.
The in-game pause menu is owned by a "game scope" whose lifetime is tied to the save session, not the scene — so it survives an Area 1 ↔ Area 2 swap but is torn down and rebuilt when you load a different save (which prevents a stale pause menu from surviving a load).
GameScopeNodeSO (Scripts/UI/GameScopeNodeSO.cs) — a SequencerNodeSO with a Mode { Ensure, Dispose }. It tracks the live scope in statics (s_live = the GameObject, s_liveSession = the GameFullSaveManager.CurrentSave.Data reference it was spawned for), reset at SubsystemRegistration so nothing leaks across play sessions when domain reload is disabled.
NavHost fully unregisters the pause graph before the new one mounts — otherwise you get a duplicate pause destination), then respawns. If no scope is alive, it spawns one and DontDestroyOnLoads it. A same-session scene swap (Next Area) leaves it untouched.GameScope.prefab (Navigation/2_...Global_InGame/) is the scope host: a PauseEscapeTrigger (Scripts/UI.Generic/Pause/PauseEscapeTrigger.cs) that re-arms its EscapeAction and then pushes the pause destination on Esc, plus a NavHost mounting graph 2 with _persistAcrossScenes = true.The pause node points at PauseMenu.prefab (PauseMenuViewExample), a Dim-backdrop overlay whose Continue/Load/Settings/Main Menu/Quit buttons push into the global graph (Load → saveload, Settings → settings); Main Menu confirms through the global confirm destination (PushAsync<int> + ChoicePopupArgs) before leaving the session.
Scenes 02 and 03 use the identical 6-node runner; the only difference is which screen prefab is instanced under the (initially inactive) SceneParent:
02) instances GameShell.prefab → GameHud.uxml + the SaveSummaryBinder card, _areaName = "Area 1", _nextSceneName = 03_LunaUIDemoFullArea2.03) instances Area2.prefab → Area2.uxml + an EncounterController, _areaName = "Area 2", _nextSceneName = 02_LunaUIDemoFullBase.The Next Area button on either HUD calls SceneLoader.LoadScene (by scene name) with the Fade transition, forming the loop.
EncounterController (Scripts/UI/EncounterController.cs) is a deliberately tiny combat demo whose only job is to make save persistence observable. It queries #EnemyName, #EnemyHpBar (a red luna:ProgressBar), #AttackButton, and #RewardLabel. Each Attack drains the enemy's HP (max 30, 10 per hit); on defeat it awards Wallet.Add("Gold", 25) and Experience.AddExp("Hero", 40), shows +25 Gold +40 XP, and spawns the next enemy from { Slime, Bat, Goblin, Wolf }. Those Wallet / Experience mutations fire the same events the HUD's ResourceChipBinder listens to, so the top bar's Gold / XP / Level update live — no polling.
The inventory destination (graph 2, Dim overlay, reachable via the HUD Inventory button or the Pause menu) opens InventoryScreen.prefab (Navigation/2_...Global_InGame/) — one PanelRenderer hosting UXML/Screens/InventoryGameFull.uxml, driven by EquipmentViewPagination (Scripts/UI/Inventory/, a UIViewComponent). It wires the Inventory package UI against the live save:
InventoryViewPagination bound to CurrentSave.Data.Inventory, using the InventoryItemSlot.uxml slot template, a page-button strip, and explicit filter buttons (All / Weapon / Shield / Armor / Potion, all named FilterItemType). The filters are plain functions in EquipmentViewPagination.FilterItems — deliberately not descriptor indices, so button labels can't drift from catalog key order.EquipmentRackController over seven equipment slots (SlotHand / SlotShield / SlotOffhand / SlotHelmet / SlotChest / SlotLeggings / SlotBoots) plus a consumable slot (SlotPotion), bound to CurrentSave.Data.EquippedItems so equipping persists. Each equipment type maps to its UXML slot by name via GameFullEquipmentSlotMap (never by catalog order); slot targeting + accept rules come from the RPGStats bridge (RpgEquipmentSlotTargeting, RpgEquipmentSlotAcceptPolicies).LunaInventoryDragDefaults.RpgStatComparisonProvider + ItemStatDataController render the Attack / Health / Defense delta a candidate item would apply into StatsContainer, aggregated by IEquipableFeatureRpgModule.Aggregation.IItemFeature.BuildTooltipContent composes its tooltip; equipped items gain an (Equipped) footer via LunaInventoryTooltipHelpers.Item data lives in ScriptableObjects/Items/: seven InventoryItemDefinitionSOs (Armor, Axe, Shield, Sword, WoodenSword, PotionHealth, PotionMana), each composed from an EquipableFeature (equipment type + AttributeEffect) or a ConsumableFeature (cooldown), plus an ItemTierFeature (rarity). Icons resolve through ItemSpriteDatabase (a SpriteCatalog) and attributes through the RPGStats/ attribute definitions — all registered at boot via ServiceRegistrySOs.
The Find Loot button on Area 1's Loot content card drives a reward loop that makes the economy observable — and in Area 2, defeating an enemy triggers the exact same pipeline (EncounterController → LootCardController.RollAndGrant, bursting from the Attack button):
LootCardController (Scripts/UI/Loot/LootCardController.cs, on the HUD prefab) rolls DropTable.asset (ScriptableObjects/Items/Droptable/ — a 10-entry table of Gold/Gem currency, Hero XP, and item drops) on each roll, guaranteeing at least one drop.DropResultRouter (Scripts/UI/Loot/) splits results by catalog id — Currencies → Wallet, Experiences → ExperienceTracker, everything else → Inventory.UXML/Components/Atoms/UIAttractorParticleGold/Gem.uxml particles + the LootRewardMotion preset) flies decoratively from the loot card to the chip's UIAttractorTarget — the commit point is the data write, never the animation.NotificationHistory toast and LastDropLabel summarizes the roll.Every scene has a SequenceRunner (_runOnAwake). The node assets live in ScriptableObjects/SequenceRunner/.
00): Game Scope Dispose → Init group (Register Services → Instantiate Dependencies [LunaUIDemoFullDependencies.prefab, DontDestroyOnLoad] → Game Services [registers IInventoryItemDatabase + IEquipableFeatureRpgModule]) → Scene Loader Startup (loads scene index 1 with a deferred FadeWithInput transition).02, 03): Init group → Ensure Save Loaded → Game Scope Ensure → Boot Nav Graphs → Activate Game Object (SceneParent) → Reveal Loading Screen.01): Game Scope Dispose → Init group → Activate Game Object → Boot Nav Graphs → Reveal Loading Screen.Node roles: Ensure Save Loaded guards that a save session is loaded before the game scene reads it (see below); Game Scope Ensure / Dispose manage the pause scope; Boot Nav Graphs mounts the scene's NavHosts; Activate Game Object reveals the scene content held inactive during boot; Reveal Loading Screen waits for nav-ready then completes the deferred loading transition.
GameFull is the place to see the full save loop driven by Newtonsoft JSON.
GameFullSaveData (Scripts/SaveSystem/GameFullSaveData.cs) implements IGameSaveData, IData. Fields: Metadata ([JsonProperty(Order = -100)] so it serializes first for a fast metadata read), PlayerName, Wallet, Experience (ExperienceTracker), Inventory, NotificationHistory, and EquippedItems ([JsonProperty(NullValueHandling = NullValueHandling.Ignore)]). The default constructor seeds nothing — it also runs during deserialization, and pre-seeding the read-only Wallet/Experience collections would collide with the values the loader adds on top. Seeding lives in OnNewSaveCreated(): Wallet.Add("Gold", 500), Wallet.Add("Gem", 100), Experience.AddExp("Hero", 865), and 50 random items from IInventoryItemDatabase.GameFullSaveManager (GameSaveManager<GameFullSaveData, GameFullSaveMetadata>) writes one JSON file per slot under Application.persistentDataPath/saves_gamefull/, serializing via ServiceLocator.Get<SerializationManager>(). Autosaves raise GameSaveEvents.AutosaveStart / AutosaveComplete. GameFullSaveMetadata carries a Gold snapshot plus the inherited save date / version / autosave flag.EnsureSaveLoadedNodeSO (Scripts/SaveSystem/EnsureSaveLoadedNodeSO.cs) is the cold-start guard in the game-scene runner, built on the manager's session-save API: if a save was already chosen this play session (HasSessionSave — set by the menu's New Game / Continue / Load through SetSessionSave) it no-ops; otherwise it loads the newest save from disk (TryLoadNewestIntoSession), or seeds a fresh one (StartNewSession) — so you can press Play directly in a game scene without going through the menu. Note it deliberately does not test CurrentSave.Data != null: the save DataSO clones its default template every play session, so data presence can't distinguish "player picked a save" from "seeded template".ScriptableObjects/Serialization/): GameFull Service Registry Newtonsoft.asset registers the Newtonsoft IDataSerializer adapter and the SerializationManagerRegistrar, which stands up the SerializationManager and merges known types from the Luna default provider + GameFull Serialization Types SO.asset (which registers GameSaveMetadata, GameFullSaveMetadata, and GameFullSaveData for polymorphic deserialization).SceneLoadTransitionFadeWithInput / SceneLoadTransitionCircleWithInput (Scripts/UI/SceneTransition/) extend Luna's plain fade/circle transitions and override FadeOut() to raise SceneTransition.LoadingScreenContinueRequested instead of exiting immediately — a press-to-continue gate. This is a policy/mechanism split: the transition decides when to exit; the LoadingScreen component owns the prompt UI and the input wait, communicating only through static Actions.SceneLoadProgressBinder drives the loader's determinate RadialProgressBar from SceneLoader.OnLoadProgress (which self-animates via its TargetValue setter) and restores the indeterminate RadialLoading spinner between loads.LoadingGameFull.uxml provides the spinner, the #LoadingProgress bar, and the InputPromptContinue prompt that the press-to-continue path binds. See the loading screen page for the full sequencer-driven reveal flow.| Screen | Script | Notes |
|---|---|---|
| Main menu | MainMenuViewExample : MainMenuView<…> | Continue / New Game / Load / Credits / Settings / Quit; staggered entry animations; Discord + Wishlist buttons; Quit uses the awaitable PushAsync<int> choice popup. |
| Pause menu | PauseMenuViewExample : PauseMenuView | Continue / Inventory / Load / Settings / Main Menu / Quit; Return-to-menu confirms via the global confirm destination (PushAsync<int> + ChoicePopupArgs). |
| Inventory / equipment | EquipmentViewPagination : UIViewComponent | Paginated item grid + 6-slot equipment rack + consumable slot; drag-drop equip, live Attack/Health/Defense comparison, feature tooltips. Opened from the HUD or Pause menu. |
| Save / Load | GameSaveViewListExample : GameSaveViewList<…> | Custom two-part slot layout; save is disabled in the main menu (IsInGame() = not on scene 1); overwrite/delete confirm through the global confirm destination; Load sets the session save and transitions to the game scene. |
| Confirm / Choice | ConfirmPopupGameFull.prefab (ChoicePopupController) | One global confirm destination for the whole game — pushed with per-call ChoicePopupArgs via PushAsync<int>, index 0 = affirmative, resolved through LunaNavigation.Pop(int). See Confirmation Popup. |
| Notifications | NotificationView | Toasts from NotificationHistory (max 5), with a link to the full history modal. |
| Settings | SettingsGameFull.uxml (luna:TabView) | Four tabs (Game / Video / Audio / Controls) as separate prefabs in the global graph; input rebinding on Controls; a language dropdown on Game. |
GameFull/
├── README.md
├── CupkekGames.Luna.Demo.Game.Full.asmdef
├── 00_LunaUIDemoFullInitialization.unity ← boot scene (sample root, sorts first)
├── Scenes/
│ ├── 01_LunaUIDemoFullMainMenu.unity
│ ├── 02_LunaUIDemoFullBase.unity ← Area 1 (HUD + Save Summary)
│ └── 03_LunaUIDemoFullArea2.unity ← Area 2 (encounter demo)
├── Navigation/ ← the 4 nav graphs + their screen prefabs
│ ├── 0_GameFullNavGraphSO_Global/ ← SaveLoadGameFull, SettingsGameFull, ConfirmPopupGameFull, Tabs/
│ ├── 1_GameFullNavGraphSO_MainMenu/ ← MainMenu, Credits, MainMenuQuit
│ ├── 2_GameFullNavGraphSO_Global_InGame/ ← GameScope, PauseMenu, InventoryScreen
│ └── 3_GameFullNavGraphSO_GameScene/ ← GameShell (Area 1), Area2
├── Scripts/
│ ├── GamePrefabs.cs ← scene-lifecycle prefab spawner
│ ├── SaveSystem/ ← GameFullSaveData (+ SO + Metadata + Manager
│ │ + SerializationTypesSO) + EnsureSaveLoadedNodeSO
│ ├── UI/ ← GameHudView, ResourceChipBinder, SaveSummaryBinder,
│ │ │ GameScopeNodeSO, EncounterController,
│ │ │ MainMenuViewExample, PauseMenuViewExample, GameSaveViewListExample
│ │ ├── Inventory/ ← EquipmentViewPagination + LunaInventoryDragDefaults / TooltipHelpers
│ │ ├── Loot/ ← LootCardController + DropResultRouter
│ │ └── SceneTransition/ ← SceneLoadProgressBinder + fade/circle-with-input presets
│ └── UI.Generic/ ← nested asmdef; NotificationView + Pause/PauseEscapeTrigger
├── ScriptableObjects/
│ ├── Items/ ← item defs + database + providers + Droptable (loot), registered at boot
│ ├── UIAttractor/ ← LootRewardMotion (loot-burst motion preset)
│ ├── Resources/ ← Currencies (Gold, Gem) + Experiences (Hero curve)
│ ├── RPGStats/ ← equipment attribute data + provider (registered at boot)
│ ├── SequenceRunner/ ← boot chain + game-scene nodes (Ensure Save Loaded, Game Scope, Boot Nav Graphs, …)
│ ├── Serialization/ ← Newtonsoft service registry + registrar + types SO
│ └── Settings/ ← SettingsCurrent + SettingsDefault
├── Sprites/ ← Items/ + UI/ — sample-owned item/slot/chip icons (no cross-sample refs)
├── UXML/
│ ├── Screens/ ← MainGameFull, GameHud (+ .uss), PauseMenu, SettingsGameFull, Area2, LoadingGameFull,
│ │ InventoryGameFull (+ .uss), Credits, AutoSave, LoadingTransitionCircleHole
│ ├── Shared/ ← HudBar.uxml (the shared top bar)
│ └── Components/ ← ChoicePopup, InventoryItemSlot, Atoms/ (UIAttractor particles)
├── Audio/ ← demo audio
└── URP/ ← render-pipeline configsLocalizedString bindings) is left to you. See the UI Toolkit Localization guide. The JP SDF font and the .locale-ja styling hook already ship in the Essentials theme.Settings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction