Save & Load

GameSaveViewList<TSaveData, TSaveMetadata> renders the slot list for the GameSave system — load / overwrite / delete buttons, autosave/manual filter toggles, gamepad-friendly InputPrompts, and overwrite/delete confirmations through a global Confirmation Popup navigation destination. Subclass it for your save types and provide two slot-content slots (SlotOne, SlotTwo) per row.

Save & Load

Setup

The component requires both a PanelRenderer and a GameSaveView on the same GameObject ([RequireComponent] enforces this). The PanelRenderer delivers its tree asynchronously, so all element lookups run at the UILoaded milestone (via GameSaveView.WhenUILoaded) — no UIDocument is involved. The UXML must contain the named elements the abstract class queries:

ElementNamePurpose
ButtonNewSaveCreate a new save slot.
ToggleShowAutoFilter: include autosaves.
ToggleShowManualFilter: include manual saves.
InputPromptLoadLoad selected slot.
InputPromptOverwriteOverwrite selected slot.
InputPromptDeleteDelete selected slot.
ListViewLoadListSlot list. Items rendered from _listEntryTemplate.

Also serialize a VisualTreeAsset _listEntryTemplate — Luna instantiates it per row and your SlotOne/SlotTwo overrides populate it.

Confirmations

Overwrite and Delete are destructive, so both go through a confirm popup. The inspector field _confirmDest (a CatalogKey into the nav-destination catalog) is required: the view pushes that destination with per-call ChoicePopupArgs ("Overwrite Save 3" / "Delete Save 3") via LunaNavigation.PushAsync<int> and acts only when the affirmative choice (index 0) comes back; Escape or a backdrop dismiss is a no-op. The acted-on slot is captured before the push, so changing the list selection while the popup is open can't retarget the action. Point it at one global ChoicePopupController destination — see Confirmation Popup.

Leaving _confirmDest empty is a configuration error: the view logs an error (at Awake and again on the button press) and refuses the overwrite/delete instead of acting unconfirmed. The editor also warns via OnValidate while the field is unset.

API

csharp
namespace CupkekGames.GameSave.Luna; [RequireComponent(typeof(PanelRenderer))] [RequireComponent(typeof(GameSaveView))] public abstract class GameSaveViewList<TSaveData, TSaveMetadata> : MonoBehaviour where TSaveData : IGameSaveData, IData, new() where TSaveMetadata : GameSaveMetadata

Methods (abstract — you implement)

MethodDescription
GetSaveManager()Return your GameSaveManager<TSaveData, TSaveMetadata> instance.
IsInGame()Return true if a save can be written from here. Disables NewSave/Overwrite when false.
SlotOne(int index, GameSaveMetadataWithSlot<TSaveMetadata> metadata)Build the left-hand row content.
SlotTwo(int index, GameSaveMetadataWithSlot<TSaveMetadata> metadata)Build the right-hand row content.
OnLoadButtonClicked()Load the selected slot. Use GetSelectedMetadata() to read the row.
GetTooltipController()Return the scene's TooltipController (passed to each slot row for its tooltips).

Properties

PropertyTypeDescription
GameSaveManagerGameSaveManager<TSaveData, TSaveMetadata>Resolved during Awake().
GameSaveViewGameSaveViewThe sibling view component on the same GameObject.

Example

csharp
using UnityEngine.UIElements; using UnityEngine; using CupkekGames.GameSave; using CupkekGames.GameSave.Luna; using CupkekGames.Luna; using CupkekGames.SceneManagement; using CupkekGames.Services; using System; namespace CupkekGames.Luna.Demo.Game.Full { public class GameSaveViewListExample : GameSaveViewList<GameFullSaveData, GameFullSaveMetadata> { protected override GameSaveManager<GameFullSaveData, GameFullSaveMetadata> GetSaveManager() => ServiceLocator.Get<GameFullSaveManager>(); protected override bool IsInGame() => !SceneLoader.Instance.IsLoaded(1); // MainMenu scene index protected override VisualElement SlotOne(int index, GameSaveMetadataWithSlot<GameFullSaveMetadata> meta) { VisualElement container = new(); container.AddToClassList("flex-row"); VisualElement left = new(); VisualElement right = new(); container.Add(left); container.Add(right); left.Add(new Label("Index: " + index)); left.Add(new Label("File: " + meta.SaveSlot)); right.Add(new Label(meta.Metadata.SaveDate.ToString())); right.Add(new Label(meta.Metadata.IsAutosave ? "Autosave" : "Manual Save")); container.Add(new Label("Save Version: " + meta.Metadata.SaveVersion)); return container; } protected override VisualElement SlotTwo(int index, GameSaveMetadataWithSlot<GameFullSaveMetadata> meta) => new Label("Gold: " + meta.Metadata.Gold); protected override void OnLoadButtonClicked() { var meta = GetSelectedMetadata(); if (!meta.HasValue) return; try { // SetSessionSave (not a bare CurrentSave.Data assignment) also marks // the save as chosen for this play session — see GameSave docs. GameSaveManager.SetSessionSave(GameSaveManager.GetSave(meta.Value.SaveSlot)); } catch (Exception e) { Debug.LogException(e, this); return; } SceneLoader.Instance.LoadScene(2, SceneTransitionDatabase.Instance.Transitions.GetValue("Fade"), true); GameSaveView.ReturnClicked(); // pops the save/load view off the nav back stack } protected override TooltipController GetTooltipController() => Luna.Sample.Essentials.TooltipDatabaseExample.Instance.TooltipController; } }

See also

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL