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:
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
@@ -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
@@ -18,4 +18,10 @@ MonoBehaviour:
|
|||||||
walkClipSpeed: 1.4
|
walkClipSpeed: 1.4
|
||||||
runClip: {fileID: -203655887218126122, guid: a4d4588e12cac6f48a17583c08a100d2, type: 3}
|
runClip: {fileID: -203655887218126122, guid: a4d4588e12cac6f48a17583c08a100d2, type: 3}
|
||||||
runClipSpeed: 1.5
|
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
|
||||||
@@ -120,6 +120,7 @@ MonoBehaviour:
|
|||||||
jumpHeight: 1.2
|
jumpHeight: 1.2
|
||||||
gravity: -14.715
|
gravity: -14.715
|
||||||
groundedStickVelocity: -2
|
groundedStickVelocity: -2
|
||||||
|
fallAnimationSpeed: 0.1
|
||||||
slideDownSteepSlopes: 1
|
slideDownSteepSlopes: 1
|
||||||
slideAcceleration: 20
|
slideAcceleration: 20
|
||||||
maxSlideSpeed: 8
|
maxSlideSpeed: 8
|
||||||
|
|||||||
@@ -7834,14 +7834,6 @@ PrefabInstance:
|
|||||||
propertyPath: m_Name
|
propertyPath: m_Name
|
||||||
value: Player
|
value: Player
|
||||||
objectReference: {fileID: 0}
|
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_RemovedComponents: []
|
||||||
m_RemovedGameObjects: []
|
m_RemovedGameObjects: []
|
||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ public partial class PlayerCharacterController : MonoBehaviour
|
|||||||
[Tooltip("Small downward velocity kept while grounded for reliable ground contact.")]
|
[Tooltip("Small downward velocity kept while grounded for reliable ground contact.")]
|
||||||
public float groundedStickVelocity = -2f;
|
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")]
|
[Header("Slope Sliding")]
|
||||||
[Tooltip("Slide down surfaces steeper than the CharacterController's Slope Limit instead of balancing on them.")]
|
[Tooltip("Slide down surfaces steeper than the CharacterController's Slope Limit instead of balancing on them.")]
|
||||||
public bool slideDownSteepSlopes = true;
|
public bool slideDownSteepSlopes = true;
|
||||||
@@ -177,6 +181,13 @@ public partial class PlayerCharacterController : MonoBehaviour
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsMovingOnGround => _movingOnGround;
|
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>
|
/// <summary>
|
||||||
/// Set by <see cref="PlayerPoseController"/> while a pose Playable graph owns the Animator output.
|
/// 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
|
/// 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 _sprinting; // sprint key state this frame (exposed via IsSprinting)
|
||||||
private bool _isIdle; // snapshot: player essentially still this frame (exposed via IsIdle)
|
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 _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
|
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).
|
// 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
|
_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
|
_isIdle = ControlEnabled && (h * h + v * v) <= 1e-4f; // attached & no climb input = holding a pose
|
||||||
_movingOnGround = false; // on a wall/ceiling, never ground locomotion
|
_movingOnGround = false; // on a wall/ceiling, never ground locomotion
|
||||||
|
_isFalling = false; // attached to a surface, not falling
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,6 +466,9 @@ public partial class PlayerCharacterController : MonoBehaviour
|
|||||||
bool movingOnGround = _controller.isGrounded && horizontalVel.sqrMagnitude > RunningSpeedThresholdSqr;
|
bool movingOnGround = _controller.isGrounded && horizontalVel.sqrMagnitude > RunningSpeedThresholdSqr;
|
||||||
_movingOnGround = movingOnGround;
|
_movingOnGround = movingOnGround;
|
||||||
_isIdle = ControlEnabled && _controller.isGrounded && !movingOnGround; // standing still on the floor
|
_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>
|
/// <summary>
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ public class PlayerPose : ScriptableObject
|
|||||||
[Range(0.1f, 3f)]
|
[Range(0.1f, 3f)]
|
||||||
[SerializeField] private float runClipSpeed = 1f;
|
[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")]
|
[Header("Collision")]
|
||||||
[Tooltip("CharacterController capsule center Y while this pose is active (meters, local space). Lower " +
|
[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.")]
|
"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>
|
/// <summary>Playback speed multiplier for the run clip (1 = authored speed).</summary>
|
||||||
public float RunClipSpeed => runClipSpeed;
|
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>
|
/// <summary>CharacterController capsule center Y applied while this pose is active.</summary>
|
||||||
public float ControllerCenterY => controllerCenterY;
|
public float ControllerCenterY => controllerCenterY;
|
||||||
|
|
||||||
|
|||||||
@@ -147,11 +147,12 @@ public partial class PlayerPoseController : MonoBehaviour
|
|||||||
|
|
||||||
PlayerPose pose = ActivePose();
|
PlayerPose pose = ActivePose();
|
||||||
|
|
||||||
// Pick the clip for the current locomotion state. Standing still, airborne, on a wall or on the
|
// Pick the clip for the current locomotion state. Standing still, on a wall or on the ceiling
|
||||||
// ceiling holds the idle pose; moving on the ground plays the run clip while sprinting or the
|
// holds the idle pose; moving on the ground plays the run clip while sprinting or the walk clip
|
||||||
// walk clip otherwise. Both locomotion clips are optional — a null one falls back to the idle
|
// otherwise; falling (airborne and descending past the controller's threshold) plays the fall
|
||||||
// pose, so an unset walk clip keeps the held-pose behaviour. PlayPose crossfades on any change
|
// clip. All three extra clips are optional — a null one falls back to the idle pose, so an unset
|
||||||
// and hands the rig back only when there is no clip at all (empty list / null idle clip).
|
// 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;
|
AnimationClip clip = pose ? pose.Clip : null;
|
||||||
float speed = pose ? pose.ClipSpeed : 1f;
|
float speed = pose ? pose.ClipSpeed : 1f;
|
||||||
if (pose && _player.IsMovingOnGround)
|
if (pose && _player.IsMovingOnGround)
|
||||||
@@ -163,6 +164,11 @@ public partial class PlayerPoseController : MonoBehaviour
|
|||||||
speed = _player.IsSprinting ? pose.RunClipSpeed : pose.WalkClipSpeed;
|
speed = _player.IsSprinting ? pose.RunClipSpeed : pose.WalkClipSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (pose && _player.IsFalling && pose.FallClip)
|
||||||
|
{
|
||||||
|
clip = pose.FallClip;
|
||||||
|
speed = pose.FallClipSpeed;
|
||||||
|
}
|
||||||
PlayPose(clip, speed);
|
PlayPose(clip, speed);
|
||||||
|
|
||||||
// CharacterController capsule: each pose carries its own center Y / radius / height so a low pose
|
// CharacterController capsule: each pose carries its own center Y / radius / height so a low pose
|
||||||
|
|||||||
+11
-9
@@ -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)
|
Schrägen mit Aufwärts-Hop drüber (kein Kanten-Flackern)
|
||||||
- [x] **Animator-Anbindung**: `Running`-Bool wird bei Bodenbewegung gesetzt
|
- [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)
|
### Bemalen (Kern der Camouflage-Idee)
|
||||||
- [x] **MeshPainter**: nahtfreies Bemalen direkt auf dem 3D-Mesh des Charakters
|
- [x] **MeshPainter**: nahtfreies Bemalen direkt auf dem 3D-Mesh des Charakters
|
||||||
- [x] **GPU- und CPU-Pfad**, umschaltbar; Edge-Padding/Dilation gegen Naht-Linien
|
- [x] **GPU- und CPU-Pfad**, umschaltbar; Edge-Padding/Dilation gegen Naht-Linien
|
||||||
- [x] Pinsel: **Farbe**, **Größe**, Texturauflösung, Grundfarbe (Inspector)
|
- [x] Pinsel: **Farbe**, **Größe**, Texturauflösung, Grundfarbe (Inspector)
|
||||||
- [x] Shader: `MeshPaintBrush.shader`, `MeshPaintDilate.shader`
|
- [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
|
### Kamera
|
||||||
- [x] **Cinemachine** 3.1.7 Third-Person **Orbit-Kamera** (Maus)
|
- [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 ❌
|
## 4. Checkliste — Was noch fehlt ❌
|
||||||
|
|
||||||
### Bemalen / Camouflage (höchste Priorität, ist unser Alleinstellungsmerkmal)
|
### 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
|
- [ ] 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)
|
### Spielablauf (Hide & Seek)
|
||||||
- [ ] **Seeker/Hunter-Rolle** (immer menschlicher Spieler)
|
- [ ] **Seeker/Hunter-Rolle** (immer menschlicher Spieler)
|
||||||
- [ ] **Runden-System**: Vorbereitungsphase (verstecken/bemalen) → Suchphase
|
- [ ] **Runden-System**: Vorbereitungsphase (verstecken/bemalen) → Suchphase
|
||||||
|
|||||||
Reference in new issue
Block a user