Skip to content

Commit

Permalink
Added simple rigidbody drawer and enum pop-up... (#1902)
Browse files Browse the repository at this point in the history
* Added simple debug component editor for rigidbody, added searchbar for searching through the hierarchy when attempting to find a gameobject with a specific name or component.

* Undid showing the debuggers when debugging, removed extraneous whitespace from the NitroxDebugManager class, expanded the mass text input field

* Undo searchbar in hierarchy because there is already one in the other debugger.

* Added pagination to the existing search functionality.

* Added additional rigidbody fields, reworked search results display logic

* Reworked the rigidbody layout to use NitroxGUILayout, added support for Flags enums to the EnumPopup.

* Make velocity and angular velocity read-only.

* Set the read-only vector3 back to editable textboxes.

* Added method to handle an unknown Enum type.

* Added enum visibility toggle.

Co-authored-by: Measurity <[email protected]>
  • Loading branch information
Shirkie01 and Measurity authored Dec 3, 2022
1 parent 9e211fe commit 02789b6
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 31 deletions.
99 changes: 87 additions & 12 deletions NitroxClient/Debuggers/Drawer/NitroxGUILayout.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using UnityEngine;

namespace NitroxClient.Debuggers.Drawer;
Expand Down Expand Up @@ -76,16 +77,16 @@ public static IConvertible ConvertibleField(IConvertible value, float valueWidth
activeConvertibleFieldLastValue = value;
break;
case false when recorded: // Lost focus this frame
{
activeConvertibleField = -1;
if (parsed)
{
activeConvertibleField = -1;
if (parsed)
{
break;
}

value = TryParseIConvertible(value, strValue, out IConvertible newValue) ? newValue : activeConvertibleFieldLastValue;
break;
}

value = TryParseIConvertible(value, strValue, out IConvertible newValue) ? newValue : activeConvertibleFieldLastValue;
break;
}
}

return value;
Expand Down Expand Up @@ -145,13 +146,87 @@ public static float SliderField(float value, float minValue, float maxValue, flo
return Math.Max(minValue, Math.Min(maxValue, FloatField(value, valueWidth)));
}

/// <summary>
/// Displays an enum of an unknown type.
/// </summary>
/// <param name="selected">The selected enum value.</param>
/// <param name="buttonWidth">The button width</param>
/// <returns>The newly selected enum value.</returns>
public static Enum EnumPopup(Enum selected, float buttonWidth = VALUE_WIDTH)
{
return EnumPopupInternal(selected, buttonWidth);
}

public static T EnumPopup<T>(T selected, float buttonWidth = VALUE_WIDTH) where T : Enum
{
//TODO: Implement selecting of Enum in new window
Color oldColor = GUI.backgroundColor;
GUI.backgroundColor = Color.cyan;
GUILayout.Button(selected.ToString(), GUILayout.MaxWidth(buttonWidth));
GUI.backgroundColor = oldColor;
return (T)EnumPopupInternal(selected, buttonWidth);
}

/// <summary>
/// Displays an enum of a known type.
/// </summary>
/// <param name="selected">The selected enum value.</param>
/// <param name="buttonWidth">The button width.</param>
/// <returns>The newly selected enum value.</returns>
private static Enum EnumPopupInternal(Enum selected, float buttonWidth = VALUE_WIDTH)
{
Type enumType = selected.GetType();
string[] enumNames = Enum.GetNames(enumType);

// Enums can be bit flags. If this is the case, we need to support toggling the bits
if (enumType.CustomAttributes.Select(a => a.AttributeType).Contains(typeof(FlagsAttribute)))
{
bool IsFlagSet<T>(T value, T flag)
{
long lValue = Convert.ToInt64(value);
long lFlag = Convert.ToInt64(flag);
return (lValue & lFlag) != 0;
};

T SetFlags<T>(T value, T flags, bool toggle)
{
long lValue = Convert.ToInt64(value);
long lFlag = Convert.ToInt64(flags);
if (toggle)
{
lValue |= lFlag;
}
else
{
lValue &= (~lFlag);
}

if (lFlag == 0)
{
lValue = 0;
}

return (T)Enum.ToObject(typeof(T), lValue);
};

Enum[] enumValues = Enum.GetValues(enumType).Cast<Enum>().ToArray();

using (new GUILayout.VerticalScope())
{
for (int i = 0; i < enumValues.Length; i++)
{
Enum enumValue = enumValues[i];
string enumName = enumNames[i];

bool isFlagSet = IsFlagSet(selected, enumValue);

selected = SetFlags(selected, enumValue, GUILayout.Toggle(isFlagSet, enumName, "Button", GUILayout.Width(buttonWidth)));
}
}

}
else
{
// Normal enum, only picks one value
int selectedIndex = Array.IndexOf(enumNames, selected.ToString());
selectedIndex = GUILayout.SelectionGrid(selectedIndex, enumNames, 1, GUILayout.Width(buttonWidth));
return (Enum)Enum.Parse(enumType, enumNames[selectedIndex]);
}
return selected;
}

Expand Down
109 changes: 109 additions & 0 deletions NitroxClient/Debuggers/Drawer/Unity/RigidbodyDrawer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using UnityEngine;

namespace NitroxClient.Debuggers.Drawer.Unity
{
/// <summary>
/// Draws a Rigidbody component on the gameobjects in the <see cref="SceneDebugger"/>
/// </summary>
public class RigidbodyDrawer : IDrawer
{
private const float LABEL_WIDTH = 120;
private const float VALUE_MAX_WIDTH = 405;

public Type[] ApplicableTypes => new[] { typeof(Rigidbody) };

public void Draw(object target)
{
Rigidbody rb = (Rigidbody)target;

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Mass", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
rb.mass = NitroxGUILayout.FloatField(rb.mass, VALUE_MAX_WIDTH);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Drag", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
rb.drag = NitroxGUILayout.FloatField(rb.drag, VALUE_MAX_WIDTH);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Angular Drag", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
rb.angularDrag = NitroxGUILayout.FloatField(rb.angularDrag, VALUE_MAX_WIDTH);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Use Gravity");
NitroxGUILayout.Separator();
rb.useGravity = NitroxGUILayout.BoolField(rb.useGravity);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Is Kinematic");
NitroxGUILayout.Separator();
rb.isKinematic = NitroxGUILayout.BoolField(rb.isKinematic);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Interpolate", GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
rb.interpolation = NitroxGUILayout.EnumPopup(rb.interpolation, NitroxGUILayout.VALUE_WIDTH);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Collision Detection", GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
rb.collisionDetectionMode = NitroxGUILayout.EnumPopup(rb.collisionDetectionMode, NitroxGUILayout.VALUE_WIDTH);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Freeze Rotation");
NitroxGUILayout.Separator();
rb.freezeRotation = NitroxGUILayout.BoolField(rb.freezeRotation);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Velocity", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
VectorDrawer.DrawVector3(rb.velocity, VALUE_MAX_WIDTH);
}

GUILayout.Space(10);

using (new GUILayout.HorizontalScope())
{
GUILayout.Label("Angular Velocity", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
NitroxGUILayout.Separator();
VectorDrawer.DrawVector3(rb.angularVelocity, VALUE_MAX_WIDTH);
}
}
}
}
63 changes: 57 additions & 6 deletions NitroxClient/Debuggers/SceneDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class SceneDebugger : BaseDebugger
private readonly Dictionary<int, bool> componentsVisibilityByID = new();
private readonly Dictionary<int, FieldInfo[]> cachedFieldsByComponentID = new();
private readonly Dictionary<int, MethodInfo[]> cachedMethodsByComponentID = new();
private readonly Dictionary<int, IDictionary<Type, bool>> enumVisibilityByComponentIDAndEnumType = new();

public SceneDebugger(IEnumerable<IDrawer> drawers, IEnumerable<IStructDrawer> structDrawers) : base(650, null, KeyCode.S, true, false, false, GUISkinCreationOptions.DERIVEDCOPY)
{
Expand Down Expand Up @@ -339,27 +340,29 @@ private void DrawFields(UnityEngine.Object target)
}
else
{
GUILayout.FlexibleSpace();

switch (fieldValue)
{
case null:
GUILayout.Box("Field is null");
GUILayout.Box("Field is null", GUILayout.Width(NitroxGUILayout.VALUE_WIDTH));
break;
case ScriptableObject scriptableObject:
if (GUILayout.Button(field.Name))
if (GUILayout.Button(field.Name, GUILayout.Width(NitroxGUILayout.VALUE_WIDTH)))
{
DrawFields(scriptableObject);
}

break;
case GameObject gameObject:
if (GUILayout.Button(field.Name))
if (GUILayout.Button(field.Name, GUILayout.Width(NitroxGUILayout.VALUE_WIDTH)))
{
UpdateSelectedObject(gameObject);
}

break;
case bool boolValue:
if (GUILayout.Button(boolValue.ToString()))
if (GUILayout.Button(boolValue.ToString(), GUILayout.Width(NitroxGUILayout.VALUE_WIDTH)))
{
field.SetValue(target, !boolValue);
}
Expand All @@ -376,17 +379,65 @@ private void DrawFields(UnityEngine.Object target)
field.SetValue(target, NitroxGUILayout.ConvertibleField((IConvertible)fieldValue));
break;
case Enum enumValue:
NitroxGUILayout.EnumPopup(enumValue);
DrawEnum(target, field, enumValue);
break;
default:
GUILayout.TextArea(fieldValue.ToString(), "options");
GUILayout.TextArea(fieldValue.ToString(), "options", GUILayout.Width(NitroxGUILayout.VALUE_WIDTH));
break;
}
}
}
}
}

/// <summary>
/// Draws an enum field on a component.
/// </summary>
/// <param name="target">The target containing the field.</param>
/// <param name="field">The enum field</param>
/// <param name="enumValue">The selected enum value.</param>
private void DrawEnum(UnityEngine.Object target, FieldInfo field, Enum enumValue)
{
// This is the first time enountering this target type
if (!enumVisibilityByComponentIDAndEnumType.TryGetValue(target.GetInstanceID(), out IDictionary<Type, bool> enumVisibilityByType))
{
// Add an empty subdictionary, it will be handled by the statement below.
enumVisibilityByType = new Dictionary<Type, bool>();
enumVisibilityByComponentIDAndEnumType.Add(target.GetInstanceID(), enumVisibilityByType);
}

// This is the first time we are encountering this enum type on this component
if (!enumVisibilityByType.TryGetValue(field.FieldType, out _))
{
enumVisibilityByType.Add(field.FieldType, false);
}

using (new GUILayout.VerticalScope())
{
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();

// Toggle the visibility and save it in the subdictionary
if (GUILayout.Button("Show / Hide", GUILayout.Width(NitroxGUILayout.VALUE_WIDTH)))
{
enumVisibilityByType[field.FieldType] = !enumVisibilityByType[field.FieldType];
}
}

// Show the enum list.
if (enumVisibilityByType[field.FieldType])
{
GUILayout.Space(5);
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
field.SetValue(target, NitroxGUILayout.EnumPopup(enumValue, 250));
}
}
}
}

private void DrawMonoBehaviourMethods(MonoBehaviour monoBehaviour)
{
using (new GUILayout.HorizontalScope("box"))
Expand Down
Loading

0 comments on commit 02789b6

Please sign in to comment.