1
0
mirror of https://github.com/originalnicodr/CinematicUnityExplorer.git synced 2025-07-18 17:38:01 +08:00

20 Commits
1.1.0 ... 1.2.0

Author SHA1 Message Date
48fec535fd When writing down a bigger timescale than the maximum move the slider to the right. 2024-07-21 18:44:13 -03:00
5e9abd33b4 Added setting to toggle menu UI autoscaling off, and allow the timescale input field to accept values above the slider maximum. 2024-07-18 09:41:49 -03:00
6ebf19dfa0 Update README.md 2024-07-18 08:57:44 -03:00
cc34a50e21 Revert "Add controller support (#76)"
This reverts commit b785f76ba3.
2024-07-18 08:57:22 -03:00
954b6f4f1c Updated readme 2024-06-30 16:55:05 -03:00
b785f76ba3 Add controller support (#76)
Added controller support.
2024-06-30 15:51:58 -03:00
d2d22de66c Fixed enabled in BonesPanel, refactored SetMeshesEnabled to not use reflection and to disable meshes consistently with the BonesManager. 2024-06-20 18:52:25 -03:00
af2ad057fb Added mesh togglers for each bone, added non-bone meshes to the BoneManager, captured multiple skinnedMeshRenderers, and restored the mesh scale when enabling the animator again. 2024-06-20 18:30:31 -03:00
ca726e0ae7 Added mesh toggle to the animator panel, and reworked the panel UI. 2024-06-20 00:01:29 -03:00
a02460be33 Added buttons to restore individual bones in the BonesManager. 2024-06-19 18:42:46 -03:00
30b9e6e387 Increased CUE version. 2024-06-18 21:36:16 -03:00
58f477ed0d Adjusted space between Animator cells. 2024-06-18 21:35:06 -03:00
fd672615be Added animation sliders to more precisely choose the animation frame in wich you want to set up a character. 2024-06-18 21:26:18 -03:00
f8886700d9 Initialized slider increments with values that make more sense for each one. 2024-06-18 19:49:26 -03:00
dae6930b43 Turn off the animator toggle on the bones manager when modifying one of the sliders or input fields. 2024-06-18 18:42:57 -03:00
d9ebe5819c Added a "bones panel" in the animator to access and control the bones of a character. 2024-06-18 09:41:07 -03:00
171ee3f0d8 Append new animations instead of discarding the previous ones when refreshing the animators. 2024-06-12 19:00:19 -03:00
448ab66f5b Merge branch 'master' of github.com:originalnicodr/UnityExplorer 2024-06-12 18:35:46 -03:00
b090f68732 Properly disable CinemachineBrain on freecam on IL2CPP games. 2024-06-12 18:35:39 -03:00
13dcef668f IGCSDOF: Go back to the original position after an EndSession. (#74)
* (igcsdof) remove unnecessary buffer.

* (igcsdof) fix: go back to the original positon.

* Move the check of EndSession to UnityIGCSConnector class
2024-05-19 23:14:50 -03:00
13 changed files with 661 additions and 48 deletions

View File

@ -204,10 +204,14 @@ Allows you to manually play characters and NPC animations in a scene. This shoul
Favorite animations so they appear first on the dropdown list by clicking on the star button with the animation selected.
The Animator Panel also allows you to freeze all characters in a scene all at once, alongside giving you control over which characters should ignore the master toggler. That way you can make the playable character avoid getting frozen, or avoid un-freezing NPCs or enemies that already have the animations you want them to have.
The Animator Panel also allows you to freeze all characters in a scene all at once, alongside giving you control over which characters should ignore the master toggler. Each animator also comes with a toggle to be able to hide a character meshes from a scene.
Alongside all of this, you can also open each character game object by clicking on their names, so you can move, rotate, scale them around, disable them, or further edit their properties and child objects.
### Bones Panel
For each animator, you can also spawn a Bones Panel. This panel will list all of the character's bones and meshes, and provide easy-to-access toggles to disable them and sliders to move them around, allowing you to pose a character to your liking.
## Misc Panel
- HUD toggle.
- Force high LODs toggle. This means that the highest models possible will be forced on all meshes.

View File

@ -1,6 +1,6 @@
{
"name": "com.originalnicodr.cinematicunityexplorer",
"version": "1.1.0",
"version": "1.2.0",
"displayName": "CinematicUnityExplorer",
"description": "UnityExplorer fork focused on providing tools for creating marketing material for Unity games.",
"unity": "2017.1",

View File

@ -21,13 +21,16 @@ namespace UnityExplorer.UI.Panels
public Toggle IgnoreMasterToggle;
public Toggle AnimatorToggle;
public Toggle MeshToggle;
public ButtonRef inspectButton;
ButtonRef playButton;
public Dropdown animatorDropdown;
ButtonRef favAnimation;
Slider animationTimeline;
ButtonRef playButton;
ButtonRef openBonesPanelButton;
// ICell
public float DefaultHeight => 25f;
public float DefaultHeight => 30f;
public GameObject UIRoot { get; set; }
public RectTransform Rect { get; set; }
@ -64,7 +67,7 @@ namespace UnityExplorer.UI.Panels
}
private void PlayButton_OnClick(){
animatorPlayer.PlayOverridingAnimation();
animatorPlayer.PlayOverridingAnimation(0);
// Needed for the new animation to play for some reason
EnableAnimation(false);
EnableAnimation(true);
@ -72,31 +75,36 @@ namespace UnityExplorer.UI.Panels
public virtual GameObject CreateContent(GameObject parent)
{
GameObject AnimatorToggleObj = UIFactory.CreateToggle(parent, $"AnimatorToggle", out AnimatorToggle, out Text animatorToggleText);
UIFactory.SetLayoutElement(AnimatorToggleObj, minHeight: 25);
AnimatorToggle.isOn = animatorPlayer != null && animatorPlayer.animator.speed == 1;
AnimatorToggle.onValueChanged.AddListener(EnableAnimation);
UIRoot = UIFactory.CreateUIObject("AnimatorCell", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 4, childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleWidth: 9999);
UIRoot = AnimatorToggleObj;
UIRoot.SetActive(false);
GameObject MeshToggleObj = UIFactory.CreateToggle(UIRoot, "MeshToggle", out MeshToggle, out Text MeshToggleText);
UIFactory.SetLayoutElement(MeshToggleObj, minHeight: 30);
MeshToggle.onValueChanged.AddListener(EnableMesh);
Rect = UIRoot.GetComponent<RectTransform>();
Rect.anchorMin = new Vector2(0, 1);
Rect.anchorMax = new Vector2(0, 1);
Rect.pivot = new Vector2(0.5f, 1);
Rect.sizeDelta = new Vector2(25, 25);
Rect.sizeDelta = new Vector2(30, 30);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 0);
inspectButton = UIFactory.CreateButton(UIRoot, "InspectButton", "");
UIFactory.SetLayoutElement(inspectButton.GameObject, minWidth: 200, minHeight: 25);
inspectButton.OnClick += () => InspectorManager.Inspect(animatorPlayer.animator.gameObject);
GameObject AnimatorToggleObj = UIFactory.CreateToggle(UIRoot, "AnimatorToggle", out AnimatorToggle, out Text animatorToggleText);
UIFactory.SetLayoutElement(AnimatorToggleObj, minHeight: 30);
AnimatorToggle.isOn = animatorPlayer != null && animatorPlayer.animator.speed == 1;
AnimatorToggle.onValueChanged.AddListener(EnableAnimation);
ButtonRef resetAnimation = UIFactory.CreateButton(UIRoot, "Reset Animation", "Reset");
UIFactory.SetLayoutElement(resetAnimation.GameObject, minWidth: 50, minHeight: 25);
resetAnimation.OnClick += ResetAnimation;
GameObject ignoresMasterTogglerObj = UIFactory.CreateToggle(UIRoot, $"AnimatorIgnoreMasterToggle", out IgnoreMasterToggle, out Text ignoreMasterToggleText);
GameObject ignoresMasterTogglerObj = UIFactory.CreateToggle(UIRoot, "AnimatorIgnoreMasterToggle", out IgnoreMasterToggle, out Text ignoreMasterToggleText);
UIFactory.SetLayoutElement(ignoresMasterTogglerObj, minHeight: 25, minWidth: 155);
IgnoreMasterToggle.isOn = false;
IgnoreMasterToggle.onValueChanged.AddListener(IgnoreMasterToggle_Clicked);
@ -112,7 +120,7 @@ namespace UnityExplorer.UI.Panels
favAnimation.ButtonText.text = animatorPlayer.IsAnimationFaved(animatorPlayer.overridingAnimation) ? "★" : "☆";
};
GameObject overridingAnimationObj = UIFactory.CreateDropdown(UIRoot, $"Animations_Dropdown", out animatorDropdown, null, 14, (idx) => {
GameObject overridingAnimationObj = UIFactory.CreateDropdown(UIRoot, "Animations_Dropdown", out animatorDropdown, null, 14, (idx) => {
if (animatorPlayer.animator.wrappedObject == null)
return;
animatorPlayer.overridingAnimation = idx < animatorDropdown.options.Count ? animatorPlayer.animations.Find(a => a.name == animatorDropdown.options[idx].text) : animatorPlayer.overridingAnimation;
@ -151,10 +159,24 @@ namespace UnityExplorer.UI.Panels
//UpdateDropdownOptions();
};
GameObject animationTimelineObj = UIFactory.CreateSlider(UIRoot, "AnimationTimelineSlider", out animationTimeline);
UIFactory.SetLayoutElement(animationTimelineObj, minHeight: 25, minWidth: 200, flexibleHeight: 0);
animationTimeline.minValue = 0;
animationTimeline.maxValue = 1;
animationTimeline.onValueChanged.AddListener((float val) => {
animatorPlayer.PlayOverridingAnimation(val);
AnimatorToggle.isOn = false;
});
playButton = UIFactory.CreateButton(UIRoot, "PlayButton", "Play", new Color(0.2f, 0.26f, 0.2f));
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: 150, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
openBonesPanelButton.OnClick += () => { animatorPlayer.OpenBonesPanel(); };
return UIRoot;
}
@ -170,5 +192,9 @@ namespace UnityExplorer.UI.Panels
if (animatorPlayer.animator.wrappedObject != null)
animatorPlayer.animator.speed = value ? 1 : 0;
}
internal void EnableMesh(bool value){
animatorPlayer.SetMeshesEnabled(value);
}
}
}

View File

@ -20,6 +20,11 @@ namespace UnityExplorer.UI.Panels
public List<IAnimationClip> animations = new List<IAnimationClip>();
public bool shouldIgnoreMasterToggle = false;
BonesManager bonesManager;
private List<Transform> bones = new List<Transform>();
private List<SkinnedMeshRenderer> skinnedMeshes = new();
private List<MeshRenderer> extraMeshes = new();
public IAnimationClip overridingAnimation;
private IAnimationClip lastCurrentAnimation;
private IRuntimeAnimatorController originalAnimatorController;
@ -42,6 +47,9 @@ namespace UnityExplorer.UI.Panels
this.overridingAnimation = lastCurrentAnimation != null ? lastCurrentAnimation : (animations.Count > 0 ? animations[0] : null);
this.favAnimations = new List<IAnimationClip>();
this.skinnedMeshes.AddRange(this.animator.wrappedObject.gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(false));
this.extraMeshes.AddRange(this.animator.wrappedObject.gameObject.GetComponentsInChildren<MeshRenderer>(false));
}
// Include the animations being played in other layers
@ -54,6 +62,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){
@ -65,7 +74,7 @@ namespace UnityExplorer.UI.Panels
}
}
public void PlayOverridingAnimation(){
public void PlayOverridingAnimation(float normalizedTime){
ResetAnimation();
if (animatorOverrideController == null){
animatorOverrideController = new IAnimatorOverrideController();
@ -84,7 +93,7 @@ namespace UnityExplorer.UI.Panels
IAnimationClip currentAnimation = animator.GetCurrentAnimatorClipInfo(0)[0].clip;
animatorOverrideController[currentAnimation] = overridingAnimation;
animator.Play(currentAnimation.name);
animator.Play(currentAnimation.name, normalizedTime);
lastCurrentAnimation = currentAnimation;
}
@ -115,6 +124,35 @@ namespace UnityExplorer.UI.Panels
animator.enabled = value;
}
}
private List<Transform> GetMeshes(){
List<Transform> meshes = new List<Transform>();
foreach (SkinnedMeshRenderer skinnedMesh in skinnedMeshes) {
meshes.AddRange(skinnedMesh.bones);
}
meshes.AddRange(extraMeshes.Select(m => m.transform));
return meshes.GroupBy(b => b.name).Select(b => b.First()).ToList().OrderBy(b => b.name).ToList();
}
public void OpenBonesPanel(){
if (skinnedMeshes.Count == 0 && extraMeshes.Count == 0) return;
if (bonesManager == null){
bonesManager = new BonesManager(UIManager.GetPanel<UnityExplorer.UI.Panels.AnimatorPanel>(UIManager.Panels.AnimatorPanel).Owner, GetMeshes(), animator);
}
bonesManager.SetActive(true);
}
public void SetMeshesEnabled(bool value){
foreach (SkinnedMeshRenderer skinnedMesh in skinnedMeshes) {
skinnedMesh.TryCast<Renderer>().enabled = value;
}
foreach (MeshRenderer meshRenderer in extraMeshes) {
meshRenderer.gameObject.SetActive(value);
}
}
}
public class IAnimator
@ -209,9 +247,9 @@ namespace UnityExplorer.UI.Panels
}
}
public void Play(string animatorClip){
public void Play(string animatorClip, float normalizedTime){
MethodInfo play = realType.GetMethod("Play", new Type[] {typeof(string), typeof(int), typeof(float)});
play.Invoke(_animator.TryCast(), new object[] {animatorClip, -1, 0f});
play.Invoke(_animator.TryCast(), new object[] {animatorClip, -1, normalizedTime});
}
public float speed

View File

@ -0,0 +1,342 @@
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;
ComponentControl positionControl;
ComponentControl rotationControl;
ComponentControl scaleControl;
public AxisComponentControl CurrentSlidingAxisControl { get; set; }
public BonesManager Owner;
private ButtonRef inspectButton;
// 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, BonesManager bonesManager){
this.bone = bone;
inspectButton.ButtonText.text = bone.name;
Owner = bonesManager;
}
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);
GameObject header = UIFactory.CreateUIObject("BoneHeader", UIRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(header, false, false, true, true, 4, childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(header, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800);
GameObject MeshToggleObj = UIFactory.CreateToggle(header, "MeshToggle", out Toggle MeshToggle, out Text MeshToggleText);
UIFactory.SetLayoutElement(MeshToggleObj, minHeight: 30);
MeshToggle.onValueChanged.AddListener(SetBoneEnabled);
inspectButton = UIFactory.CreateButton(header, "InspectButton", "");
UIFactory.SetLayoutElement(inspectButton.GameObject, minWidth: 150, minHeight: 25);
inspectButton.OnClick += () => InspectorManager.Inspect(bone.gameObject);
GameObject headerButtons = UIFactory.CreateUIObject("BoneHeader", header);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(headerButtons, false, false, true, true, 4, childAlignment: TextAnchor.MiddleRight);
UIFactory.SetLayoutElement(headerButtons, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800);
ButtonRef restoreBoneStateButton = UIFactory.CreateButton(headerButtons, "RestoreBoneState", "Restore State");
UIFactory.SetLayoutElement(restoreBoneStateButton.GameObject, minWidth: 125, minHeight: 25);
restoreBoneStateButton.OnClick += RestoreBoneState;
positionControl = ComponentControl.Create(this, UIRoot, "Local Position", TransformType.LocalPosition, 0.01f);
rotationControl = ComponentControl.Create(this, UIRoot, "Rotation", TransformType.Rotation, 10f);
scaleControl = ComponentControl.Create(this, UIRoot, "Scale", TransformType.Scale, 0.1f);
return UIRoot;
}
private void RestoreBoneState(){
Owner.RestoreBoneState(bone.name);
}
private void SetBoneEnabled(bool value){
bone.gameObject.SetActive(value);
}
// 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);
if (Owner.turnOffAnimatorToggle.isOn) Owner.turnOffAnimatorToggle.isOn = false;
}
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);
if (Owner.Owner.turnOffAnimatorToggle.isOn) Owner.Owner.turnOffAnimatorToggle.isOn = false;
}
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, float increment)
{
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.Increment = increment;
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 = increment.ToString();
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 = -owner.Increment;
slider.maxValue = owner.Increment;
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;
}
}
}

View File

@ -29,6 +29,9 @@ namespace CinematicUnityExplorer.Cinematic
// Store the initial position when a session start in IGCSDof.
Mono.CSharp.Tuple<Vector3, Quaternion> position = null;
// When we end a session, we need to make sure to go back to the position when the session ends.
private Mono.CSharp.Tuple<Vector3, Quaternion> endSessionPosition = null;
private readonly bool isValid = false;
private bool _isActive = false;
public bool IsActive => isValid && _isActive;
@ -42,21 +45,21 @@ namespace CinematicUnityExplorer.Cinematic
private readonly Queue<StepCommand> commands = new();
private IntPtr CameraStatus = IntPtr.Zero;
// In order to avoid allocations on every Update call, we create this buffer to allocate once
// and copy from here the CameraStatus (because Marshal.Copy requires a buffer, urgh).
private readonly byte[] CameraStatusBuffer = new byte[] { 0x0 };
public void UpdateFreecamStatus(bool enabled)
{
if (CameraStatus == IntPtr.Zero) return;
CameraStatusBuffer[0] = enabled ? (byte)0x1 : (byte)0x0;
Marshal.Copy(CameraStatusBuffer, 0, CameraStatus, 1);
Marshal.WriteByte(CameraStatus, enabled ? (byte)0x1 : (byte)0x0);
}
public void ExecuteCameraCommand(Camera cam)
{
var transform = cam.transform;
// Check whether we should go back to the original position despite being active or not
this.ShouldMoveToOriginalPosition(transform);
if (!_isActive || position == null)
{
position = new(transform.position, transform.rotation);
@ -89,8 +92,23 @@ namespace CinematicUnityExplorer.Cinematic
_isActive = true;
}
// At the EndSession, since we have a queue system, we have to have a special check when the session ends and
// then move the camera back to the original position, because the queue gets cleaned as soon as the session
// ends.
public void ShouldMoveToOriginalPosition(Transform transform)
{
if (!isValid) return;
if (endSessionPosition == null) return;
transform.position = endSessionPosition.Item1;
transform.rotation = endSessionPosition.Item2;
endSessionPosition = null;
}
private void EndSession()
{
endSessionPosition = position;
position = null;
_isActive = false;

View File

@ -29,6 +29,7 @@ namespace UnityExplorer.Config
public static ConfigElement<string> CSConsole_Assembly_Blacklist;
public static ConfigElement<string> Reflection_Signature_Blacklist;
public static ConfigElement<bool> Reflection_Hide_NativeInfoPtrs;
public static ConfigElement<bool> Auto_Scale_UI;
public static ConfigElement<bool> Default_Gameplay_Freecam;
public static ConfigElement<KeyCode> Pause;
@ -180,6 +181,10 @@ namespace UnityExplorer.Config
"For example, this will hide 'Class.NativeFieldInfoPtr_value' for the field 'Class.value'.",
false);
Auto_Scale_UI = new("Make the mod UI automatically scale with resolution",
"Especially useful when running games in high resolutions and you are having a hard time reading the mods menu (requires restart).",
true);
Default_Gameplay_Freecam = new("Default Gameplay Freecam",
"Turn this on if you want the default gameplay freecam toggle on the Freecam panel to be on on startup.",
false);

View File

@ -20,7 +20,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "CinematicUnityExplorer";
public const string VERSION = "1.1.0";
public const string VERSION = "1.2.0";
public const string AUTHOR = "originalnicodr, Sinai, yukieiji";
public const string GUID = "com.originalnicodr.cinematicunityexplorer";
@ -80,7 +80,7 @@ namespace UnityExplorer
KeypressListener.Setup();
MakeUEUIScale();
if (ConfigManager.Auto_Scale_UI.Value) MakeUEUIScale();
}
internal static void Update()

View File

@ -23,14 +23,15 @@ 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 => 1300;
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;
Toggle masterAnimatorToggle = new Toggle();
Toggle masterAnimatorToggle;
Toggle masterMeshToggle;
private static ScrollPool<AnimatorCell> animatorScrollPool;
internal List<AnimatorPlayer> animators = new List<AnimatorPlayer>();
@ -47,8 +48,6 @@ namespace UnityExplorer.UI.Panels
animatorScrollPool.Initialize(this);
DoneScrollPoolInit = true;
}
animatorScrollPool.Refresh(true, false);
}
private void FindAllAnimators(){
@ -68,8 +67,16 @@ namespace UnityExplorer.UI.Panels
{
if (animators[i].animator.wrappedObject != null){
int newAnimatorsIndex = newAnimators.FindIndex(a => a.animator.wrappedObject == animators[i].animator.wrappedObject);
if (newAnimatorsIndex != -1)
if (newAnimatorsIndex != -1) {
// If refreshing the animator gives us new animations, add them to the already existing ones.
// Might break stuff.
foreach (IAnimationClip animationClip in newAnimators[newAnimatorsIndex].animations) {
// TODO: Refactor AnimatorPlayer.animations from List<IAnimationClip> to HashSet<IAnimationClip> to avoid checking this
if (!animators[i].animations.Contains(animationClip))
animators[i].animations.Add(animationClip);
}
newAnimators[newAnimatorsIndex] = animators[i];
}
}
}
animators = newAnimators;
@ -99,6 +106,19 @@ namespace UnityExplorer.UI.Panels
animatorScrollPool.Refresh(true, false);
}
public void MasterToggleMeshes(bool enable){
// Load animators for the first time if there are not any
if (animators.Count == 0) FindAllAnimators();
foreach (AnimatorPlayer animatorPlayer in animators){
if (!animatorPlayer.shouldIgnoreMasterToggle){
animatorPlayer.SetMeshesEnabled(enable);
}
}
animatorScrollPool.Refresh(true, false);
}
public void HotkeyToggleAnimators(){
masterAnimatorToggle.isOn = !masterAnimatorToggle.isOn;
}
@ -110,25 +130,37 @@ namespace UnityExplorer.UI.Panels
GameObject firstGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999);
//UIElements.Add(horiGroup);
ButtonRef updateAnimators = UIFactory.CreateButton(firstGroup, "RefreshAnimators", "Refresh Animators");
UIFactory.SetLayoutElement(updateAnimators.GameObject, minWidth: 150, minHeight: 25);
updateAnimators.OnClick += FindAllAnimators;
GameObject headerSpace1 = UIFactory.CreateUIObject("HeaderSpace1", firstGroup);
UIFactory.SetLayoutElement(headerSpace1, minWidth: 0, flexibleWidth: 0);
GameObject meshObj = UIFactory.CreateToggle(firstGroup, "Master Mesh Toggle", out masterMeshToggle, out Text masterMeshText);
UIFactory.SetLayoutElement(meshObj, minHeight: 25, minWidth: 230);
masterMeshToggle.onValueChanged.AddListener(value => MasterToggleMeshes(value));
masterMeshText.text = "Master Mesh Toggler";
GameObject animatorObj = UIFactory.CreateToggle(firstGroup, "Master Animation Toggle", out masterAnimatorToggle, out Text masterAnimatorText);
UIFactory.SetLayoutElement(animatorObj, minHeight: 25);
masterAnimatorToggle.onValueChanged.AddListener(value => MasterToggleAnimators(value));
masterAnimatorText.text = "Master Animator Toggler";
GameObject headerSpace2 = UIFactory.CreateUIObject("HeaderSpace2", firstGroup);
UIFactory.SetLayoutElement(headerSpace2, minWidth: 10, flexibleWidth: 0);
ButtonRef resetAnimators = UIFactory.CreateButton(firstGroup, "ResetAnimators", "Reset Animators");
UIFactory.SetLayoutElement(resetAnimators.GameObject, minWidth: 150, minHeight: 25);
resetAnimators.OnClick += ResetAllAnimators;
GameObject animatorObj = UIFactory.CreateToggle(firstGroup, $"Master Animation Toggle", out masterAnimatorToggle, out Text masterAnimatorText);
UIFactory.SetLayoutElement(animatorObj, minHeight: 25);
masterAnimatorToggle.isOn = true;
masterAnimatorToggle.onValueChanged.AddListener(value => MasterToggleAnimators(value));
masterAnimatorText.text = "Master Toggler";
GameObject secondGroup = UIFactory.CreateHorizontalGroup(firstGroup, "HeaderRight", false, false, true, true, 3,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleRight);
UIFactory.SetLayoutElement(secondGroup, minHeight: 25, flexibleWidth: 9999);
GameObject secondGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999);
ButtonRef updateAnimators = UIFactory.CreateButton(secondGroup, "RefreshAnimators", "Refresh Animators");
UIFactory.SetLayoutElement(updateAnimators.GameObject, minWidth: 150, minHeight: 25);
updateAnimators.OnClick += FindAllAnimators;
GameObject headerSpaceRight = UIFactory.CreateUIObject("HeaderSpaceRight", firstGroup);
UIFactory.SetLayoutElement(headerSpaceRight, minWidth: 25, flexibleWidth: 0);
animatorScrollPool = UIFactory.CreateScrollPool<AnimatorCell>(ContentRoot, "AnimatorsList", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));

135
src/UI/Panels/BonesPanel.cs Normal file
View File

@ -0,0 +1,135 @@
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 Dictionary<string, CachedBonesTransform> bonesOriginalState = new();
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(OnTurnOffAnimatorToggle);
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);
}
private void OnTurnOffAnimatorToggle(bool value)
{
if (value){
// Restore meshes manually in case some are not part of a skeleton and won't get restored automatically.
// Besides, this restores the scale, which the animator doesn't.
foreach (Transform bone in bones){
CachedBonesTransform CachedBonesTransform = bonesOriginalState[bone.name];
bone.localPosition = CachedBonesTransform.position;
bone.localEulerAngles = CachedBonesTransform.angles;
bone.localScale = CachedBonesTransform.scale;
// We assume these were on before. If not we should save its state beforehand.
bone.gameObject.SetActive(true);
}
} else {
bonesOriginalState.Clear();
foreach (Transform bone in bones){
bonesOriginalState[bone.name] = new CachedBonesTransform(bone.localPosition, bone.localEulerAngles, bone.localScale);
}
}
animator.enabled = value;
}
public void RestoreBoneState(string boneName)
{
foreach (Transform bone in bones){
if (bone.name == boneName){
CachedBonesTransform CachedBonesTransform = bonesOriginalState[boneName];
bone.localPosition = CachedBonesTransform.position;
bone.localEulerAngles = CachedBonesTransform.angles;
bone.localScale = CachedBonesTransform.scale;
return;
}
}
}
public void SetCell(BonesCell cell, int index)
{
if (index >= bones.Count)
{
cell.Disable();
return;
}
Transform bone = bones[index];
cell.SetBone(bone, this);
cell.UpdateTransformControlValues(true);
}
public void OnCellBorrowed(BonesCell cell) {
cell.UpdateVectorSlider();
}
public override void Update()
{
base.Update();
foreach(BonesCell boneCell in boneScrollPool.CellPool) {
boneCell.UpdateVectorSlider();
}
}
}
struct CachedBonesTransform
{
public CachedBonesTransform(Vector3 position, Vector3 angles, Vector3 scale)
{
this.position = position;
this.angles = angles;
this.scale = scale;
}
public readonly Vector3 position;
public readonly Vector3 angles;
public readonly Vector3 scale;
}
}

View File

@ -241,7 +241,8 @@ namespace UnityExplorer.UI.Panels
IEnumerable<Behaviour> comps = ourCamera.GetComponentsInChildren<Behaviour>();
foreach (Behaviour comp in comps)
{
if (comp.GetActualType().ToString() == "Cinemachine.CinemachineBrain"){
string comp_type = comp.GetActualType().ToString();
if (comp_type == "Cinemachine.CinemachineBrain" || comp_type == "Il2CppCinemachine.CinemachineBrain"){
comp.enabled = enable;
disabledCinemachine = !enable;
break;
@ -685,7 +686,6 @@ namespace UnityExplorer.UI.Panels
FreeCamPanel.EndFreecam();
return;
}
Transform transform = FreeCamPanel.ourCamera.transform;
if (!FreeCamPanel.blockFreecamMovementToggle.isOn && !FreeCamPanel.cameraPathMover.playingPath && FreeCamPanel.connector?.IsActive != true) {

View File

@ -74,13 +74,26 @@ namespace UnityExplorer.UI.Widgets
{
if (float.TryParse(val, out float f))
{
if (f < slider.minValue || f > slider.maxValue){
if (f < slider.minValue){
ExplorerCore.LogWarning("Error, new time scale value outside of margins.");
timeInput.Text = desiredTime.ToString("0.00");
return;
}
slider.value = f; // Will update the desiredTime value and extra things
// Allow registering timescale values above the slider max value
if (f >= slider.maxValue) {
// Move the slider to the right
slider.value = slider.maxValue;
desiredTime = f;
pause = false;
previousDesiredTime = desiredTime;
}
else {
slider.value = f; // Will update the desiredTime value and extra things
}
timeInput.Text = f.ToString("0.00");
}
}