Add per-pose fall animation state and clip

Play a dedicated falling animation when the player is airborne and
descending past a scale-aware velocity threshold.

- PlayerCharacterController: new IsFalling state + Fall Animation Speed
  threshold (true only when !grounded && !wallClimbing && descending,
  scaled via CapsuleScale); explicitly false during wall/ceiling climb.
- PlayerPose: per-pose Fall Clip + Fall Clip Speed (mirrors walk/run).
- PlayerPoseController: select the fall clip while falling, fall back to
  the idle clip when a pose has none (no regression).
- Author content: Paintman-Fall.anim assigned to Pose_Idle; UMotion
  project, Player prefab and scene authoring.
- Docs: check off the fall animation in GAME_DESIGN.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-06-27 13:49:16 +02:00
1 parent fc73e26da1
commit 3b26f2e622
10 files changed
+11515 -101

No files matched your search

File diff suppressed because it is too large. Load diff
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 460c2fe161ff3a6449648312c4e14eff
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large. Load diff
+7 -1
View File
@@ -18,4 +18,10 @@ MonoBehaviour:
walkClipSpeed: 1.4
runClip: {fileID: -203655887218126122, guid: a4d4588e12cac6f48a17583c08a100d2, type: 3}
runClipSpeed: 1.5
ceilingYOffset: 0
fallClip: {fileID: 7400000, guid: 460c2fe161ff3a6449648312c4e14eff, type: 2}
fallClipSpeed: 1
controllerCenterY: 0.66
controllerRadius: 0.1
controllerHeight: 1.3
cameraTargetYOffset: 0
firstPersonCameraTargetYOffset: 0
+1
View File
@@ -120,6 +120,7 @@ MonoBehaviour:
jumpHeight: 1.2
gravity: -14.715
groundedStickVelocity: -2
fallAnimationSpeed: 0.1
slideDownSteepSlopes: 1
slideAcceleration: 20
maxSlideSpeed: 8
-8
View File
@@ -7834,14 +7834,6 @@ PrefabInstance:
propertyPath: m_Name
value: Player
objectReference: {fileID: 0}
- target: {fileID: 3694788198465753627, guid: cf153a3aea3d5da4f9d4cdbff0be8180, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8001664246499114791, guid: cf153a3aea3d5da4f9d4cdbff0be8180, type: 3}
propertyPath: m_Enabled
value: 1
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
@@ -71,6 +71,10 @@ public partial class PlayerCharacterController : MonoBehaviour
[Tooltip("Small downward velocity kept while grounded for reliable ground contact.")]
public float groundedStickVelocity = -2f;
[Tooltip("Downward speed (units/second) past which the player counts as falling, for the fall " +
"animation. Only while airborne (not grounded, not wall-climbing). Scales with player size.")]
public float fallAnimationSpeed = 4f;
[Header("Slope Sliding")]
[Tooltip("Slide down surfaces steeper than the CharacterController's Slope Limit instead of balancing on them.")]
public bool slideDownSteepSlopes = true;
@@ -177,6 +181,13 @@ public partial class PlayerCharacterController : MonoBehaviour
/// </summary>
public bool IsMovingOnGround => _movingOnGround;
/// <summary>
/// True while airborne and descending faster than <see cref="fallAnimationSpeed"/> (scaled by player
/// size): not grounded and not wall-climbing. Rising after a jump, standing, moving on the ground, and
/// wall/ceiling states all read false. Exposed so the pose system can pick its fall clip.
/// </summary>
public bool IsFalling => _isFalling;
/// <summary>
/// Set by <see cref="PlayerPoseController"/> while a pose Playable graph owns the Animator output.
/// While set, the run-bool is not poked: it would be moot (the graph overrides it) and Unity would
@@ -211,6 +222,7 @@ public partial class PlayerCharacterController : MonoBehaviour
private bool _sprinting; // sprint key state this frame (exposed via IsSprinting)
private bool _isIdle; // snapshot: player essentially still this frame (exposed via IsIdle)
private bool _movingOnGround; // snapshot: grounded & moving above the run threshold (exposed via IsMovingOnGround)
private bool _isFalling; // snapshot: airborne & descending past the fall threshold (exposed via IsFalling)
private float _groundOffset; // signed vertical mesh raise from the ground mouse pose-adjust (+ up / - down); folded into SetMeshLean
// Ground contact normal from the latest Move (for slope sliding).
@@ -367,6 +379,7 @@ public partial class PlayerCharacterController : MonoBehaviour
_horizontalVelocity = Vector3.zero; // drop ground momentum so resuming starts from rest
_isIdle = ControlEnabled && (h * h + v * v) <= 1e-4f; // attached & no climb input = holding a pose
_movingOnGround = false; // on a wall/ceiling, never ground locomotion
_isFalling = false; // attached to a surface, not falling
return;
}
@@ -453,6 +466,9 @@ public partial class PlayerCharacterController : MonoBehaviour
bool movingOnGround = _controller.isGrounded && horizontalVel.sqrMagnitude > RunningSpeedThresholdSqr;
_movingOnGround = movingOnGround;
_isIdle = ControlEnabled && _controller.isGrounded && !movingOnGround; // standing still on the floor
// Falling: airborne and descending past the threshold (wall-climb already returned above, so
// not-grounded here means free in the air). Scaled by player size like gravity/jump.
_isFalling = !_controller.isGrounded && _verticalVelocity < -fallAnimationSpeed * CapsuleScale;
}
/// <summary>
+14
View File
@@ -38,6 +38,14 @@ public class PlayerPose : ScriptableObject
[Range(0.1f, 3f)]
[SerializeField] private float runClipSpeed = 1f;
[Tooltip("Optional. Played instead of the idle clip while falling (airborne and descending past the " +
"controller's fall threshold). Leave empty to keep holding the idle pose while falling. Should loop.")]
[SerializeField] private AnimationClip fallClip;
[Tooltip("Playback speed multiplier for the fall clip. 1 = authored speed.")]
[Range(0.1f, 3f)]
[SerializeField] private float fallClipSpeed = 1f;
[Header("Collision")]
[Tooltip("CharacterController capsule center Y while this pose is active (meters, local space). Lower " +
"this together with Height so a short pose still rests its feet on the floor.")]
@@ -82,6 +90,12 @@ public class PlayerPose : ScriptableObject
/// <summary>Playback speed multiplier for the run clip (1 = authored speed).</summary>
public float RunClipSpeed => runClipSpeed;
/// <summary>Optional clip played while falling; null = hold the idle pose.</summary>
public AnimationClip FallClip => fallClip;
/// <summary>Playback speed multiplier for the fall clip (1 = authored speed).</summary>
public float FallClipSpeed => fallClipSpeed;
/// <summary>CharacterController capsule center Y applied while this pose is active.</summary>
public float ControllerCenterY => controllerCenterY;
+11 -5
View File
@@ -147,11 +147,12 @@ public partial class PlayerPoseController : MonoBehaviour
PlayerPose pose = ActivePose();
// Pick the clip for the current locomotion state. Standing still, airborne, on a wall or on the
// ceiling holds the idle pose; moving on the ground plays the run clip while sprinting or the
// walk clip otherwise. Both locomotion clips are optional — a null one falls back to the idle
// pose, so an unset walk clip keeps the held-pose behaviour. PlayPose crossfades on any change
// and hands the rig back only when there is no clip at all (empty list / null idle clip).
// Pick the clip for the current locomotion state. Standing still, on a wall or on the ceiling
// holds the idle pose; moving on the ground plays the run clip while sprinting or the walk clip
// otherwise; falling (airborne and descending past the controller's threshold) plays the fall
// clip. All three extra clips are optional — a null one falls back to the idle pose, so an unset
// clip keeps the held-pose behaviour. PlayPose crossfades on any change and hands the rig back
// only when there is no clip at all (empty list / null idle clip).
AnimationClip clip = pose ? pose.Clip : null;
float speed = pose ? pose.ClipSpeed : 1f;
if (pose && _player.IsMovingOnGround)
@@ -163,6 +164,11 @@ public partial class PlayerPoseController : MonoBehaviour
speed = _player.IsSprinting ? pose.RunClipSpeed : pose.WalkClipSpeed;
}
}
else if (pose && _player.IsFalling && pose.FallClip)
{
clip = pose.FallClip;
speed = pose.FallClipSpeed;
}
PlayPose(clip, speed);
// CharacterController capsule: each pose carries its own center Y / radius / height so a low pose
+11 -9
View File
@@ -59,11 +59,22 @@ Online-PvP-Versteckspiel (Hide & Seek), 2–10 Spieler. Kurzüberblick der Mecha
Schrägen mit Aufwärts-Hop drüber (kein Kanten-Flackern)
- [x] **Animator-Anbindung**: `Running`-Bool wird bei Bodenbewegung gesetzt
### Posen & Charakter
- [x] **Posen-System** (zusammenrollen, hinlegen) zum Anpassen der Silhouette
- [x] Finales Charakter-Modell + Rig
- [x] Animationen (Klettern, Springen, Posen) — **Laufen** bereits über den
`Running`-Animator-Bool angebunden
- [x] **Fall-Animation**: eigene Pose-Animation beim Fallen (Abwärts-Velocity über
Schwellenwert, nicht am Boden; pro Pose authorbar mit Idle-Fallback)
### Bemalen (Kern der Camouflage-Idee)
- [x] **MeshPainter**: nahtfreies Bemalen direkt auf dem 3D-Mesh des Charakters
- [x] **GPU- und CPU-Pfad**, umschaltbar; Edge-Padding/Dilation gegen Naht-Linien
- [x] Pinsel: **Farbe**, **Größe**, Texturauflösung, Grundfarbe (Inspector)
- [x] Shader: `MeshPaintBrush.shader`, `MeshPaintDilate.shader`
- [x] **Farbwähler-UI** (Farbrad/Color-Picker) statt fester Inspector-Farbe
- [x] **Pipette / Eyedropper**: Umgebungsfarbe per Klick aufnehmen
- [x] Weitere Brushes (Größe per Hotkey/Slider, evtl. Muster/Texturen)
### Kamera
- [x] **Cinemachine** 3.1.7 Third-Person **Orbit-Kamera** (Maus)
@@ -83,17 +94,8 @@ Online-PvP-Versteckspiel (Hide & Seek), 2–10 Spieler. Kurzüberblick der Mecha
## 4. Checkliste — Was noch fehlt ❌
### Bemalen / Camouflage (höchste Priorität, ist unser Alleinstellungsmerkmal)
- [ ] **Farbwähler-UI** (Farbrad/Color-Picker) statt fester Inspector-Farbe
- [ ] **Pipette / Eyedropper**: Umgebungsfarbe per Klick aufnehmen
- [ ] Weitere Brushes (Größe per Hotkey/Slider, evtl. Muster/Texturen)
- [ ] Bemalung **zurücksetzen** / Undo
### Posen & Charakter
- [ ] **Posen-System** (zusammenrollen, hinlegen) zum Anpassen der Silhouette
- [ ] Finales Charakter-Modell + Rig
- [ ] Animationen (Klettern, Springen, Posen) — **Laufen** bereits über den
`Running`-Animator-Bool angebunden
### Spielablauf (Hide & Seek)
- [ ] **Seeker/Hunter-Rolle** (immer menschlicher Spieler)
- [ ] **Runden-System**: Vorbereitungsphase (verstecken/bemalen) → Suchphase