我有 3 种预制件:A、B 和 C
在设计关卡时,在播放模式下,我在关卡中添加和定位它们。
如何创建一个 ScriptableObject 来保存对这些预制件及其转换的所有实例的引用?
具体来说,在 PlayMode 中,Scriptable 对象应动态响应预制件在 PlayMode 中的位置和旋转变化。
我无法想象如何做到这一点,尽管这似乎是对 Scriptable Objects 的一种很好的使用。
我有 3 种预制件:A、B 和 C
在设计关卡时,在播放模式下,我在关卡中添加和定位它们。
如何创建一个 ScriptableObject 来保存对这些预制件及其转换的所有实例的引用?
具体来说,在 PlayMode 中,Scriptable 对象应动态响应预制件在 PlayMode 中的位置和旋转变化。
我无法想象如何做到这一点,尽管这似乎是对 Scriptable Objects 的一种很好的使用。
您可以将所需的信息存储在专门的课程中,例如
[Serializable]
public class InstanceInformation
{
public GameObject UsedPrefab;
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Scale;
public InstanceInformation(GameObject usedPrefab, Transform transform)
{
UsedPrefab = usedPrefab;
Rotation = transform.rotation;
Position = transform.position;
Scale = transform.localScale;
}
public void UpdateValues(Transform transform)
{
Rotation = transform.rotation;
Position = transform.position;
Scale = transform.localScale;
}
}
并且在您的 ScriptableObject 中有一个
[CreateAssetMenu]
public class LevelData : ScriptableObject
{
public List<InstanceInformation> instances = new List<InstanceInformation>();
}
然后每次实例化一个预制件时,您也会在其中创建一个相应的条目instances
。
所以稍后在你的经理脚本中,你实例化你所做的事情,例如
// Reference this via the Inspector
public LevelData scriptable;
// For keeping a link for the currently Instantiated stuff
// so everytime you manipulate them later on you can update the according data entry
private Dictionary<GameObject, InstanceInformation> objectToInstanceInformation = new Dictionary<GameObject, InstanceInformation>();
...
var obj = Instantiate(aPrefab, aPosition, aRotation);
var instanceInfo = new InstanceInfo(aPrefab, obj.transform);
// Add the reference to the ScriptableObject list
scriptable.instances.Add(instanceInfo);
// And also keep track of the reference linked to the actual instance
objectToInstanceInformation.Add(obj, instanceInfo);
现在你可以重复或在某个时刻打电话
public void SaveInstanceInformations()
{
foreach(var kvp in objectToInstanceInformation)
{
var obj = kvp.key;
var instanceInfo = kvp.value;
instanceInfo.UpdateValues(obj.transform);
}
}
因为InstanceInformation
是一个类,因此一个引用类型在此处自动更改值也会更改instances
ScriptableObject 中的相应条目!
所以稍后当你想加载状态时,你可以简单地做
foreach (var instance in scriptable.instances)
{
var obj = Instantiate(instance.UsedPrefab, instance.Position, instance.Rotation);
obj.transform.localScale = instance.Scale;
objectToInstanceInformation.Add(obj, instance);
}
最后,为了能够持久存储这些数据,您可以使用BinaryFormatter
类似
[CreateAssetMenu]
public class LevelData : ScriptableObject
{
// here you can set a filename the data shall be stored to
[SerializeField] private string fileName = "level.dat";
public List<InstanceInformation> instances = new List<InstanceInformation>();
// called when the object is loaded
private void OnEnable()
{
// try to load the data from drive
// in the editor use the Application.streamingAssetsPath
// in a build use Application.persistentDataPath
var folder = Application.isEditor ? Application.streamingAssetsPath : Application.persistentDataPath;
if(!File.Exists(Path.Combine(folder, fileName)))
{
// on the first run the folder and file will not exist
// in the editor do nothing (the file simply doesn't exist yet)
// in a build copy the content from the streaming assets folder to the persistent data path (if exists)
var fallbackFolder = Application.streamingAssetsPath;
if(!Driectoy.Exists(fallbackFolder)) return;
if(!File.Exists(Path.Combine(fallbackFolder, fileName))) return;
// copy fallback file to persistent file
File.Copy(Path.Combine(fallbackFolder, FileName), Path.Combine(folder, fileName));
}
// load the list
using(var file = File.Open(Path.Combine(folder, fileName), FileMode.Open, FileAccess.Read, FileShare.Read))
{
var binaryFormatter = new BinaryFormatter();
instances = (List<InstanceInformation>)binaryFormatter.Deserialize(file);
}
}
// called when the object is destroyed (you app ended)
private void OnDestroy()
{
// save the data to drive
// in the editor use the Application.streamingAssets
// in a build use Application.persistentDataPath
var folder = Application.isEditor ? Application.streamingAssets : Application.persistentDataPath;
if(!Directoy.Exists(folder)) Directory.CreateDirectory(folder);
using(var file = File.Open(Path.Combine(folder, fileName), FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(file, instances);
}
}
}