5.3 Save/Load arhitektūra
Stundas uzdevums: Izveidot pilnvērtīgu saglabāšanas sistēmu ar vairākiem slotiem un autosave.
80 min plāns: Teorija un paraugs (~10 min) · 1. uzdevums (~20 min) - izveido SaveManager ar slotiem · 2. uzdevums (~25 min) - liec objektiem saglabāt sevi pašiem · 3. uzdevums (~20 min) - pievieno autosave un versiju · Iesniegšana (~5 min).
Pirms sāc: atver savu projektu ar JSON saglabāšanu no 5.2 stundas. Šodien no vienas funkcijas taisīsi sistēmu, kas der visai spēlei.
Teorija: Save sistēmas arhitektūra
Profesionāla save sistēma sastāv no vairākiem komponentiem:
- SaveManager - galvenais koordinators.
- Saveable interfeiss - visi saglabājami objekti to implementē.
- Save slots - vairāki saglabāšanas faili (slot1.json, slot2.json, ...).
- Autosave - automātiska saglabāšana ik pa N minūtēm vai pie checkpoint.
using Godot;
using Godot.Collections;
using System.Collections.Generic;
public interface ISaveable
{
Dictionary SaveState();
void LoadState(Dictionary state);
}
public partial class SaveManager : Node
{
private readonly List<ISaveable> saveables = new();
public void RegisterSaveable(ISaveable saveable)
{
saveables.Add(saveable);
}
public void SaveToSlot(int slot) { }
public void LoadFromSlot(int slot) { }
}
Kā šo izmantot projektā
Atceries: ar redzamu efektu editorā nepietiek. Paskaidro, kura C# klase glabā stāvokli, kura metode to maina un kā Godot node struktūra izmanto šo kodu.
Pārbaudi: C# kods pārbauda failu kļūdas, validē datus, izmanto versijas lauku un pamato algoritmu sarežģītību.
public class PersistenceCheckpoint
{
public string Lesson = "5.3 Save/Load arhitektūra";
public bool UsesCSharp = true;
public bool HandlesMissingFile = true;
public bool ValidatesData = true;
public bool DocumentsComplexity = true;
}
Praktiskie uzdevumi
1. uzdevums -
Izveido SaveManager ar slotiem
Beigās spēlētājs varēs izvēlēties, kurā vietā saglabāt.
- Izveido klasi
SaveManagerar metodiprivate string SlotPath(int slot). - Atgriez tajā ceļu
user://slot1.jsonatbilstoši numuram. - Uzraksti metodes
public void Save(int slot)unpublic bool Load(int slot). - Saglabā dažādus datus slotos 1 un 2.
- Ielādē slotu 1 un pārbaudi, ka dati ir pareizie.
- Ielādē slotu 2 un salīdzini.
- Uzraksti metodi, kas pasaka, kuri sloti vispār eksistē.
Gatavs, kad: abi sloti glabā atšķirīgus datus, un metode pareizi pasaka, kuri sloti ir aizpildīti.
2. uzdevums -
Liec objektiem saglabāt sevi pašiem
Beigās katrs objekts pats zinās, ko par sevi saglabāt.
- Izveido interfeisu
public interface ISaveablear divām metodēm. - Pieteic tajā
Dictionary SaveState();. - Pieteic arī
void LoadState(Dictionary state);. - Liec spēlētāja klasei implementēt
ISaveableun abas metodes. - Liec arī inventāra klasei to pašu.
- Savāc
SaveManagervisusISaveableobjektus vienā sarakstā. - Izsauc ciklā katram
SaveState()un saliec rezultātus vienāDictionary.
Gatavs, kad: viens Save() izsaukums saglabā gan spēlētāju, gan inventāru, un SaveManager nezina to iekšējo uzbūvi.
3. uzdevums -
Pievieno autosave un versiju
Beigās spēle saglabāsies pati, un vecie faili neradīs kļūdas.
- Pievieno saglabātajam
Dictionarylaukuversionar vērtību 1. - Pārbaudi ielādējot, vai versija sakrīt.
- Izdrukā brīdinājumu un lieto noklusējumus, ja versija ir citāda.
- Pievieno
Timermezglu, kas ik pēc 60 sekundēm izsauc autosave. - Saglabā autosave atsevišķā slotā, lai tas nepārrakstītu spēlētāja slotu.
- Palaid spēli un pagaidi vienu autosave ciklu.
- Nomaini failā versiju uz 2 un pārbaudi brīdinājumu.
Gatavs, kad: autosave fails parādās pats, un ar nepareizu versiju spēle brīdina, nevis avarē.
Papildu uzdevums - Parādi slotu izvēlni
Ja pamatdarbs ir gatavs, ļauj slotus izvēlēties uz ekrāna.
- Izveido UI ar trim pogām - pa vienai katram slotam.
- Parādi pie katras, vai slots ir tukšs vai aizpildīts.
- Ielādē attiecīgo slotu, nospiežot pogu.
- Pievieno pogu slota dzēšanai.
- Pārbaudi, ka pēc dzēšanas slots rāda 'tukšs'.
Gatavs, kad: izvēlnē redzams katra slota statuss, un slotus var ielādēt un dzēst ar pogām.
Ko sagatavo
Stundas mērķis: Tavā spēlē ir SaveManager ar vairākiem slotiem, objekti paši saglabā savus datus caur ISaveable interfeisu, darbojas autosave, un saglabātajam failam ir versijas pārbaude.
GitHub krātuvē jābūt:
- Faili
SaveManager.csunISaveable.cs. - Divi paraugi saglabāto JSON failu no dažādiem slotiem.
- Ekrānuzņēmums ar brīdinājumu par nepareizu versiju.
- README.md: kāpēc
SaveManagernav jāzina, kas tieši ir spēlētāja iekšienē.
Kā iesniedz: Veic commit un push. Skolotāja norādītajā vietā ievieto tikai GitHub krātuves saiti; visiem prasītajiem failiem, README.md pierakstiem un pierādījumiem jābūt pašā krātuvē.
Biežākās kļūdas
- Save fails fragmented: Save daļas atsevišķos failos vs viens - izvēlies vienu pieeju.
- Race condition: Save un load notiek vienlaikus - lieto mutex vai sekvenciāli.
- Slot UI nesinhronizējas: Pārbaudi metadata refresh pēc save.
Godot ekrānuzņēmumi
Koda piemērs (paplašināts)
using Godot;
using Godot.Collections;
using System.Collections.Generic;
public partial class SaveManager : Node
{
private readonly List<Node> saveables = new();
private float autosaveTimer = 0.0f;
public override void _Process(double delta)
{
autosaveTimer += (float)delta;
if (autosaveTimer >= 300.0f) // 5 min
{
SaveToSlot(0); // 0 = autosave
autosaveTimer = 0.0f;
}
}
public void SaveToSlot(int slot)
{
Dictionary allData = new();
foreach (Node node in saveables)
{
if (node.HasMethod("SaveState"))
{
allData[node.GetPath().ToString()] = node.Call("SaveState");
}
}
allData["timestamp"] = Time.GetUnixTimeFromSystem();
allData["version"] = 1;
string path = $"user://save_{slot}.json";
using FileAccess file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
file?.StoreString(Json.Stringify(allData, " "));
}
}