Disable foot IK on pose playables and add ball pose
- PlayerPoseController: add applyFootIK toggle (default off) and apply it to each pose playable so straight-knee idles render straight; Mecanim foot IK was bending the knees to plant the feet (162° instead of 180°). - Add Pose_Ball / Paintman-Ball animation and reorder the pose list. - Move StraightenKneesTool editor utility under Assets/Unused/Editor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
5dd35dd0f9
commit
080b2a1c39
10 files changed
+11609
-82
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f8de430ad75135743b87456111e7abac
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 7400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,21 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e3e6fee2893b9d848b8b1ccca743c14f, type: 3}
|
||||||
|
m_Name: Pose_Ball
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::PlayerPose
|
||||||
|
clip: {fileID: 7400000, guid: f8de430ad75135743b87456111e7abac, type: 2}
|
||||||
|
clipSpeed: 1
|
||||||
|
walkClip: {fileID: 0}
|
||||||
|
walkClipSpeed: 1
|
||||||
|
runClip: {fileID: 0}
|
||||||
|
runClipSpeed: 1
|
||||||
|
ceilingYOffset: 1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c5cf9d420554e5e4e96d3bee1903967a
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -2213,12 +2213,14 @@ MonoBehaviour:
|
|||||||
poseSaveId:
|
poseSaveId:
|
||||||
poses:
|
poses:
|
||||||
- {fileID: 11400000, guid: 3b59c882f27d0d04a82d2db251af1728, type: 2}
|
- {fileID: 11400000, guid: 3b59c882f27d0d04a82d2db251af1728, type: 2}
|
||||||
- {fileID: 11400000, guid: 1545cfba9b5e06d41ae72c7ec766baaf, type: 2}
|
|
||||||
- {fileID: 11400000, guid: 649ecccdabb7ef9438addcec6663b037, type: 2}
|
- {fileID: 11400000, guid: 649ecccdabb7ef9438addcec6663b037, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 1545cfba9b5e06d41ae72c7ec766baaf, type: 2}
|
||||||
|
- {fileID: 11400000, guid: c5cf9d420554e5e4e96d3bee1903967a, type: 2}
|
||||||
defaultPoseIndex: 0
|
defaultPoseIndex: 0
|
||||||
cycleKey: 17
|
cycleKey: 17
|
||||||
poseBlendDuration: 0.2
|
poseBlendDuration: 0.2
|
||||||
ceilingOffsetSpeed: 4
|
ceilingOffsetSpeed: 4
|
||||||
|
applyFootIK: 0
|
||||||
--- !u!114 &1141039644
|
--- !u!114 &1141039644
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ public partial class PlayerPoseController : MonoBehaviour
|
|||||||
[Min(0f)]
|
[Min(0f)]
|
||||||
[SerializeField] private float ceilingOffsetSpeed = 4f;
|
[SerializeField] private float ceilingOffsetSpeed = 4f;
|
||||||
|
|
||||||
|
[Tooltip("Apply Mecanim foot IK to the played humanoid clips. Off (default) plays each pose exactly as " +
|
||||||
|
"authored. On bends the knees to plant the feet on the IK goal, which makes a straight-knee idle " +
|
||||||
|
"render visibly bent even when the clip's Lower Leg Stretch muscle is fully straight.")]
|
||||||
|
[SerializeField] private bool applyFootIK = false;
|
||||||
|
|
||||||
private PlayerCharacterController _player;
|
private PlayerCharacterController _player;
|
||||||
private Animator _animator;
|
private Animator _animator;
|
||||||
private string _graphName; // cached so building the graph allocates no string from the Update path
|
private string _graphName; // cached so building the graph allocates no string from the Update path
|
||||||
@@ -269,6 +274,10 @@ public partial class PlayerPoseController : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
_toPlayable = AnimationClipPlayable.Create(_graph, clip);
|
_toPlayable = AnimationClipPlayable.Create(_graph, clip);
|
||||||
|
// Off by default: foot IK plants the foot on its goal and bends the knee to reach it, so a straight-knee
|
||||||
|
// pose renders bent (SampleAnimation, which the authoring tools check against, never applies it). The
|
||||||
|
// outgoing pose on input 0 is a former _toPlayable, so it already carries this setting.
|
||||||
|
_toPlayable.SetApplyFootIK(applyFootIK);
|
||||||
_mixer.ConnectInput(1, _toPlayable, 0);
|
_mixer.ConnectInput(1, _toPlayable, 0);
|
||||||
_toClip = clip;
|
_toClip = clip;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 963f2dea141da8d439f1433399db6d4e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Editor utility that pins the humanoid knee muscles to fully straight on selected animation clips.
|
||||||
|
///
|
||||||
|
/// On this project's auto-generated PaintmanAvatar, a fully straight leg corresponds to the
|
||||||
|
/// "Lower Leg Stretch" muscle sitting at its maximum (1.0); anything below reads as a visibly bent
|
||||||
|
/// knee (~8 deg per 0.1). Authoring tools (UMotion, Mixamo retargets) commonly land at ~0.93, which
|
||||||
|
/// shows as the "slightly bent knees on an idle that should be straight" symptom. This tool sets the
|
||||||
|
/// Left/Right Lower Leg Stretch curves to a constant 1.0 so the knees read as a true 180 deg.
|
||||||
|
///
|
||||||
|
/// Only native .anim humanoid clips can be edited — imported clips (FBX/Blend) are regenerated on
|
||||||
|
/// import, so their curves can't be changed here and are skipped with a warning.
|
||||||
|
/// </summary>
|
||||||
|
public static class StraightenKneesTool
|
||||||
|
{
|
||||||
|
private const string LeftStretch = "Left Lower Leg Stretch";
|
||||||
|
private const string RightStretch = "Right Lower Leg Stretch";
|
||||||
|
private const float StraightValue = 1f; // muscle value that maps to a fully straight knee on this avatar
|
||||||
|
|
||||||
|
private const string ToolsMenu = "Tools/Chameleon/Straighten Knees (Selected Clips)";
|
||||||
|
private const string AssetsMenu = "Assets/Chameleon/Straighten Knees";
|
||||||
|
|
||||||
|
[MenuItem(ToolsMenu, false, 100)]
|
||||||
|
[MenuItem(AssetsMenu, false, 100)]
|
||||||
|
private static void StraightenSelected()
|
||||||
|
{
|
||||||
|
List<AnimationClip> clips = GetSelectedClips();
|
||||||
|
if (clips.Count == 0)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog("Straighten Knees",
|
||||||
|
"Select one or more native (.anim) humanoid AnimationClips in the Project window.", "OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int changed = 0, skipped = 0;
|
||||||
|
foreach (AnimationClip clip in clips)
|
||||||
|
{
|
||||||
|
if (StraightenClip(clip)) changed++;
|
||||||
|
else skipped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed > 0)
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
|
||||||
|
Debug.Log($"[Straighten Knees] Lower-leg stretch set to {StraightValue:0.##} on {changed} clip(s)" +
|
||||||
|
(skipped > 0 ? $", skipped {skipped} (non-.anim or non-humanoid)." : "."));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem(ToolsMenu, true)]
|
||||||
|
[MenuItem(AssetsMenu, true)]
|
||||||
|
private static bool StraightenSelectedValidate() => GetSelectedClips().Count > 0;
|
||||||
|
|
||||||
|
private static bool StraightenClip(AnimationClip clip)
|
||||||
|
{
|
||||||
|
if (!clip)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string path = AssetDatabase.GetAssetPath(clip);
|
||||||
|
if (!path.EndsWith(".anim", System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[Straighten Knees] '{clip.name}' is an imported clip ({path}); its curves are " +
|
||||||
|
"regenerated on import and can't be edited. Make an editable .anim copy first.", clip);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!clip.humanMotion)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[Straighten Knees] '{clip.name}' is not a humanoid clip; skipped.", clip);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Undo.RegisterCompleteObjectUndo(clip, "Straighten Knees");
|
||||||
|
SetMuscleConstant(clip, LeftStretch, StraightValue);
|
||||||
|
SetMuscleConstant(clip, RightStretch, StraightValue);
|
||||||
|
EditorUtility.SetDirty(clip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Replaces (or adds) the named muscle curve with a flat constant across the whole clip.</summary>
|
||||||
|
private static void SetMuscleConstant(AnimationClip clip, string muscle, float value)
|
||||||
|
{
|
||||||
|
var binding = EditorCurveBinding.FloatCurve(string.Empty, typeof(Animator), muscle);
|
||||||
|
float end = clip.length > 0f ? clip.length : 1f / Mathf.Max(1f, clip.frameRate);
|
||||||
|
var curve = new AnimationCurve(new Keyframe(0f, value), new Keyframe(end, value));
|
||||||
|
AnimationUtility.SetEditorCurve(clip, binding, curve);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<AnimationClip> GetSelectedClips()
|
||||||
|
{
|
||||||
|
var result = new List<AnimationClip>();
|
||||||
|
foreach (Object o in Selection.objects)
|
||||||
|
{
|
||||||
|
if (o is AnimationClip clip && !result.Contains(clip))
|
||||||
|
result.Add(clip);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4fc3fefa8bfcb6a4abe9fdd8e395e6cf
|
||||||
Reference in new issue
Block a user