218 lines
7.7 KiB
C#
218 lines
7.7 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
using ES3Internal;
|
|||
|
#if UNITY_EDITOR
|
|||
|
using UnityEditor;
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
namespace ES3Internal
|
|||
|
{
|
|||
|
public class ES3Prefab : MonoBehaviour
|
|||
|
{
|
|||
|
public long prefabId = GetNewRefID();
|
|||
|
/*
|
|||
|
* We need to store references to all dependencies of the prefab because only supported scripts will be serialised.
|
|||
|
* This means that although supported scripts have their dependencies added to the reference manager when we load the prefab,
|
|||
|
* there will not be any dependencies in the reference manager for scripts which are not supported. So it will not be possible to save any reference to these.
|
|||
|
*/
|
|||
|
public ES3RefIdDictionary localRefs = new ES3RefIdDictionary();
|
|||
|
|
|||
|
public void Awake()
|
|||
|
{
|
|||
|
// Add the references to the reference list when this prefab is instantiated.
|
|||
|
var mgr = ES3ReferenceMgrBase.Current;
|
|||
|
|
|||
|
if (mgr == null)
|
|||
|
return;
|
|||
|
|
|||
|
foreach (var kvp in localRefs)
|
|||
|
if (kvp.Key != null)
|
|||
|
mgr.Add(kvp.Key);
|
|||
|
}
|
|||
|
|
|||
|
public long Get(UnityEngine.Object obj)
|
|||
|
{
|
|||
|
long id;
|
|||
|
if (localRefs.TryGetValue(obj, out id))
|
|||
|
return id;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
public long Add(UnityEngine.Object obj)
|
|||
|
{
|
|||
|
long id;
|
|||
|
if (localRefs.TryGetValue(obj, out id))
|
|||
|
return id;
|
|||
|
|
|||
|
if (!ES3ReferenceMgr.CanBeSaved(obj))
|
|||
|
return -1;
|
|||
|
id = GetNewRefID();
|
|||
|
localRefs.Add(obj, id);
|
|||
|
return id;
|
|||
|
}
|
|||
|
|
|||
|
public Dictionary<string, string> GetReferences()
|
|||
|
{
|
|||
|
var localToGlobal = new Dictionary<string, string>();
|
|||
|
|
|||
|
var refMgr = ES3ReferenceMgr.Current;
|
|||
|
|
|||
|
if (refMgr == null)
|
|||
|
return localToGlobal;
|
|||
|
|
|||
|
foreach (var kvp in localRefs)
|
|||
|
{
|
|||
|
long id = refMgr.Add(kvp.Key);
|
|||
|
localToGlobal[kvp.Value.ToString()] = id.ToString();
|
|||
|
}
|
|||
|
return localToGlobal;
|
|||
|
}
|
|||
|
|
|||
|
public void ApplyReferences(Dictionary<long, long> localToGlobal)
|
|||
|
{
|
|||
|
if (ES3ReferenceMgrBase.Current == null)
|
|||
|
return;
|
|||
|
|
|||
|
foreach (var localRef in localRefs)
|
|||
|
{
|
|||
|
long globalId;
|
|||
|
if (localToGlobal.TryGetValue(localRef.Value, out globalId))
|
|||
|
ES3ReferenceMgrBase.Current.Add(localRef.Key, globalId);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static long GetNewRefID()
|
|||
|
{
|
|||
|
return ES3ReferenceMgrBase.GetNewRefID();
|
|||
|
}
|
|||
|
#if UNITY_EDITOR
|
|||
|
public void GeneratePrefabReferences()
|
|||
|
{
|
|||
|
#if UNITY_2021_3_OR_NEWER
|
|||
|
if (this.gameObject.scene.name != null || UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null)
|
|||
|
#elif UNITY_2018_3_OR_NEWER
|
|||
|
if (this.gameObject.scene.name != null || UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null)
|
|||
|
#else
|
|||
|
if (this.gameObject.scene.name != null)
|
|||
|
#endif
|
|||
|
return;
|
|||
|
|
|||
|
// Create a new reference list so that any objects which are no longer dependencies are removed.
|
|||
|
var tempLocalRefs = new ES3RefIdDictionary();
|
|||
|
|
|||
|
// Get dependencies of children also.
|
|||
|
var transforms = GetComponentsInChildren<Transform>();
|
|||
|
var gos = new GameObject[transforms.Length];
|
|||
|
for (int i = 0; i < transforms.Length; i++)
|
|||
|
gos[i] = transforms[i].gameObject;
|
|||
|
|
|||
|
bool addedNewReference = false;
|
|||
|
|
|||
|
// Add the GameObject's dependencies to the reference list.
|
|||
|
foreach (var obj in EditorUtility.CollectDependencies(gos))
|
|||
|
{
|
|||
|
var dependency = (UnityEngine.Object)obj;
|
|||
|
if (obj == null || !ES3ReferenceMgr.CanBeSaved(dependency))
|
|||
|
continue;
|
|||
|
|
|||
|
var id = Get(dependency);
|
|||
|
// If we're adding a new reference, do an Undo.RecordObject to ensure it persists.
|
|||
|
if (id == -1)
|
|||
|
{
|
|||
|
addedNewReference = true;
|
|||
|
Undo.RecordObject(this, "Update Easy Save 3 Prefab");
|
|||
|
EditorUtility.SetDirty(this);
|
|||
|
}
|
|||
|
tempLocalRefs.Add(dependency, id == -1 ? GetNewRefID() : id);
|
|||
|
}
|
|||
|
|
|||
|
if (addedNewReference || tempLocalRefs.Count != localRefs.Count)
|
|||
|
localRefs = tempLocalRefs;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Create a blank ES3Type for ES3Prefab as it does not require serialising/deserialising when stored as a Component.
|
|||
|
*/
|
|||
|
namespace ES3Types
|
|||
|
{
|
|||
|
[UnityEngine.Scripting.Preserve]
|
|||
|
public class ES3Type_ES3Prefab : ES3Type
|
|||
|
{
|
|||
|
public static ES3Type Instance = null;
|
|||
|
|
|||
|
public ES3Type_ES3Prefab() : base(typeof(ES3Prefab)) { Instance = this; }
|
|||
|
|
|||
|
public override void Write(object obj, ES3Writer writer)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public override object Read<T>(ES3Reader reader)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Use this ES3Type to serialise the .
|
|||
|
*/
|
|||
|
public class ES3Type_ES3PrefabInternal : ES3Type
|
|||
|
{
|
|||
|
public static ES3Type Instance = new ES3Type_ES3PrefabInternal();
|
|||
|
|
|||
|
public ES3Type_ES3PrefabInternal() : base(typeof(ES3Type_ES3PrefabInternal)) { Instance = this; }
|
|||
|
|
|||
|
public override void Write(object obj, ES3Writer writer)
|
|||
|
{
|
|||
|
ES3Prefab es3Prefab = (ES3Prefab)obj;
|
|||
|
|
|||
|
writer.WriteProperty("prefabId", es3Prefab.prefabId.ToString(), ES3Type_string.Instance);
|
|||
|
writer.WriteProperty("refs", es3Prefab.GetReferences());
|
|||
|
}
|
|||
|
|
|||
|
public override object Read<T>(ES3Reader reader)
|
|||
|
{
|
|||
|
var prefabId = reader.ReadRefProperty();
|
|||
|
|
|||
|
if (ES3ReferenceMgrBase.Current == null)
|
|||
|
return null;
|
|||
|
|
|||
|
var es3Prefab = ES3ReferenceMgrBase.Current.GetPrefab(prefabId);
|
|||
|
if (es3Prefab == null)
|
|||
|
throw new MissingReferenceException("Prefab with ID " + prefabId + " could not be found.\nPress the 'Refresh References' button on the ES3ReferenceMgr Component of the Easy Save 3 Manager in the scene to refresh prefabs.");
|
|||
|
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
// Use PrefabUtility.InstantiatePrefab if we're saving in the Editor and the application isn't playing.
|
|||
|
// This keeps the connection to the prefab, which is useful for Editor scripts saving data outside of runtime.
|
|||
|
var instance = Application.isPlaying ? GameObject.Instantiate(es3Prefab.gameObject) : PrefabUtility.InstantiatePrefab(es3Prefab.gameObject);
|
|||
|
#else
|
|||
|
var instance = GameObject.Instantiate(es3Prefab.gameObject);
|
|||
|
#endif
|
|||
|
var instanceES3Prefab = ((GameObject)instance).GetComponent<ES3Prefab>();
|
|||
|
if (instanceES3Prefab == null)
|
|||
|
throw new MissingReferenceException("Prefab with ID " + prefabId + " was found, but it does not have an ES3Prefab component attached.");
|
|||
|
|
|||
|
ReadInto<T>(reader, instance);
|
|||
|
|
|||
|
return instanceES3Prefab.gameObject;
|
|||
|
}
|
|||
|
|
|||
|
public override void ReadInto<T>(ES3Reader reader, object obj)
|
|||
|
{
|
|||
|
// Load as ES3Refs and convert to longs.
|
|||
|
var localToGlobal_refs = reader.ReadProperty<Dictionary<ES3Ref, ES3Ref>>(ES3Type_ES3RefDictionary.Instance);
|
|||
|
var localToGlobal = new Dictionary<long, long>();
|
|||
|
foreach (var kvp in localToGlobal_refs)
|
|||
|
localToGlobal.Add(kvp.Key.id, kvp.Value.id);
|
|||
|
|
|||
|
if (ES3ReferenceMgrBase.Current == null)
|
|||
|
return;
|
|||
|
|
|||
|
((GameObject)obj).GetComponent<ES3Prefab>().ApplyReferences(localToGlobal);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|