Added a "bones panel" in the animator to access and control the bones of a character.

This commit is contained in:
originalnicodr committed 2024-06-18 09:41:07 -03:00
1 parent 171ee3f0d8
commit d9ebe5819c
5 files changed
+421 -3

No files matched your search

+6
View File
@@ -25,6 +25,7 @@ namespace UnityExplorer.UI.Panels
ButtonRef playButton;
public Dropdown animatorDropdown;
ButtonRef favAnimation;
ButtonRef openBonesPanelButton;
// ICell
public float DefaultHeight => 25f;
@@ -155,6 +156,11 @@ namespace UnityExplorer.UI.Panels
UIFactory.SetLayoutElement(playButton.Component.gameObject, minHeight: 25, minWidth: 90);
playButton.OnClick += PlayButton_OnClick;
openBonesPanelButton = UIFactory.CreateButton(UIRoot, "OpenBonesPanelButton", "Open bones panel");
UIFactory.SetLayoutElement(openBonesPanelButton.Component.gameObject, minWidth: 125, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
openBonesPanelButton.OnClick += () => { animatorPlayer.OpenBonesPanel(); };
return UIRoot;
}
+18
View File
@@ -20,6 +20,9 @@ namespace UnityExplorer.UI.Panels
public List<IAnimationClip> animations = new List<IAnimationClip>();
public bool shouldIgnoreMasterToggle = false;
BonesManager bonesManager;
private List<Transform> bones = new List<Transform>();
public IAnimationClip overridingAnimation;
private IAnimationClip lastCurrentAnimation;
private IRuntimeAnimatorController originalAnimatorController;
@@ -54,6 +57,7 @@ namespace UnityExplorer.UI.Panels
}
public void ResetAnimation(){
if (bonesManager != null) bonesManager.turnOffAnimatorToggle.isOn = true;
// Let the game change animations again
animator.StopPlayback();
if (originalAnimatorController != null && animator.wrappedObject != null){
@@ -115,6 +119,20 @@ namespace UnityExplorer.UI.Panels
animator.enabled = value;
}
}
private void FindBones(){
GameObject parentObj = animator.wrappedObject.gameObject;
SkinnedMeshRenderer skeleton = parentObj.GetComponentInChildren<SkinnedMeshRenderer>();
bones = new List<Transform>(skeleton.bones);
}
public void OpenBonesPanel(){
if (!bones.Any()) FindBones();
if (bonesManager == null){
bonesManager = new BonesManager(UIManager.GetPanel<UnityExplorer.UI.Panels.AnimatorPanel>(UIManager.Panels.AnimatorPanel).Owner, bones, animator);
}
bonesManager.SetActive(true);
}
}
public class IAnimator
+311
View File
@@ -0,0 +1,311 @@
using UnityExplorer.UI.Widgets;
using UniverseLib.Input;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.UI.Panels
{
public class BonesCell : ICell
{
public Transform bone;
public TransformControls transformControls;
Text boneName;
ComponentControl positionControl;
ComponentControl rotationControl;
ComponentControl scaleControl;
public AxisComponentControl CurrentSlidingAxisControl { get; set; }
// ICell
public float DefaultHeight => 25f;
public GameObject UIRoot { get; set; }
public RectTransform Rect { get; set; }
public bool Enabled => UIRoot.activeSelf;
public void Enable() => UIRoot.SetActive(true);
public void Disable() => UIRoot.SetActive(false);
public void SetBone(Transform bone){
this.bone = bone;
boneName.text = bone.name;
}
public virtual GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject("BoneCell", parent, new Vector2(25, 25));
Rect = UIRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, false, false, true, true, 3);
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, minWidth: 50, flexibleWidth: 9999);
boneName = UIFactory.CreateLabel(UIRoot, $"BoneLabel", "");
UIFactory.SetLayoutElement(boneName.gameObject, minWidth: 100, minHeight: 25);
positionControl = ComponentControl.Create(this, UIRoot, "Local Position", TransformType.LocalPosition);
rotationControl = ComponentControl.Create(this, UIRoot, "Rotation", TransformType.Rotation);
scaleControl = ComponentControl.Create(this, UIRoot, "Scale", TransformType.Scale);
return UIRoot;
}
// TransformControls-like functions
public void UpdateTransformControlValues(bool force){
positionControl.Update(force);
rotationControl.Update(force);
scaleControl.Update(force);
}
public void UpdateVectorSlider()
{
AxisComponentControl control = CurrentSlidingAxisControl;
if (control == null)
return;
if (!IInputManager.GetMouseButton(0))
{
control.slider.value = 0f;
control = null;
return;
}
AxisControlOperation(control.slider.value, control.parent, control.axis);
}
public void AxisControlOperation(float value, ComponentControl parent, int axis)
{
Transform transform = bone;
Vector3 vector = parent.Type switch
{
TransformType.Position => transform.position,
TransformType.LocalPosition => transform.localPosition,
TransformType.Rotation => transform.localEulerAngles,
TransformType.Scale => transform.localScale,
_ => throw new NotImplementedException()
};
// apply vector value change
switch (axis)
{
case 0:
vector.x += value; break;
case 1:
vector.y += value; break;
case 2:
vector.z += value; break;
}
// set vector back to transform
switch (parent.Type)
{
case TransformType.Position:
transform.position = vector; break;
case TransformType.LocalPosition:
transform.localPosition = vector; break;
case TransformType.Rotation:
transform.localEulerAngles = vector; break;
case TransformType.Scale:
transform.localScale = vector; break;
}
UpdateTransformControlValues(false);
}
}
// Duplication of Vector3Control class
public class ComponentControl
{
public BonesCell Owner { get; }
public Transform Transform => Owner.bone;
public TransformType Type { get; }
public InputFieldRef MainInput { get; }
public AxisComponentControl[] axisComponentControl { get; } = new AxisComponentControl[3];
public InputFieldRef IncrementInput { get; set; }
public float Increment { get; set; } = 0.1f;
Vector3 lastValue;
Vector3 CurrentValue => Type switch
{
TransformType.Position => Transform.position,
TransformType.LocalPosition => Transform.localPosition,
TransformType.Rotation => Transform.localEulerAngles,
TransformType.Scale => Transform.localScale,
_ => throw new NotImplementedException()
};
public ComponentControl(BonesCell cell, TransformType type, InputFieldRef input)
{
this.Owner = cell;
this.Type = type;
this.MainInput = input;
}
public void Update(bool force)
{
// Probably not needed
if (Transform == null) return;
Vector3 currValue = CurrentValue;
if (force || (!MainInput.Component.isFocused && !lastValue.Equals(currValue)))
{
MainInput.Text = ParseUtility.ToStringForInput<Vector3>(currValue);
lastValue = currValue;
}
}
void OnTransformInputEndEdit(TransformType type, string input)
{
switch (type)
{
case TransformType.Position:
{
if (ParseUtility.TryParse(input, out Vector3 val, out _))
Transform.position = val;
}
break;
case TransformType.LocalPosition:
{
if (ParseUtility.TryParse(input, out Vector3 val, out _))
Transform.localPosition = val;
}
break;
case TransformType.Rotation:
{
if (ParseUtility.TryParse(input, out Vector3 val, out _))
Transform.localEulerAngles = val;
}
break;
case TransformType.Scale:
{
if (ParseUtility.TryParse(input, out Vector3 val, out _))
Transform.localScale = val;
}
break;
}
Owner.UpdateTransformControlValues(true);
}
void IncrementInput_OnEndEdit(string value)
{
if (!ParseUtility.TryParse(value, out float increment, out _))
IncrementInput.Text = ParseUtility.ToStringForInput<float>(Increment);
else
{
Increment = increment;
foreach (AxisComponentControl slider in axisComponentControl)
{
slider.slider.minValue = -increment;
slider.slider.maxValue = increment;
}
}
}
public static ComponentControl Create(BonesCell cell, GameObject transformGroup, string title, TransformType type)
{
GameObject rowObj = UIFactory.CreateUIObject($"Row_{title}", transformGroup);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rowObj, false, false, true, true, 5, 0, 0, 0, 0, default);
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 9999);
Text titleLabel = UIFactory.CreateLabel(rowObj, "PositionLabel", title, TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(titleLabel.gameObject, minHeight: 25, minWidth: 110);
InputFieldRef inputField = UIFactory.CreateInputField(rowObj, "InputField", "...");
UIFactory.SetLayoutElement(inputField.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 999);
ComponentControl control = new(cell, type, inputField);
inputField.Component.GetOnEndEdit().AddListener((string value) => { control.OnTransformInputEndEdit(type, value); });
control.axisComponentControl[0] = AxisComponentControl.Create(rowObj, "X", 0, control);
control.axisComponentControl[1] = AxisComponentControl.Create(rowObj, "Y", 1, control);
control.axisComponentControl[2] = AxisComponentControl.Create(rowObj, "Z", 2, control);
control.IncrementInput = UIFactory.CreateInputField(rowObj, "IncrementInput", "...");
control.IncrementInput.Text = "0.1";
UIFactory.SetLayoutElement(control.IncrementInput.GameObject, minWidth: 30, flexibleWidth: 0, minHeight: 25);
control.IncrementInput.Component.GetOnEndEdit().AddListener(control.IncrementInput_OnEndEdit);
if (type == TransformType.Scale){
GameObject extraRowObj = UIFactory.CreateUIObject($"Row_UniformScale", transformGroup);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(extraRowObj, false, false, true, true, 5, 0, 0, 0, 0, default);
UIFactory.SetLayoutElement(extraRowObj, minHeight: 25, flexibleWidth: 9999);
Text uniformScaleTitleLabel = UIFactory.CreateLabel(extraRowObj, "UniformScaleLabel", "Uniform Scale:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(uniformScaleTitleLabel.gameObject, minHeight: 25, minWidth: 110);
GameObject uniformScaleControlObj = UIFactory.CreateSlider(extraRowObj, "UniformScaleSlider", out Slider uniformScaleControl);
UIFactory.SetLayoutElement(uniformScaleControlObj, minHeight: 25, minWidth: 200, flexibleHeight: 0);
uniformScaleControl.minValue = 0.0001f;
uniformScaleControl.maxValue = 10;
uniformScaleControl.value = 1;
uniformScaleControl.onValueChanged.AddListener((float val) => { cell.bone.localScale = new Vector3(val, val, val); });
}
return control;
}
}
// // Duplication of AxisControl class
public class AxisComponentControl
{
public readonly ComponentControl parent;
public readonly int axis;
public readonly Slider slider;
public AxisComponentControl(int axis, Slider slider, ComponentControl parentControl)
{
this.parent = parentControl;
this.axis = axis;
this.slider = slider;
}
void OnVectorSliderChanged(float value)
{
parent.Owner.CurrentSlidingAxisControl = value == 0f ? null : this;
}
void OnVectorMinusClicked()
{
parent.Owner.AxisControlOperation(-this.parent.Increment, this.parent, this.axis);
}
void OnVectorPlusClicked()
{
parent.Owner.AxisControlOperation(this.parent.Increment, this.parent, this.axis);
}
public static AxisComponentControl Create(GameObject parent, string title, int axis, ComponentControl owner)
{
Text label = UIFactory.CreateLabel(parent, $"Label_{title}", $"{title}:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 30);
GameObject sliderObj = UIFactory.CreateSlider(parent, $"Slider_{title}", out Slider slider);
UIFactory.SetLayoutElement(sliderObj, minHeight: 25, minWidth: 75, flexibleWidth: 0);
slider.m_FillImage.color = Color.clear;
slider.minValue = -0.1f;
slider.maxValue = 0.1f;
AxisComponentControl sliderControl = new(axis, slider, owner);
slider.onValueChanged.AddListener(sliderControl.OnVectorSliderChanged);
ButtonRef minusButton = UIFactory.CreateButton(parent, "MinusIncrementButton", "-");
UIFactory.SetLayoutElement(minusButton.GameObject, minWidth: 20, flexibleWidth: 0, minHeight: 25);
minusButton.OnClick += sliderControl.OnVectorMinusClicked;
ButtonRef plusButton = UIFactory.CreateButton(parent, "PlusIncrementButton", "+");
UIFactory.SetLayoutElement(plusButton.GameObject, minWidth: 20, flexibleWidth: 0, minHeight: 25);
plusButton.OnClick += sliderControl.OnVectorPlusClicked;
return sliderControl;
}
}
}
+1 -3
View File
@@ -23,7 +23,7 @@ namespace UnityExplorer.UI.Panels
public override string Name => "Animator";
public override UIManager.Panels PanelType => UIManager.Panels.AnimatorPanel;
public override int MinWidth => 900;
public override int MinWidth => 1050;
public override int MinHeight => 200;
public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f);
public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f);
@@ -47,8 +47,6 @@ namespace UnityExplorer.UI.Panels
animatorScrollPool.Initialize(this);
DoneScrollPoolInit = true;
}
animatorScrollPool.Refresh(true, false);
}
private void FindAllAnimators(){
+85
View File
@@ -0,0 +1,85 @@
using UniverseLib.UI;
using UniverseLib.UI.Panels;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.UI.Panels
{
public class BonesManager : PanelBase, ICellPoolDataSource<BonesCell>
{
public override string Name => $"Bones Manager";
public override int MinWidth => 1000;
public override int MinHeight => 400;
public override Vector2 DefaultAnchorMin => Vector2.zero;
public override Vector2 DefaultAnchorMax => Vector2.zero;
public Toggle turnOffAnimatorToggle;
private IAnimator animator;
private Text skeletonName;
private List<Transform> bones = new List<Transform>();
private ScrollPool<BonesCell> boneScrollPool;
public int ItemCount => bones.Count;
private bool DoneScrollPoolInit;
public BonesManager(UIBase owner, List<Transform> bones, IAnimator animator) : base(owner)
{
this.bones = bones;
this.animator = animator;
skeletonName.text = $"Skeleton: {animator?.name}";
}
public override void SetActive(bool active)
{
base.SetActive(active);
if (active && !DoneScrollPoolInit)
{
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
boneScrollPool.Initialize(this);
DoneScrollPoolInit = true;
}
}
protected override void ConstructPanelContent()
{
skeletonName = UIFactory.CreateLabel(ContentRoot, $"SkeletonName", "");
UIFactory.SetLayoutElement(skeletonName.gameObject, minWidth: 100, minHeight: 25);
skeletonName.fontSize = 16;
GameObject turnOffAnimatorToggleObj = UIFactory.CreateToggle(ContentRoot, "Animator toggle", out turnOffAnimatorToggle, out Text turnOffAnimatorToggleText);
UIFactory.SetLayoutElement(turnOffAnimatorToggleObj, minHeight: 25, flexibleWidth: 9999);
turnOffAnimatorToggle.onValueChanged.AddListener(value => { animator.enabled = value; });
turnOffAnimatorToggleText.text = "Toggle animator (needs to be off to move bones)";
boneScrollPool = UIFactory.CreateScrollPool<BonesCell>(ContentRoot, "BonesList", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.06f, 0.06f, 0.06f));
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
}
public void SetCell(BonesCell cell, int index)
{
if (index >= bones.Count)
{
cell.Disable();
return;
}
Transform bone = bones[index];
cell.SetBone(bone);
cell.UpdateTransformControlValues(true);
}
public void OnCellBorrowed(BonesCell cell) {
cell.UpdateVectorSlider();
}
public override void Update()
{
base.Update();
foreach(BonesCell boneCell in boneScrollPool.CellPool) {
boneCell.UpdateVectorSlider();
}
}
}
}