1. 程式人生 > >[Unity]儲存執行時對Scene中GameObject屬性的修改

[Unity]儲存執行時對Scene中GameObject屬性的修改

google中原連結已失效,只剩快照了(unity3d-save-your-in-play-transform-modifications),所以寫篇文章記錄一下.

很多時候,我們想把運行遊戲時對場景中物體屬性的修改儲存下來,但一旦停止執行這些修改就恢復執行前的狀態了。對於有原Prefab的物體比較好辦,把執行中修改後的物體直接拖回去覆蓋原Prefab即可.而對只在Scene中存在的GameObject就沒法這樣做了。這裡以transform為例總結一種解決方法.

總體思路就是在執行中修改完畢後,save到某檔案中,然後停止執行後再讀取回來。但過程中又牽扯到一個問題:如果給transform新增按鈕會改變transform的佈局,而且用base.DrawDefaultInspector();或者base.OnInspectorGUI();都並不能畫出預設佈局的樣子,而是會變成這樣:

多出來個W,很難看。所以我們要先解決預設檢視佈局的問題,方法是用下面提到的DrawABetterInspector()函式來繪製.然後我們新增Save和Load兩個按鈕並新增相關功能.

程式碼如下:

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

[CustomEditor(typeof(Transform))]
public class TransformEditor : Editor
{
	public void DrawABetterInspector(Transform t)
	{
		// Replicate the standard transform inspector gui        
		EditorGUIUtility.labelWidth = 25;
		EditorGUIUtility.fieldWidth = 50;

		EditorGUI.indentLevel = 0;
		Vector3 position = EditorGUILayout.Vector3Field("Position", t.localPosition);
		Vector3 eulerAngles = EditorGUILayout.Vector3Field("Rotation", t.localEulerAngles);
		Vector3 scale = EditorGUILayout.Vector3Field("Scale", t.localScale);

		EditorGUIUtility.labelWidth = 0;
		EditorGUIUtility.fieldWidth = 0;

		if (GUI.changed)
		{
			Undo.RecordObject(t, "Transform Change");

			t.localPosition = FixIfNaN(position);
			t.localEulerAngles = FixIfNaN(eulerAngles);
			t.localScale = FixIfNaN(scale);
		}
	}

	private Vector3 FixIfNaN(Vector3 v)
	{
		if (float.IsNaN(v.x))
		{
			v.x = 0.0f;
		}
		if (float.IsNaN(v.y))
		{
			v.y = 0.0f;
		}
		if (float.IsNaN(v.z))
		{
			v.z = 0.0f;
		}
		return v;
	}

	public override void OnInspectorGUI()
	{
		Transform t = (Transform)target;

		DrawABetterInspector(t);

		if (GUILayout.Button("Save"))
		{
			SaveData(t.gameObject);
		}

		if (GUILayout.Button("Load"))
		{
			LoadData(t.gameObject);
		}

	}

	string GetInstanceFileName(GameObject baseObject)
	{
		return System.IO.Path.GetTempPath() + baseObject.name + "_" + baseObject.GetInstanceID() + ".keepTransform.txt";
	}

	public void SaveData(GameObject baseObject)
	{
		List<string> saveData = new List<string>();

		saveData.Add(this.GetInstanceID().ToString());

		saveData.Add(baseObject.transform.localPosition.x.ToString());
		saveData.Add(baseObject.transform.localPosition.y.ToString());
		saveData.Add(baseObject.transform.localPosition.z.ToString());

		saveData.Add(baseObject.transform.localRotation.eulerAngles.x.ToString());
		saveData.Add(baseObject.transform.localRotation.eulerAngles.y.ToString());
		saveData.Add(baseObject.transform.localRotation.eulerAngles.z.ToString());

		saveData.Add(baseObject.transform.localScale.x.ToString());
		saveData.Add(baseObject.transform.localScale.y.ToString());
		saveData.Add(baseObject.transform.localScale.z.ToString());


		System.IO.File.WriteAllLines(GetInstanceFileName(baseObject), saveData.ToArray());
	}

	public void LoadData(GameObject baseObject)
	{
		string[] lines = System.IO.File.ReadAllLines(GetInstanceFileName(baseObject));
		if (lines.Length > 0)
		{
			baseObject.transform.localPosition = new Vector3(System.Convert.ToSingle(lines[1]), System.Convert.ToSingle(lines[2]), System.Convert.ToSingle(lines[3]));
			baseObject.transform.localRotation = Quaternion.Euler(System.Convert.ToSingle(lines[4]), System.Convert.ToSingle(lines[5]), System.Convert.ToSingle(lines[6]));
			baseObject.transform.localScale = new Vector3(System.Convert.ToSingle(lines[7]), System.Convert.ToSingle(lines[8]), System.Convert.ToSingle(lines[9]));
			System.IO.File.Delete(GetInstanceFileName(baseObject));
		}
	}
}


將以上檔案儲存為TransformEditor.cs並儲存到Assets/Editor資料夾下即可,修改後的Transform面板如下:


建立一個Cube,執行場景,對其Transform做些修改,點Save儲存.然後停止場景,Cube恢復修改前的樣子.此時再點選Load,可以看到儲存的修改被載入回來了.