using System; using System.Collections.Generic; using System.IO; using UnityEngine; using BlackRoad.Worldbuilder.Building; namespace BlackRoad.Worldbuilder.Core { /// /// Saves and loads all Buildable objects in the scene /// to a JSON file in Application.persistentDataPath. /// F5 = save, F9 = load (default slot "slot1"). /// public class WorldSerializer : MonoBehaviour { [Header("Registry")] [SerializeField] private BuildableRegistry registry; [Header("Slot")] [SerializeField] private string slotName = "slot1"; [Header("Keys")] [SerializeField] private KeyCode saveKey = KeyCode.F5; [SerializeField] private KeyCode loadKey = KeyCode.F9; [Header("Options")] [SerializeField] private bool autoCreateFolder = true; [SerializeField] private string folderName = "WorldSaves"; [Serializable] private class PlacedObjectData { public string id; public float px, py, pz; public float rx, ry, rz; public float sx, sy, sz; } [Serializable] private class WorldSave { public PlacedObjectData[] objects; } private string SaveDirectory => autoCreateFolder ? Path.Combine(Application.persistentDataPath, folderName) : Application.persistentDataPath; private string SavePath => Path.Combine(SaveDirectory, $"{slotName}.json"); private void Update() { if (Input.GetKeyDown(saveKey)) { Save(); } if (Input.GetKeyDown(loadKey)) { Load(); } } [ContextMenu("Save Now")] public void Save() { if (registry == null) { Debug.LogError("[WorldSerializer] No BuildableRegistry assigned."); return; } var buildables = FindObjectsOfType(); var list = new List(); foreach (var b in buildables) { if (b == null) continue; var t = b.transform; var id = b.Id; if (string.IsNullOrWhiteSpace(id)) continue; var data = new PlacedObjectData { id = id, px = t.position.x, py = t.position.y, pz = t.position.z, rx = t.rotation.eulerAngles.x, ry = t.rotation.eulerAngles.y, rz = t.rotation.eulerAngles.z, sx = t.localScale.x, sy = t.localScale.y, sz = t.localScale.z }; list.Add(data); } var save = new WorldSave { objects = list.ToArray() }; string json = JsonUtility.ToJson(save, true); if (!Directory.Exists(SaveDirectory)) { Directory.CreateDirectory(SaveDirectory); } File.WriteAllText(SavePath, json); Debug.Log($"[WorldSerializer] Saved {list.Count} objects to {SavePath}"); } [ContextMenu("Load Now")] public void Load() { if (registry == null) { Debug.LogError("[WorldSerializer] No BuildableRegistry assigned."); return; } if (!File.Exists(SavePath)) { Debug.LogWarning($"[WorldSerializer] No save file at {SavePath}"); return; } string json = File.ReadAllText(SavePath); var save = JsonUtility.FromJson(json); if (save == null || save.objects == null) { Debug.LogError("[WorldSerializer] Failed to parse save file."); return; } // Remove existing buildables first var existing = FindObjectsOfType(); foreach (var b in existing) { if (b != null) { Destroy(b.gameObject); } } int spawned = 0; foreach (var obj in save.objects) { var prefab = registry.GetPrefab(obj.id); if (prefab == null) { Debug.LogWarning($"[WorldSerializer] No prefab for id '{obj.id}'"); continue; } Vector3 pos = new Vector3(obj.px, obj.py, obj.pz); Quaternion rot = Quaternion.Euler(obj.rx, obj.ry, obj.rz); Vector3 scale = new Vector3(obj.sx, obj.sy, obj.sz); var instance = Instantiate(prefab, pos, rot); instance.transform.localScale = scale; spawned++; } Debug.Log($"[WorldSerializer] Loaded {spawned} objects from {SavePath}"); } } }