Added support to manually play animations of entities on the scene (only for Mono builds). Also refactored the AnimatorPanel with a pool of objects.

This commit is contained in:
originalnicodr committed 2024-02-13 00:48:22 -03:00
1 parent 4f91fdfa66
commit 59ffa462cc
2 files changed
+229 -80

No files matched your search

+76 -80
View File
@@ -4,6 +4,7 @@ using UnityExplorer.Inspectors;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
using UniverseLib.UI.Widgets.ScrollView;
using UnityEngine;
#if UNHOLLOWER
using UnhollowerRuntimeLib;
@@ -12,9 +13,13 @@ using UnhollowerRuntimeLib;
using Il2CppInterop.Runtime.Injection;
#endif
#if CPP
using Animator = UnityEngine.Behaviour;
#endif
namespace UnityExplorer.UI.Panels
{
internal class AnimatorPanel : UEPanel
internal class AnimatorPanel : UEPanel, ICellPoolDataSource<AnimatorCell>
{
public AnimatorPanel(UIBase owner) : base(owner)
{
@@ -23,106 +28,68 @@ namespace UnityExplorer.UI.Panels
public override string Name => "Animator";
public override UIManager.Panels PanelType => UIManager.Panels.AnimatorPanel;
public override int MinWidth => 500;
public override int MinHeight => 500;
public override int MinHeight => 200;
public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f);
public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f);
public override bool NavButtonWanted => true;
public override bool ShouldSaveActiveState => true;
List<GameObject> UIElements = new List<GameObject>();
internal Dictionary<string,List<Behaviour>> animators = new Dictionary<string,List<Behaviour>>();
internal Dictionary<string,List<Toggle>> animatorToggles = new Dictionary<string,List<Toggle>>();
internal Dictionary<string,bool> ignoresMasterToggler = new Dictionary<string,bool>();
Dictionary<Animator, Func<bool>> animationEndedFunctions = new Dictionary<Animator, Func<bool>>();
Toggle masterAnimatorToggle = new Toggle();
private static ScrollPool<AnimatorCell> animatorScrollPool;
internal List<Animator> animators = new List<Animator>();
public int ItemCount => animators.Count;
private static bool DoneScrollPoolInit;
public override void SetActive(bool active)
{
base.SetActive(active);
if (active && !DoneScrollPoolInit)
{
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
animatorScrollPool.Initialize(this);
DoneScrollPoolInit = true;
}
animatorScrollPool.Refresh(true, false);
}
private void FindAllAnimators(){
// Enable all animators on refresh
if (animators.Count != 0) {
masterAnimatorToggle.isOn = true; // Will also trigger "MasterToggleAnimators(true)"
animators.Clear();
animatorToggles.Clear();
ignoresMasterToggler.Clear();
animationEndedFunctions.Clear();
#if MONO
foreach (AnimatorCell animatorCell in animatorScrollPool.CellPool)
{
animatorCell.ResetAnimation();
}
#endif
}
Type searchType = ReflectionUtility.GetTypeByName("UnityEngine.Animator");
searchType = searchType is Type type ? type : searchType.GetActualType();
List<Behaviour> animatorsList = RuntimeHelper.FindObjectsOfTypeAll(searchType).Select(obj => obj.TryCast<Behaviour>())
animators = RuntimeHelper.FindObjectsOfTypeAll(searchType).Select(obj => obj.TryCast<Animator>())
.Where(a => a.GetComponentsInChildren<SkinnedMeshRenderer>(false).Length != 0 && a.enabled && a.GetComponentsInChildren<Rigidbody>(false).Length != 0)
.OrderBy(x=>x.name)
.ToList();
foreach(Behaviour animator in animatorsList){
GameObject gObj = animator.gameObject;
string key = getNameWithoutClone(gObj.name);
if (!animators.ContainsKey(key)){
animators.Add(key, new List<Behaviour>());
}
animators[key].Add(animator);
}
BuildAnimatorsTogglers();
}
private string getNameWithoutClone(string name){
string newName = name;
while (newName.EndsWith("(Clone)")){
newName = newName.Substring(0, newName.Length - 7);
}
return newName;
}
private void BuildAnimatorsTogglers(){
foreach (var comp in UIElements){
UnityEngine.Object.Destroy(comp);
//UIElements.Remove(comp);
}
foreach (KeyValuePair<string, List<Behaviour>> entry in animators)
{
// A weird canse insensitive "Contains" to identify the player
ignoresMasterToggler[entry.Key] = entry.Key.IndexOf("play", 0, StringComparison.OrdinalIgnoreCase) >= 0;
GameObject horiGroup = UIFactory.CreateHorizontalGroup(ContentRoot, $"{entry.Key}_Group", false, false, true, false, 4,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleWidth: 9999);
UIElements.Add(horiGroup);
// is Affected by Master Toggler toggle
Toggle ignoresMasterTogglerToggle;
GameObject ignoresMasterTogglerObj = UIFactory.CreateToggle(horiGroup, $"Toggle {entry.Key}", out ignoresMasterTogglerToggle, out Text ignoresMasterTogglerText);
UIFactory.SetLayoutElement(ignoresMasterTogglerObj, minHeight: 25);
ignoresMasterTogglerToggle.isOn = ignoresMasterToggler[entry.Key];
ignoresMasterTogglerToggle.onValueChanged.AddListener(value => ignoresMasterToggler[entry.Key] = value);
ignoresMasterTogglerText.text = entry.Key;
foreach (Behaviour entityAnimator in entry.Value){
Toggle animatorToggle;
GameObject animatorObj = UIFactory.CreateToggle(horiGroup, $"Toggle {entry.Key}", out animatorToggle, out Text animatorToggleText);
if (!animatorToggles.ContainsKey(entry.Key)){
animatorToggles.Add(entry.Key, new List<Toggle>());
}
animatorToggles[entry.Key].Add(animatorToggle);
UIFactory.SetLayoutElement(animatorObj, minHeight: 25);
animatorToggle.isOn = true;
animatorToggle.onValueChanged.AddListener(value => entityAnimator.enabled = value);
}
}
animatorScrollPool.Refresh(true, false);
}
public void MasterToggleAnimators(bool enable){
// Load animators for the first time if there are not any
if (animators.Count == 0) FindAllAnimators();
foreach (KeyValuePair<string, List<Behaviour>> entry in animators)
foreach (AnimatorCell animatorCell in animatorScrollPool.CellPool)
{
if (!ignoresMasterToggler[entry.Key]){
foreach (Toggle animatorToggle in animatorToggles[entry.Key]){
animatorToggle.isOn = enable;
}
if (animatorCell.animator != null && !animatorCell.IgnoreMasterToggle.isOn){
animatorCell.AnimatorToggle.isOn = enable;
}
}
}
@@ -131,6 +98,13 @@ namespace UnityExplorer.UI.Panels
masterAnimatorToggle.isOn = !masterAnimatorToggle.isOn;
}
#if MONO
public override void Update(){
foreach (Animator animator in animators){
animationEndedFunctions[animator]();
}
}
#endif
// ~~~~~~~~ UI construction / callbacks ~~~~~~~~
protected override void ConstructPanelContent()
@@ -154,11 +128,33 @@ namespace UnityExplorer.UI.Panels
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999);
Text isPlayerLabel = UIFactory.CreateLabel(secondGroup, "IsAffectedByMasterToggler", "Ignores Master Toggler", TextAnchor.MiddleLeft, Color.white, false, 15);
UIFactory.SetLayoutElement(isPlayerLabel.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0);
Text enableLabel = UIFactory.CreateLabel(secondGroup, "EnableLabel", "Enable ", TextAnchor.MiddleRight, Color.white, false, 15);
UIFactory.SetLayoutElement(enableLabel.gameObject, minHeight: 25, minWidth: 75, flexibleWidth: 9999);
animatorScrollPool = UIFactory.CreateScrollPool<AnimatorCell>(ContentRoot, "AnimatorsList", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
}
public void SetCell(AnimatorCell cell, int index)
{
if (index >= animators.Count)
{
cell.Disable();
return;
}
Animator animator = animators[index];
if (animator == null)
return;
cell.animator = animator;
cell.name.text = animator.gameObject.name;
cell.MaybeSetIgnoreMasterToggleSet();
#if MONO
cell.DrawAnimatorPlayer();
animationEndedFunctions[animator] = cell.IsPlayingSelectedAnimation;
#endif
}
public void OnCellBorrowed(AnimatorCell cell) { }
}
}