Was wondering if there was a way to save game objects once being turned off such as gates or portals etc?
I tried adding a different json saveable entity to each npc that “moves” around by dialogue (by being turned off and reappearing in other locations as if saved etc) but when I am loading the original NPC appears.
I have seen this solution however its got the old saving system was wondering if there was something that could be done to convert this to the JsonSaveableEntity.
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
namespace GameDevTV.Saving
{
[ExecuteAlways]
public class ManagedSaveableEntity : MonoBehaviour
{
[SerializeField] string uniqueIdentifier = "";
// CACHED STATE
static Dictionary<string, ManagedSaveableEntity> globalLookup = new Dictionary<string, ManagedSaveableEntity>();
public string GetUniqueIdentifier()
{
return uniqueIdentifier;
}
public JToken CaptureAsJtoken()
{
JObject state = new JObject();
IDictionary<string, JToken> stateDict = state;
foreach (IJsonSaveable jsonSaveable in GetComponents<IJsonSaveable>())
{
JToken token = jsonSaveable.CaptureAsJToken();
string component = jsonSaveable.GetType().ToString();
Debug.Log($"{name} Capture {component} = {token.ToString()}");
stateDict[jsonSaveable.GetType().ToString()] = token;
}
return state;
}
public void RestoreFromJToken(JToken s)
{
JObject state = s.ToObject<JObject>();
IDictionary<string, JToken> stateDict = state;
foreach (IJsonSaveable jsonSaveable in GetComponents<IJsonSaveable>())
{
string component = jsonSaveable.GetType().ToString();
if (stateDict.ContainsKey(component))
{
Debug.Log($"{name} Restore {component} =>{stateDict[component].ToString()}");
jsonSaveable.RestoreFromJToken(stateDict[component]);
}
}
}
#if UNITY_EDITOR
private void Update()
{
if (Application.IsPlaying(gameObject)) return;
if (string.IsNullOrEmpty(gameObject.scene.path)) return;
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty property = serializedObject.FindProperty("uniqueIdentifier");
if (string.IsNullOrEmpty(property.stringValue) || !IsUnique(property.stringValue))
{
property.stringValue = System.Guid.NewGuid().ToString();
serializedObject.ApplyModifiedProperties();
}
globalLookup[property.stringValue] = this;
}
#endif
private bool IsUnique(string candidate)
{
if (!globalLookup.ContainsKey(candidate)) return true;
if (globalLookup[candidate] == this) return true;
if (globalLookup[candidate] == null)
{
globalLookup.Remove(candidate);
return true;
}
if (globalLookup[candidate].GetUniqueIdentifier() != candidate)
{
globalLookup.Remove(candidate);
return true;
}
return false;
}
}
}``` I duplicated the JasonSaveableEntity, created a new script and named it like this.
Than created a new script and called it SaveableEntityManager
```using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json.Linq;
using GameDevTV.Saving;
public class SaveableEntityManager : MonoBehaviour
{
private Dictionary<ManagedSaveableEntity, bool> manageableEntities = new Dictionary<ManagedSaveableEntity, bool>();
private bool stateWasRestored = false;
private void Awake()
{
for (int i = 0; i < transform.childCount; i++)
{
if (transform.GetChild(i).TryGetComponent(out ManagedSaveableEntity entity))
{
manageableEntities[entity] = entity.gameObject.activeSelf;
entity.gameObject.SetActive(true); //Set active to ensure Awake() runs!
}
}
}
void OnEnable()
{
if (stateWasRestored) return;
foreach (KeyValuePair<ManagedSaveableEntity, bool> pair in manageableEntities)
{
pair.Key.gameObject.SetActive(pair.Value);
}
stateWasRestored = true;
}
[System.Serializable]
public struct ManagedEntity
{
public object state;
public bool isActive;
}
public JToken CaptureAsJtoken()
{
JObject state = new JObject();
IDictionary<string, JToken> stateDict = state;
foreach (KeyValuePair<ManagedSaveableEntity, bool> pair in manageableEntities)
{
JToken token = pair.Key.CaptureAsJtoken();
string component = pair.Key.GetType().ToString();
Debug.Log($"{pair.Key.name} Capture {component} = {token.ToString()}");
stateDict[component] = token;
}
return state;
}
public void RestoreFromJToken(JToken s)
{
JObject state = s.ToObject<JObject>();
IDictionary<string, JToken> stateDict = state;
foreach (KeyValuePair<ManagedSaveableEntity, bool> pair in manageableEntities)
{
string component = pair.Key.GetType().ToString();
if (stateDict.ContainsKey(component))
{
Debug.Log($"{pair.Key.name} Restore {component} =>{stateDict[component].ToString()}");
pair.Key.RestoreFromJToken(stateDict[component]);
}
}
}
}
Added it to the characters and objects that I may want to store there state. So to explain what I am trying to do here is create an object as part of the character like a exclamation mark to show the players that this NPC has got a quest for them. When I complete the dialogue I am turning this off. All this is ok. When I quick save, and quick reload the exclamation mark is still fine dissapeared. But when I click save and exit. Than return by pressing continue to reload the last save thats when everything sort of resets. Problem is I sometimes want to get a door open if an enemy was killed. If this enemy is killed but the door returns to being closed thats a problem. It kind of hinders the gameplay a little. I dont know if I am explaining it very well. Also same issue with NPCs sometimes I will have a dialogue which moves npcs to safe location. But continuing the game resets it to the original location of the NPC.
Sorry for my bad English.Any help would be appreciated.
I was able to edit the JsonSaveableEntity and this seems to work for my needs.
public JToken CaptureAsJtoken()
{
JObject state = new JObject();
// Store position and rotation
state["position"] = new JArray(transform.position.x, transform.position.y, transform.position.z);
state["rotation"] = new JArray(transform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
// Store active state
state["isActive"] = gameObject.activeSelf;
Debug.Log($"Capturing gate active state: {gameObject.activeSelf}");
// Capture child's active state
Transform childTransform = transform.Find("Quest Marker"); // Replace "ChildName" with the name of your child object
if (childTransform != null)
{
state["childActive"] = childTransform.gameObject.activeSelf;
Debug.Log($"Capturing child active state: {childTransform.gameObject.activeSelf}");
}
IDictionary<string, JToken> stateDict = state;
foreach (IJsonSaveable jsonSaveable in GetComponents<IJsonSaveable>())
{
JToken token = jsonSaveable.CaptureAsJToken();
string component = jsonSaveable.GetType().ToString();
Debug.Log($"{name} Capture {component} = {token.ToString()}");
stateDict[jsonSaveable.GetType().ToString()] = token;
}
return state;
}
public void RestoreFromJToken(JToken s)
{
JObject state = s.ToObject<JObject>();
if (state["isActive"] != null)
{
bool isActive = state["isActive"].Value<bool>();
gameObject.SetActive(isActive);
Debug.Log($"Restoring gate active state: {isActive}");
}
// Restore position and rotation
if (state["position"] != null && state["rotation"] != null)
{
transform.position = new Vector3(state["position"][0].Value<float>(), state["position"][1].Value<float>(), state["position"][2].Value<float>());
transform.eulerAngles = new Vector3(state["rotation"][0].Value<float>(), state["rotation"][1].Value<float>(), state["rotation"][2].Value<float>());
}
// Restore active state
if (state["isActive"] != null)
{
gameObject.SetActive(state["isActive"].Value<bool>());
}
// Restore child's active state
if (state["childActive"] != null)
{
Transform childTransform = transform.Find("Quest Marker"); // Replace "ChildName" with the name of your child object
if (childTransform != null)
{
childTransform.gameObject.SetActive(state["childActive"].Value<bool>());
Debug.Log($"Restoring child active state: {state["childActive"].Value<bool>()}");
}
}
IDictionary<string, JToken> stateDict = state;
foreach (IJsonSaveable jsonSaveable in GetComponents<IJsonSaveable>())
{
string component = jsonSaveable.GetType().ToString();
if (stateDict.ContainsKey(component))
{
Debug.Log($"{name} Restore {component} =>{stateDict[component].ToString()}");
jsonSaveable.RestoreFromJToken(stateDict[component]);
}
}
}
I think I found my own solution sorry for the many replies. It seems to work no errors.