An open API service indexing awesome lists of open source software.

https://github.com/pietras333/save-load-system

A comprehensive, modular save/load system for Unity games with support for multiple save slots, automatic screenshots, and flexible data management - similar to Minecraft's world system.
https://github.com/pietras333/save-load-system

game-development json save-load unity3d

Last synced: 11 months ago
JSON representation

A comprehensive, modular save/load system for Unity games with support for multiple save slots, automatic screenshots, and flexible data management - similar to Minecraft's world system.

Awesome Lists containing this project

README

          

# Unity Multi-Save System

A comprehensive, modular save/load system for Unity games with support for multiple save slots, automatic screenshots, and flexible data management - similar to Minecraft's world system.

## Features

### Core Functionality
- **Multiple Save Slots**: Create, load, delete, and duplicate game saves
- **Automatic Screenshots**: Capture and display save thumbnails
- **Flexible Data Structure**: Modular data packs for different game systems
- **Async Operations**: Non-blocking save/load operations
- **Metadata Management**: Rich save information with play time, timestamps, and custom data
- **Error Handling**: Robust error handling with fallback to defaults

### Advanced Features
- **Priority-Based Loading**: Load data packs in specific order
- **Selective Saving**: Save individual data packs or all at once
- **Data Validation**: Version compatibility checking
- **Auto-Save**: Automatic saving on application pause/focus loss
- **Memory Management**: Efficient resource cleanup

## Quick Start

### 1. Installation

Copy all the provided scripts into your Unity project:
- `MultiSaveManager.cs`
- `SaveLoadUI.cs`
- `EnhancedSaveManager.cs`
- Core interfaces and classes

### 2. Basic Setup

```csharp
// Create your data structures
[Serializable]
public class PlayerData
{
public int Level = 1;
public int Experience = 0;
public int Gold = 100;
public Vector3 Position = Vector3.zero;
}

// Create data packs
public class PlayerDataPack : DataPack
{
public override string FileName => "player.json";
public override int Priority => 1; // Load first
}
```

### 3. Scene Setup

1. Create an empty GameObject and attach `MultiSaveManager` script
2. Set up your UI with the provided `SaveLoadUI` script
3. Configure screenshot settings if using `EnhancedSaveManager`

### 4. Basic Usage

```csharp
public class GameManager : MonoBehaviour
{
private MultiSaveManager saveManager;

void Start()
{
saveManager = FindObjectOfType();

// Create a new save
await saveManager.CreateNewSave("My Adventure", "Starting my journey!");

// Load an existing save
await saveManager.LoadGameById("20240712_143022_1234");

// Access data
var playerData = saveManager.PlayerData;
playerData.Level = 5;

// Save changes
await saveManager.SaveCurrentGame();
}
}
```

## Architecture Overview

### Core Components

```
MultiSaveManager
├── IDataOperationService (DataOperationService)
├── IDataService (FileDataService)
├── IDataPack (PlayerDataPack, WorldDataPack, etc.)
└── GameSaveInfo (Metadata management)
```

### Data Flow

1. **Save Creation**: Generate unique ID → Create directory → Initialize data packs
2. **Data Loading**: Load metadata → Initialize data service → Load data packs by priority
3. **Data Saving**: Update metadata → Save data packs → Capture screenshot
4. **Save Management**: List saves → Handle duplicates → Delete saves

## Data Structure

### Save Directory Structure
```
MyGameSaves/
├── 20240712_143022_1234/
│ ├── metadata.json
│ ├── screenshot.jpg
│ ├── player.json
│ ├── world.json
│ └── settings.json
├── 20240712_150045_5678/
│ ├── metadata.json
│ ├── screenshot.jpg
│ └── ...
```

### Metadata Format
```json
{
"SaveId": "20240712_143022_1234",
"SaveName": "My Adventure",
"Description": "Starting my journey!",
"CreatedDate": "2024-07-12T14:30:22.123Z",
"LastPlayedDate": "2024-07-12T15:45:30.456Z",
"PlayTime": "01:15:08",
"Version": "1.0.0",
"CustomMetadata": {
"PlayerLevel": 5,
"QuestsCompleted": 3,
"CurrentScene": "Forest_Level_1"
}
}
```

## Creating Custom Data Packs

### 1. Define Your Data Structure

```csharp
[Serializable]
public class InventoryData
{
public List Items = new List();
public int MaxSlots = 20;
public Dictionary ItemCounts = new Dictionary();
}
```

### 2. Create the Data Pack

```csharp
public class InventoryDataPack : DataPack
{
public override string FileName => "inventory.json";
public override int Priority => 2; // Load after player data

protected override void OnDataLoaded()
{
// Custom logic after loading
Debug.Log($"Loaded {Data.Items.Count} items");
}

protected override void OnDataReset()
{
// Custom logic when resetting to defaults
Data.Items.Clear();
Data.ItemCounts.Clear();
}
}
```

### 3. Register in DataOperationService

```csharp
public class DataOperationService : IDataOperationService
{
public DataOperationService(IDataService dataService)
{
// ... existing code ...

// Register your new data pack
RegisterDataPack(new InventoryDataPack());
}
}
```

## UI Integration

### Save/Load UI Setup

1. Create a Canvas with the following structure:
```
Canvas
├── SaveLoadPanel
│ ├── SaveSlotContainer (Vertical Layout Group)
│ ├── CreateNewSaveButton
│ └── ClosePanelButton
├── NewSaveDialog
│ ├── SaveNameInput
│ ├── SaveDescriptionInput
│ ├── ConfirmCreateButton
│ └── CancelCreateButton
└── DeleteConfirmDialog
├── ConfirmDeleteButton
└── CancelDeleteButton
```

2. Create a SaveSlot prefab with:
```
SaveSlotPrefab
├── SaveNameText
├── SaveDescriptionText
├── LastPlayedText
├── PlayTimeText
├── ScreenshotImage
├── LoadButton
├── DeleteButton
├── DuplicateButton
└── CurrentSaveIndicator
```

### Accessing Save Data in UI

```csharp
public class PlayerUI : MonoBehaviour
{
private MultiSaveManager saveManager;

void Start()
{
saveManager = FindObjectOfType();

// Subscribe to save events
saveManager.OnSaveLoaded += OnSaveLoaded;
UpdatePlayerInfo();
}

private void OnSaveLoaded(GameSaveInfo saveInfo)
{
UpdatePlayerInfo();
}

private void UpdatePlayerInfo()
{
var playerData = saveManager.PlayerData;
if (playerData != null)
{
// Update UI elements
levelText.text = $"Level: {playerData.Level}";
goldText.text = $"Gold: {playerData.Gold}";
}
}
}
```

## Advanced Features

### Screenshot System

```csharp
public class ScreenshotManager : MonoBehaviour
{
[SerializeField] private Camera screenshotCamera;
[SerializeField] private Image saveSlotImage;

public void DisplaySaveScreenshot(string saveId)
{
var saveManager = FindObjectOfType();
Texture2D screenshot = saveManager.GetSaveScreenshot(saveId);

if (screenshot != null)
{
Sprite sprite = Sprite.Create(screenshot,
new Rect(0, 0, screenshot.width, screenshot.height),
Vector2.one * 0.5f);
saveSlotImage.sprite = sprite;
}
}
}
```

### Custom Metadata

```csharp
public class GameProgressTracker : MonoBehaviour
{
private MultiSaveManager saveManager;

void Start()
{
saveManager = FindObjectOfType();
}

public void TrackBossDefeated(string bossName)
{
// Add custom metadata
saveManager.AddCustomMetadata($"Boss_{bossName}_Defeated", true);
saveManager.AddCustomMetadata($"Boss_{bossName}_DefeatTime", DateTime.Now);

// Save immediately
saveManager.SaveCurrentGame();
}

public bool IsBossDefeated(string bossName)
{
return saveManager.GetCustomMetadata($"Boss_{bossName}_Defeated");
}
}
```

### Save Validation

```csharp
public class SaveValidator : MonoBehaviour
{
public bool ValidateSave(GameSaveInfo saveInfo)
{
// Check version compatibility
if (saveInfo.CustomMetadata.ContainsKey("GameVersion"))
{
string saveVersion = saveInfo.CustomMetadata["GameVersion"].ToString();
if (!IsVersionCompatible(saveVersion))
{
Debug.LogWarning($"Save version {saveVersion} may not be compatible");
return false;
}
}

// Check required data
if (!saveInfo.CustomMetadata.ContainsKey("PlayerLevel"))
{
Debug.LogError("Save missing required player data");
return false;
}

return true;
}

private bool IsVersionCompatible(string saveVersion)
{
// Implement your version compatibility logic
Version save = new Version(saveVersion);
Version current = new Version(Application.version);

return save.Major == current.Major;
}
}
```

## Best Practices

### Performance Optimization

1. **Use Async Operations**: Always prefer async save/load operations
2. **Batch Operations**: Group multiple save operations together
3. **Lazy Loading**: Only load data when needed
4. **Memory Management**: Properly dispose of resources

```csharp
// Good - Async operation
await saveManager.SaveGameAsync();

// Good - Batch save specific packs
saveManager.Save();
saveManager.Save();

// Avoid - Synchronous operations in main thread
saveManager.SaveCurrentGameSync(); // Only use in OnApplicationPause
```

### Error Handling

```csharp
public async Task SafeLoadSave(string saveId)
{
try
{
return await saveManager.LoadGameById(saveId);
}
catch (Exception ex)
{
Debug.LogError($"Failed to load save {saveId}: {ex.Message}");

// Fallback to default data
saveManager.Reset();
return false;
}
}
```

### Data Migration

```csharp
public class SaveMigrator : MonoBehaviour
{
public void MigrateSave(GameSaveInfo saveInfo)
{
if (saveInfo.CustomMetadata.ContainsKey("DataVersion"))
{
int version = (int)saveInfo.CustomMetadata["DataVersion"];

if (version < 2)
{
// Migrate from version 1 to 2
MigrateFromV1ToV2(saveInfo);
}
}
}

private void MigrateFromV1ToV2(GameSaveInfo saveInfo)
{
// Implement migration logic
saveInfo.CustomMetadata["DataVersion"] = 2;
}
}
```

## Requirements

- Unity 2020.3 or later
- Newtonsoft.Json (com.unity.nuget.newtonsoft-json)
- TextMeshPro (com.unity.textmeshpro)

## Dependencies

```json
{
"dependencies": {
"com.unity.nuget.newtonsoft-json": "3.0.2",
"com.unity.textmeshpro": "3.0.6"
}
}
```

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Changelog

### v1.0.0
- Initial release
- Multi-save system with UI
- Screenshot support
- Metadata management
- Async operations
- Error handling and validation

---

**Made with ❤️ for Unity developers**