Unity 便捷的使用Transform中的复制粘贴

Unity 便捷的使用Transform中的复制粘贴

前言

前两天的项目遇到一个频繁复制粘贴大量物体的需求,位置偏移又很小,使用Unity自带的组件复制粘贴功能需要点击3下鼠标,为了节约鼠标,保护环境,写下此文档。

用法

直接放在项目中的Editor文件夹下即可
Tips:会占用剪贴板!!!
应该可以直接调用Unity自带的CopyComponent,以后可能会继续添加。

效果

在Transform组件下面扩展了2个按钮,方便进行复制粘贴。
使用图片

使用效果

源码

源码中添加了中文注释,可自行修改。

using UnityEngine;
using UnityEditor;
using System.Text;

[CanEditMultipleObjects]
[CustomEditor(typeof(Transform), true)]
public class TransformInspector : Editor
{
	static public TransformInspector instance;

	//当前的本地坐标
	SerializedProperty mPos;
	//当前的本地旋转
	SerializedProperty mRot;
	//当前的本地缩放
	SerializedProperty mScale;

	void OnEnable()
	{
		instance = this;

		if (this)
		{
			try
			{
				var so = serializedObject;
				mPos = so.FindProperty("m_LocalPosition");
				mRot = so.FindProperty("m_LocalRotation");
				mScale = so.FindProperty("m_LocalScale");
			}
			catch { }
		}
	}

	void OnDestroy() { instance = null; }

	/// <summary>
	/// Draw the inspector widget.绘制inspector小部件。
	/// </summary>
	public override void OnInspectorGUI()
	{
		//设置label的宽度
		EditorGUIUtility.labelWidth = 15f;

		serializedObject.Update();

		DrawPosition();
		DrawRotation();
		DrawScale();
		DrawCopyAndPaste();

		serializedObject.ApplyModifiedProperties();
	}


	void DrawCopyAndPaste()
	{
		GUILayout.BeginHorizontal();
		bool reset = GUILayout.Button("Copy");
		bool reset2 = GUILayout.Button("Paste");
		GUILayout.EndHorizontal();

        if (reset)
        {
			//把数值打印出来
			var select = Selection.activeGameObject;
			if (select == null)
				return;
			//Debug.Log(select.name+"("+ mPos.vector3Value.x.ToString()+ ","+ mPos.vector3Value.y.ToString() + ","+ mPos.vector3Value.z.ToString() + ")");
			//Debug.Log(select.name + mRot.quaternionValue);
			//Debug.Log(select.name + "(" + mScale.vector3Value.x.ToString() + "," + mScale.vector3Value.y.ToString() + "," + mScale.vector3Value.z.ToString() + ")");

			StringBuilder s = new StringBuilder();
			s.Append("TransformInspector_" + "(" + mPos.vector3Value.x.ToString() + "," + mPos.vector3Value.y.ToString() + "," + mPos.vector3Value.z.ToString() + ")" + "_");
			s.Append(mRot.quaternionValue+"_");
			s.Append("(" + mScale.vector3Value.x.ToString() + "," + mScale.vector3Value.y.ToString() + "," + mScale.vector3Value.z.ToString() + ")");
			//添加到剪贴板
			UnityEngine.GUIUtility.systemCopyBuffer = s.ToString();
		}
		if (reset2)
		{
			//把数值打印出来
			//Debug.Log(UnityEngine.GUIUtility.systemCopyBuffer);
			string s = UnityEngine.GUIUtility.systemCopyBuffer;
			string[] sArr = s.Split('_');
			if (sArr[0] != "TransformInspector")
			{
				Debug.LogError("未复制Transform组件内容!Transform component content not copied!");
				return;
			}
            //Debug.Log("Pos:" + sArr[1]);
            //Debug.Log("Rot:" + sArr[2]);
            //Debug.Log("Scale:" + sArr[3]);
            try
            {
				mPos.vector3Value = ParseV3(sArr[1]);
				mRot.quaternionValue = new Quaternion() { x = ParseV4(sArr[2]).x, y = ParseV4(sArr[2]).y, z = ParseV4(sArr[2]).z, w = ParseV4(sArr[2]).w };
				mScale.vector3Value = ParseV3(sArr[3]);
			}
            catch (System.Exception ex)
            {
				Debug.LogError(ex);
                return;
            }

		}
	}
	/// <summary>
	/// String To Vector3
	/// </summary>
	/// <param name="strVector3"></param>
	/// <returns></returns>
	Vector3 ParseV3(string strVector3)
    {
		strVector3 = strVector3.Replace("(", "").Replace(")", "");
		string[] s = strVector3.Split(',');
		return new Vector3(float.Parse(s[0]), float.Parse(s[1]), float.Parse(s[2]));
    }
	/// <summary>
	/// String To Vector4
	/// </summary>
	/// <param name="strVector4"></param>
	/// <returns></returns>
	Vector4 ParseV4(string strVector4)
	{
		strVector4 = strVector4.Replace("(", "").Replace(")", "");
		string[] s = strVector4.Split(',');
		return new Vector4(float.Parse(s[0]), float.Parse(s[1]), float.Parse(s[2]),float.Parse(s[3]));
	}
	#region Position 位置
	void DrawPosition()
	{
		GUILayout.BeginHorizontal();
		EditorGUILayout.PropertyField(mPos.FindPropertyRelative("x"));
		EditorGUILayout.PropertyField(mPos.FindPropertyRelative("y"));
		EditorGUILayout.PropertyField(mPos.FindPropertyRelative("z"));
		bool reset = GUILayout.Button("P", GUILayout.Width(20f));

		GUILayout.EndHorizontal();

		if (reset) mPos.vector3Value = Vector3.zero;
	}
    #endregion
    #region Scale 缩放
    void DrawScale()
	{
		GUILayout.BeginHorizontal();
		{
			EditorGUILayout.PropertyField(mScale.FindPropertyRelative("x"));
			EditorGUILayout.PropertyField(mScale.FindPropertyRelative("y"));
			EditorGUILayout.PropertyField(mScale.FindPropertyRelative("z"));
			bool reset = GUILayout.Button("S", GUILayout.Width(20f));
			if (reset) mScale.vector3Value = Vector3.one;
		}
		GUILayout.EndHorizontal();
	}
    #endregion
    #region Rotation is ugly as hell... since there is no native support for quaternion property drawing 旋转是丑陋的地狱。。。因为四元数属性绘图没有本地支持
    enum Axes : int
	{
		None = 0,
		X = 1,
		Y = 2,
		Z = 4,
		All = 7,
	}

	Axes CheckDifference(Transform t, Vector3 original)
	{
		Vector3 next = t.localEulerAngles;

		Axes axes = Axes.None;

		if (Differs(next.x, original.x)) axes |= Axes.X;
		if (Differs(next.y, original.y)) axes |= Axes.Y;
		if (Differs(next.z, original.z)) axes |= Axes.Z;

		return axes;
	}

	Axes CheckDifference(SerializedProperty property)
	{
		Axes axes = Axes.None;

		if (property.hasMultipleDifferentValues)
		{
			Vector3 original = property.quaternionValue.eulerAngles;

			foreach (Object obj in serializedObject.targetObjects)
			{
				axes |= CheckDifference(obj as Transform, original);
				if (axes == Axes.All) break;
			}
		}
		return axes;
	}

	/// <summary>
	/// Draw an editable float field. 绘制可编辑的浮动字段。
	/// </summary>
	/// <param name="hidden">Whether to replace the value with a dash 是否将值替换为破折号</param>
	/// <param name="greyedOut">Whether the value should be greyed out or not 值是否应灰显</param>
	static bool FloatField(string name, ref float value, bool hidden, GUILayoutOption opt)
	{
		float newValue = value;
		GUI.changed = false;

		if (!hidden)
		{
			newValue = EditorGUILayout.FloatField(name, newValue, opt);
		}
		else
		{
			float.TryParse(EditorGUILayout.TextField(name, "--", opt), out newValue);
		}

		if (GUI.changed && Differs(newValue, value))
		{
			value = newValue;
			return true;
		}
		return false;
	}

	/// <summary>
	/// Because Mathf.Approximately is too sensitive.因为数学近似值太敏感了。
	/// </summary>
	static bool Differs(float a, float b) { return Mathf.Abs(a - b) > 0.0001f; }

	/// <summary>
	/// 注册Undo
	/// </summary>
	/// <param name="name"></param>
	/// <param name="objects"></param>
	static public void RegisterUndo(string name, params Object[] objects)
	{
		if (objects != null && objects.Length > 0)
		{
			UnityEditor.Undo.RecordObjects(objects, name);

			foreach (Object obj in objects)
			{
				if (obj == null) continue;
				EditorUtility.SetDirty(obj);
			}
		}
	}

	/// <summary>
	/// 角度处理
	/// </summary>
	/// <param name="angle"></param>
	/// <returns></returns>
	static public float WrapAngle(float angle)
	{
		while (angle > 180f) angle -= 360f;
		while (angle < -180f) angle += 360f;
		return angle;
	}

	void DrawRotation()
	{
		GUILayout.BeginHorizontal();
		{
			Vector3 visible = (serializedObject.targetObject as Transform).localEulerAngles;

			visible.x = WrapAngle(visible.x);
			visible.y = WrapAngle(visible.y);
			visible.z = WrapAngle(visible.z);

			Axes changed = CheckDifference(mRot);
			Axes altered = Axes.None;

			GUILayoutOption opt = GUILayout.MinWidth(30f);

			if (FloatField("X", ref visible.x, (changed & Axes.X) != 0, opt)) altered |= Axes.X;
			if (FloatField("Y", ref visible.y, (changed & Axes.Y) != 0, opt)) altered |= Axes.Y;
			if (FloatField("Z", ref visible.z, (changed & Axes.Z) != 0, opt)) altered |= Axes.Z;
			bool reset = GUILayout.Button("R", GUILayout.Width(20f));

			if (reset)
			{
				mRot.quaternionValue = Quaternion.identity;
			}
			else if (altered != Axes.None)
			{
				RegisterUndo("Change Rotation", serializedObject.targetObjects);

				foreach (Object obj in serializedObject.targetObjects)
				{
					Transform t = obj as Transform;
					Vector3 v = t.localEulerAngles;

					if ((altered & Axes.X) != 0) v.x = visible.x;
					if ((altered & Axes.Y) != 0) v.y = visible.y;
					if ((altered & Axes.Z) != 0) v.z = visible.z;

					t.localEulerAngles = v;
				}
			}
		}
		GUILayout.EndHorizontal();
	}
	#endregion
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值