UnityExplorer/src/Menu/Windows/GameObjectWindow.cs

653 lines
23 KiB
C#
Raw Normal View History

2020-08-07 22:19:03 +10:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Explorer
{
public class GameObjectWindow : UIWindow
2020-08-07 22:19:03 +10:00
{
public override string Title => WindowManager.TabView
? $"<color=cyan>[G]</color> {m_object.name}"
: $"GameObject Inspector ({m_object.name})";
2020-08-07 22:19:03 +10:00
public GameObject m_object;
// gui element holders
private string m_name;
private string m_scene;
private Transform[] m_children;
private Vector2 m_transformScroll = Vector2.zero;
private PageHelper ChildPages = new PageHelper();
2020-08-07 22:19:03 +10:00
private Component[] m_components;
2020-08-07 22:19:03 +10:00
private Vector2 m_compScroll = Vector2.zero;
private PageHelper CompPages = new PageHelper();
2020-08-07 22:19:03 +10:00
private readonly Vector3[] m_cachedInput = new Vector3[3];
2020-08-07 22:19:03 +10:00
private float m_translateAmount = 0.3f;
private float m_rotateAmount = 50f;
private float m_scaleAmount = 0.1f;
private bool m_freeze;
private Vector3 m_frozenPosition;
private Quaternion m_frozenRotation;
private Vector3 m_frozenScale;
private bool m_autoApplyTransform;
private bool m_autoUpdateTransform;
private bool m_localContext;
2020-08-07 22:19:03 +10:00
private readonly List<Component> m_cachedDestroyList = new List<Component>();
2020-08-07 22:19:03 +10:00
//private string m_addComponentInput = "";
private string m_setParentInput = "Enter a GameObject name or path";
2020-08-07 22:19:03 +10:00
public bool GetObjectAsGameObject()
{
var targetType = Target.GetType();
if (targetType == typeof(GameObject))
{
m_object = Target as GameObject;
return true;
}
else if (targetType == typeof(Transform))
{
m_object = (Target as Transform).gameObject;
return true;
}
MelonLogger.Log("Error: Target is null or not a GameObject/Transform!");
DestroyWindow();
return false;
}
public override void Init()
{
if (!GetObjectAsGameObject())
{
return;
}
m_name = m_object.name;
m_scene = string.IsNullOrEmpty(m_object.scene.name)
? "None (Asset/Resource)"
: m_object.scene.name;
2020-08-07 22:19:03 +10:00
CacheTransformValues();
Update();
2020-08-07 22:19:03 +10:00
}
private void CacheTransformValues()
{
if (m_localContext)
{
m_cachedInput[0] = m_object.transform.localPosition;
m_cachedInput[1] = m_object.transform.localEulerAngles;
}
else
{
m_cachedInput[0] = m_object.transform.position;
m_cachedInput[1] = m_object.transform.eulerAngles;
}
m_cachedInput[2] = m_object.transform.localScale;
}
2020-08-07 22:19:03 +10:00
public override void Update()
{
try
2020-08-07 22:19:03 +10:00
{
if (Target == null)
{
MelonLogger.Log("Target is null!");
DestroyWindow();
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
MelonLogger.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
if (!m_object && !GetObjectAsGameObject())
{
throw new Exception("Object is null!");
}
if (m_freeze)
{
if (m_localContext)
{
m_object.transform.localPosition = m_frozenPosition;
m_object.transform.localRotation = m_frozenRotation;
}
else
{
m_object.transform.position = m_frozenPosition;
m_object.transform.rotation = m_frozenRotation;
}
m_object.transform.localScale = m_frozenScale;
}
// update child objects
var childList = new List<Transform>();
for (int i = 0; i < m_object.transform.childCount; i++)
{
childList.Add(m_object.transform.GetChild(i));
}
childList.Sort((a, b) => b.childCount.CompareTo(a.childCount));
m_children = childList.ToArray();
ChildPages.ItemCount = m_children.Length;
// update components
var compList = new Il2CppSystem.Collections.Generic.List<Component>();
m_object.GetComponentsInternal(ReflectionHelpers.ComponentType, true, false, true, false, compList);
m_components = compList.ToArray();
CompPages.ItemCount = m_components.Length;
2020-08-07 22:19:03 +10:00
}
catch (Exception e)
{
DestroyOnException(e);
}
}
private void DestroyOnException(Exception e)
{
MelonLogger.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}");
DestroyWindow();
2020-08-07 22:19:03 +10:00
}
private void InspectGameObject(Transform obj)
2020-08-07 22:19:03 +10:00
{
var window = WindowManager.InspectObject(obj, out bool created);
if (created)
{
window.m_rect = new Rect(this.m_rect.x, this.m_rect.y, this.m_rect.width, this.m_rect.height);
DestroyWindow();
}
}
private void ReflectObject(Il2CppSystem.Object obj)
{
2020-09-12 02:52:52 +10:00
var window = WindowManager.InspectObject(obj, out bool created, true);
2020-08-07 22:19:03 +10:00
if (created)
{
if (this.m_rect.x <= (Screen.width - this.m_rect.width - 100))
{
window.m_rect = new Rect(
this.m_rect.x + this.m_rect.width + 20,
this.m_rect.y,
550,
700);
}
else
{
window.m_rect = new Rect(this.m_rect.x + 50, this.m_rect.y + 50, 550, 700);
}
}
}
public override void WindowFunction(int windowID)
{
try
{
var rect = WindowManager.TabView ? TabViewWindow.Instance.m_rect : this.m_rect;
2020-08-07 22:19:03 +10:00
if (!WindowManager.TabView)
{
Header();
GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
}
2020-08-07 22:19:03 +10:00
scroll = GUIUnstrip.BeginScrollView(scroll);
2020-08-07 22:19:03 +10:00
GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
if (m_scene == UnityHelpers.ActiveSceneName)
2020-08-07 22:19:03 +10:00
{
if (GUILayout.Button("<color=#00FF00>Send to Scene View</color>", new GUILayoutOption[] { GUILayout.Width(150) }))
{
ScenePage.Instance.SetTransformTarget(m_object.transform);
MainMenu.SetCurrentPage(0);
}
2020-08-07 22:19:03 +10:00
}
if (GUILayout.Button("Reflection Inspect", new GUILayoutOption[] { GUILayout.Width(150) }))
{
WindowManager.InspectObject(Target, out _, true);
}
GUILayout.EndHorizontal();
2020-08-07 22:19:03 +10:00
GUILayout.BeginHorizontal(null);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = m_object.transform.GetGameObjectPath();
if (m_object.transform.parent != null)
2020-08-07 22:19:03 +10:00
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
InspectGameObject(m_object.transform.parent);
}
2020-08-07 22:19:03 +10:00
}
GUILayout.TextArea(pathlabel, null);
GUILayout.EndHorizontal();
2020-08-07 22:19:03 +10:00
GUILayout.BeginHorizontal(null);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUILayout.TextArea(m_name, null);
GUILayout.EndHorizontal();
2020-08-07 22:19:03 +10:00
// --- Horizontal Columns section ---
GUILayout.BeginHorizontal(null);
2020-08-07 22:19:03 +10:00
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
TransformList(rect);
GUILayout.EndVertical();
2020-08-07 22:19:03 +10:00
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
ComponentList(rect);
GUILayout.EndVertical();
2020-08-07 22:19:03 +10:00
GUILayout.EndHorizontal(); // end horiz columns
2020-08-07 22:19:03 +10:00
GameObjectControls();
2020-08-07 22:19:03 +10:00
GUIUnstrip.EndScrollView();
2020-08-07 22:19:03 +10:00
if (!WindowManager.TabView)
{
m_rect = ResizeDrag.ResizeWindow(rect, windowID);
2020-08-07 22:19:03 +10:00
GUIUnstrip.EndArea();
}
}
catch (Exception e)
{
DestroyOnException(e);
}
2020-08-07 22:19:03 +10:00
}
private void TransformList(Rect m_rect)
2020-08-07 22:19:03 +10:00
{
GUILayout.BeginVertical(GUI.skin.box, null);
m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll);
2020-08-07 22:19:03 +10:00
GUILayout.Label("<b><size=15>Children</size></b>", null);
GUILayout.BeginHorizontal(null);
ChildPages.DrawLimitInputArea();
if (ChildPages.ItemCount > ChildPages.ItemsPerPage)
2020-08-07 22:19:03 +10:00
{
ChildPages.CurrentPageLabel();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
2020-08-07 22:19:03 +10:00
{
ChildPages.TurnPage(Turn.Left, ref this.m_transformScroll);
2020-08-07 22:19:03 +10:00
}
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
2020-08-07 22:19:03 +10:00
{
ChildPages.TurnPage(Turn.Right, ref this.m_transformScroll);
}
}
GUILayout.EndHorizontal();
if (m_children != null && m_children.Length > 0)
{
int start = ChildPages.CalculateOffsetIndex();
for (int j = start; (j < start + ChildPages.ItemsPerPage && j < ChildPages.ItemCount); j++)
{
var obj = m_children[j];
2020-08-07 22:19:03 +10:00
if (!obj)
{
GUILayout.Label("null", null);
continue;
}
UIHelpers.GOButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80);
2020-08-07 22:19:03 +10:00
}
}
else
{
GUILayout.Label("<i>None</i>", null);
}
GUIUnstrip.EndScrollView();
2020-08-07 22:19:03 +10:00
GUILayout.EndVertical();
}
private void ComponentList(Rect m_rect)
2020-08-07 22:19:03 +10:00
{
GUILayout.BeginVertical(GUI.skin.box, null);
m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll);
2020-08-07 22:19:03 +10:00
GUILayout.Label("<b><size=15>Components</size></b>", null);
GUILayout.BeginHorizontal(null);
CompPages.DrawLimitInputArea();
if (CompPages.ItemCount > CompPages.ItemsPerPage)
{
CompPages.CurrentPageLabel();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{
CompPages.TurnPage(Turn.Left, ref this.m_compScroll);
}
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
{
CompPages.TurnPage(Turn.Right, ref this.m_compScroll);
}
}
GUILayout.EndHorizontal();
2020-08-07 22:19:03 +10:00
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (m_cachedDestroyList.Count > 0)
{
m_cachedDestroyList.Clear();
}
if (m_components != null)
2020-08-07 22:19:03 +10:00
{
int start = CompPages.CalculateOffsetIndex();
for (int j = start; (j < start + CompPages.ItemsPerPage && j < CompPages.ItemCount); j++)
2020-08-07 22:19:03 +10:00
{
var component = m_components[j];
if (!component) continue;
2020-08-07 22:19:03 +10:00
var ilType = component.GetIl2CppType();
GUILayout.BeginHorizontal(null);
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
{
BehaviourEnabledBtn(component.TryCast<Behaviour>());
}
else
{
GUIUnstrip.Space(26);
}
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) }))
{
ReflectObject(component);
}
if (GUILayout.Button("<color=red>-</color>", new GUILayoutOption[] { GUILayout.Width(20) }))
{
m_cachedDestroyList.Add(component);
}
GUILayout.EndHorizontal();
2020-08-07 22:19:03 +10:00
}
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
if (m_cachedDestroyList.Count > 0)
{
for (int i = m_cachedDestroyList.Count - 1; i >= 0; i--)
{
var comp = m_cachedDestroyList[i];
GameObject.Destroy(comp);
}
}
GUIUnstrip.EndScrollView();
2020-08-07 22:19:03 +10:00
GUILayout.EndVertical();
}
private void BehaviourEnabledBtn(Behaviour obj)
{
var _col = GUI.color;
bool _enabled = obj.enabled;
if (_enabled)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.red;
}
// ------ toggle active button ------
_enabled = GUILayout.Toggle(_enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
if (obj.enabled != _enabled)
{
obj.enabled = _enabled;
}
GUI.color = _col;
}
private void GameObjectControls()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
2020-08-07 22:19:03 +10:00
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", null);
GUILayout.BeginHorizontal(null);
bool m_active = m_object.activeSelf;
m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>",
new GUILayoutOption[] { GUILayout.Width(80) });
if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); }
UIHelpers.InstantiateButton(m_object, 100);
2020-08-07 22:19:03 +10:00
if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) }))
{
GameObject.DontDestroyOnLoad(m_object);
m_object.hideFlags |= HideFlags.DontUnloadUnusedAsset;
}
2020-08-07 22:19:03 +10:00
var lbl = m_freeze ? "<color=lime>Unfreeze</color>" : "<color=orange>Freeze Pos/Rot</color>";
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(110) }))
{
m_freeze = !m_freeze;
if (m_freeze)
{
UpdateFreeze();
}
}
GUILayout.EndHorizontal();
2020-08-07 22:19:03 +10:00
GUILayout.BeginHorizontal(null);
m_setParentInput = GUILayout.TextField(m_setParentInput, null);
2020-08-07 22:19:03 +10:00
if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
{
if (GameObject.Find(m_setParentInput) is GameObject newparent)
{
m_object.transform.parent = newparent.transform;
}
else
{
MelonLogger.LogWarning($"Could not find gameobject '{m_setParentInput}'");
}
}
if (GUILayout.Button("Detach from parent", new GUILayoutOption[] { GUILayout.Width(160) }))
{
m_object.transform.parent = null;
}
2020-08-07 22:19:03 +10:00
GUILayout.EndHorizontal();
GUILayout.BeginVertical(GUI.skin.box, null);
m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false);
m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true);
m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false);
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("<color=lime>Apply to Transform</color>", null) || m_autoApplyTransform)
{
if (m_localContext)
{
m_object.transform.localPosition = m_cachedInput[0];
m_object.transform.localEulerAngles = m_cachedInput[1];
}
else
{
m_object.transform.position = m_cachedInput[0];
m_object.transform.eulerAngles = m_cachedInput[1];
}
m_object.transform.localScale = m_cachedInput[2];
if (m_freeze)
{
UpdateFreeze();
}
}
if (GUILayout.Button("<color=lime>Update from Transform</color>", null) || m_autoUpdateTransform)
{
CacheTransformValues();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?");
BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?");
GUILayout.EndHorizontal();
bool b = m_localContext;
b = GUILayout.Toggle(b, "<color=" + (b ? "lime" : "red") + ">Use local transform values?</color>", null);
if (b != m_localContext)
{
m_localContext = b;
CacheTransformValues();
if (m_freeze)
{
UpdateFreeze();
}
}
2020-08-07 22:19:03 +10:00
GUILayout.EndVertical();
if (GUILayout.Button("<color=red><b>Destroy</b></color>", new GUILayoutOption[] { GUILayout.Width(120) }))
2020-08-07 22:19:03 +10:00
{
GameObject.Destroy(m_object);
DestroyWindow();
return;
}
GUILayout.EndVertical();
}
private void UpdateFreeze()
{
if (m_localContext)
{
m_frozenPosition = m_object.transform.localPosition;
m_frozenRotation = m_object.transform.localRotation;
}
else
{
m_frozenPosition = m_object.transform.position;
m_frozenRotation = m_object.transform.rotation;
}
m_frozenScale = m_object.transform.localScale;
}
private void BoolToggle(ref bool value, string message)
{
string lbl = "<color=";
lbl += value ? "lime" : "red";
lbl += $">{message}</color>";
value = GUILayout.Toggle(value, lbl, null);
}
2020-08-07 22:19:03 +10:00
public enum TranslateType
{
Position,
Rotation,
Scale
}
private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime)
2020-08-07 22:19:03 +10:00
{
GUILayout.BeginHorizontal(null);
2020-09-05 20:38:46 +10:00
GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:",
new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) });
2020-08-07 22:19:03 +10:00
var transform = m_object.transform;
2020-08-07 22:19:03 +10:00
switch (mode)
{
case TranslateType.Position:
var pos = m_localContext ? transform.localPosition : transform.position;
GUILayout.Label(pos.ToString(), new GUILayoutOption[] { GUILayout.Width(250) });
break;
case TranslateType.Rotation:
var rot = m_localContext ? transform.localEulerAngles : transform.eulerAngles;
GUILayout.Label(rot.ToString(), new GUILayoutOption[] { GUILayout.Width(250) });
break;
case TranslateType.Scale:
GUILayout.Label(transform.localScale.ToString(), new GUILayoutOption[] { GUILayout.Width(250) });
break;
2020-08-07 22:19:03 +10:00
}
GUILayout.EndHorizontal();
Vector3 input = m_cachedInput[(int)mode];
2020-08-07 22:19:03 +10:00
GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref input.x, amount, multByTime);
2020-08-07 22:19:03 +10:00
GUILayout.Label("<color=cyan>Y:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref input.y, amount, multByTime);
2020-08-07 22:19:03 +10:00
GUILayout.Label("<color=cyan>Z:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref input.z, amount, multByTime);
2020-08-07 22:19:03 +10:00
GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) });
var amountInput = amount.ToString("F3");
amountInput = GUILayout.TextField(amountInput, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(amountInput, out float f))
2020-08-07 22:19:03 +10:00
{
amount = f;
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.EndHorizontal();
return input;
2020-08-07 22:19:03 +10:00
}
private void PlusMinusFloat(ref float f, float amount, bool multByTime)
{
string s = f.ToString("F3");
s = GUILayout.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(s, out float f2))
{
f = f2;
}
if (GUILayout.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{
f -= multByTime ? amount * Time.deltaTime : amount;
}
if (GUILayout.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{
f += multByTime ? amount * Time.deltaTime : amount;
}
}
}
}