Skip to content
blugart-devPublic

About

Physics-based reactive characters for Godot 4.6+ (Euphoria-like hit reactions)

Topics

Resources

Contributing

Stars

30 stars

Watchers

1 watching

Forks

Repository files navigation

Kickback

Tests Godot 4.7+ License: MIT

Physics-based reactive characters for Godot 4.7+

Inspired by NaturalMotion's Euphoria engine (GTA IV/V, Red Dead Redemption). Characters react dynamically to gunshots, explosions, melee hits, and arrows using active ragdoll physics — every hit produces a unique, physically-driven reaction.

See It In Action

Run any scene from demo/ in the Godot editor to see the plugin live:

  • euphoria_showcase.tscn — Best overview: active resistance, stagger sway, pain escalation, injuries
  • shooting_range.tscn — FPS shooting with 5 weapon profiles + ball-throw alt-fire
  • tuning_playground.tscn — Tuning Lab: 5 presets side-by-side + a Custom character with 35+ live sliders

Press F3 in any demo to cycle debug gizmos (bone dots → wireframe → full dashboard).

Kickback euphoria showcase — stagger, active resistance, pain escalation, injuries

Weapon variety — 5 impact profiles, each producing a distinct reaction

Shooting range with bullet, melee, arrow, shotgun, and explosion impacts

Game integration — NPCs walk, get hit, recover, and keep going

NPCs patrol, react to hits, recover with injuries, and resume

Project status

Kickback is pre-1.0 and actively evolving. It has a robust passive reaction foundation — velocity springs, balance sensing, a full state machine, terrain foot IK — but the active self-preservation behaviors that define Euphoria (stumble-stepping, arm/wall bracing, grabbing, procedural poses) are still ahead.

By difficulty-weighted progress toward that goal, the project is at ~20–25%. The 2026-09 audit revised the earlier ~30% figure: the 0.4.0 "directed stumble" is a scripted root displacement with foot-IK step targets, not balance-driven stepping, so it earns nothing on the self-preservation row. 1.0.0 is reserved for full Euphoria parity. See ROADMAP.md for the scorecard and milestones, and VERSIONING.md for what the numbers mean.

Today it works well as a hit-reaction system for humanoid (Mixamo-style) rigs. General multi-rig robustness and the self-preservation layer are on the roadmap.

Features

  • Active ragdoll — 16 RigidBody3D physics skeleton tracks animation via velocity-based springs. Hits reduce spring strength so physics temporarily wins. Full ragdoll with automatic get-up recovery.

  • Stagger state — between absorption and full ragdoll. Character visibly wobbles but stays on feet. Configurable threshold, duration, and escalation on follow-up hits.

  • Balance tracking — mass-weighted center of mass vs the midpoint and half-spread of the feet drives stagger behavior. Characters that lean too far ragdoll; balanced characters recover early. A static estimate (no extrapolated CoM or contact support polygon yet — see the audit §3.3), but physics-informed rather than timer-based.

  • Momentum transfer — running characters carry their velocity into ragdoll, tumbling forward instead of dropping in place.

  • Fatigue — repeated hits degrade effective spring strength over time. Fatigued characters wobble more at baseline and recover to lower maxes. Decays slowly between engagements.

  • Hit stacking — rapid consecutive hits escalate via streak multiplier. Hits during recovery can interrupt get-up and re-ragdoll the character.

  • Reaction pulses — sub-stagger hits produce a brief visible wobble that fades over 200ms. Even light hits are noticeable.

  • Directional bracing — during stagger, bones opposite the hit direction strengthen while hit-side bones weaken further. Characters visibly fight to stay upright.

  • Active Resistance — during stagger, per-bone spring strengths dynamically adjust every physics frame. Counter-side bones stiffen against imbalance, core engages progressively, load-bearing leg braces. Degrades with fatigue. Characters actively fight to stay upright instead of passively wobbling.

  • Stagger sway force — continuous oscillating force on core bones during stagger creates visible back-and-forth wobble as springs fight the force. Decays quadratically over stagger duration.

  • Cumulative pain — sustained fire deterministically escalates reactions (flinch → stagger → ragdoll) instead of relying on dice rolls. Pain decays between engagements.

  • Movement-aware hits — running characters are less stable and stagger more easily. Stagger direction blends with movement direction for natural stumbling.

  • Threat anticipation — anticipate_threat(direction, urgency) API for pre-hit flinch. Call when bullets fly nearby or an enemy winds up an attack.

  • Micro hit reactions — torso bend, head whip, and spin torque at the moment of impact. Every hit has a visceral split-second reaction.

  • Regional impairment — persistent per-bone injury that outlasts spring recovery. Shot in the leg causes visible sag/limp. Shot in the arm makes it dangle. Injuries heal slowly.

  • Protected bones — mark bones (e.g., legs) that never weaken from hits. Upper body reacts to impacts while legs stay animated and feet stay planted.

    Protected vs unprotected bones — same hit, different result
  • Directed stumble + arm bracing (0.4.0) — a staggering hit shoves the character along the hit direction with foot-IK catch steps, windmilling arms, and a reach-for-ground on a committed fall. Plainly: the stumble is a scripted root displacement (ActiveRagdollController._update_directed_stumble moves the character root), not balance-driven stepping — see SELF_PRESERVATION.md and the audit §3.2, which recommends replacing it.

  • Always-simulated rig — physics bodies never freeze, springs are always active. Hit reactions feel immediate with no startup delay. Gravity scales with spring strength: RagdollTuning.gravity_scale (default 1.0) × (1 − strength), so a limp ragdoll falls at real gravity.

  • Skeleton auto-detection — SkeletonDetector maps humanoid bones by name tokens plus position in the bone hierarchy. Verified in the test suite against Mixamo (mixamorig: / mixamorig_), Blender Rigify DEF bones, the Unreal Engine 5 Mannequin, generic Hips/Spine/Chest/Neck/Head rigs, and single-spine-bone rigs (no Chest: the head and arm joints re-parent to the nearest torso body). Anything else: author a RagdollProfile.

  • Animation-agnostic — works with AnimationPlayer, AnimationTree, or any system that drives Skeleton3D bone poses. Controllers emit signals; animation is the user's responsibility.

  • Configurable everything — skeleton mapping (RagdollProfile), physics tuning (RagdollTuning), impact parameters (ImpactProfile) — all via Resources with sensible defaults.

  • Hit detection utility — KickbackRaycast.shoot_from_camera() handles raycast + routing in one line.

  • Debug gizmos — F3 cycles through 3 detail levels: bone dots → skeleton wireframe + state labels → full dashboard with status panels, center of mass, velocity vectors, and balance/fatigue bars.

  • 8 demo scenes — comparison, shooting range, signals, tuning playground, stress test, animated NPC, foot IK, euphoria showcase.

Requirements

  • Godot 4.7+
  • Jolt Physics (Project Settings > Physics > 3D > Physics Engine = "Jolt Physics")

Installation

  1. Copy the addons/kickback/ folder into your project's addons/ directory
  2. Enable the plugin: Project > Project Settings > Plugins > Kickback > Enable
  3. Verify Jolt Physics is active in Project Settings > Physics > 3D

Quick Start

1. Add Kickback to your character

Select your character node (must have a Skeleton3D child), then:

Project > Tools > "Add Kickback to Selected"

This adds the active-ragdoll node set — the full physics rig with springs (stagger, ragdoll, and recovery). The plugin auto-detects humanoid bones and wires the controller nodes.

2. Send hits from your game

# One-line hit detection + routing:
func _unhandled_input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
        var profile := ImpactProfile.create_bullet()
        KickbackRaycast.shoot_from_camera(get_viewport(), event.position, profile)

Or manual routing:

var kickback: KickbackCharacter = character.get_node("KickbackCharacter")
kickback.receive_hit(hit_collider, hit_direction, hit_position, profile)

3. Impact profiles

ImpactProfile.create_bullet()     # Low impulse, high reduction, fast recovery
ImpactProfile.create_shotgun()    # Wide spread, 40% ragdoll chance
ImpactProfile.create_explosion()  # Massive impulse, upward bias, near-certain ragdoll
ImpactProfile.create_melee()      # Strong transfer, moderate ragdoll chance
ImpactProfile.create_arrow()      # Localized, moderate impulse

Or use the preset .tres files in addons/kickback/presets/.

4. Customize

  • Force stagger: kickback_character.trigger_stagger(hit_direction)
  • Force ragdoll: kickback_character.trigger_ragdoll()
  • Persistent ragdoll (death): kickback_character.set_persistent(true) — revive with set_persistent(false)
  • Animation-guided death: play your death clip, then kickback_character.set_persistent_guided(strength_scale := 0.5, ramp_time := 0.5, ease := 1.0) — the springs keep chasing the clip at strength_scale of their base and ramp to zero over ramp_time (the authored fall shapes the body, physics takes over), ending as a plain persistent ragdoll; queue_persistent_guided(...) is the spawn-safe form
  • Protected bones: set ragdoll_tuning.protected_bones to keep legs (or any bones) animated during hits
  • Query state: is_ragdolled(), is_staggering(), get_active_state_name()
  • Advanced control: var active_controller = kickback_character.get_active_controller() — the ActiveRagdollController (or null), for the queries below
  • Query balance: active_controller.get_balance_ratio() — 0.0 = balanced, 1.0+ = off-balance
  • Query fatigue: active_controller.get_fatigue() — 0.0 = fresh, 1.0 = exhausted
  • Reset fatigue: active_controller.reset_fatigue() — on healing or respawn
  • Query hit streak: active_controller.get_hit_streak() — rapid consecutive hit count
  • Query pain: active_controller.get_pain() — cumulative damage level
  • Pre-hit flinch: kickback_character.anticipate_threat(direction, urgency) — defensive lean before impact
  • Query injuries: active_controller.get_injury(rig_name) — per-bone injury level
  • Reset injuries: active_controller.reset_injuries() — heal all injuries
  • Different skeleton? Auto-detects, or create a RagdollProfile manually
  • Different physics feel? Create a RagdollTuning resource
  • Find all characters: KickbackCharacter.find_all(scene_root)

Architecture

Character (Node3D)
├── KickbackCharacter (coordinator — detects mode, routes hits)
├── PhysicsRigBuilder (creates 16 RigidBody3D + 15 joints)
├── PhysicsRigSync (SkeletonModifier3D: physics → skeleton)
├── SpringResolver (animation → physics velocity springs)
└── ActiveRagdollController (NORMAL/STAGGER/RAGDOLL/GETTING_UP/PERSISTENT)

Active Ragdoll signals (connect for custom animation handling):

  • stagger_started(hit_direction) — character wobbles, stays on feet
  • stagger_finished() — recovered from stagger
  • ragdoll_started() — full ragdoll triggered
  • recovery_started(face_up) — getting up from ragdoll
  • recovery_finished() — fully recovered
  • hit_absorbed(rig_name, strength) — light hit, no state change
  • balance_changed(ratio) — CoM balance ratio during stagger (0.0 = balanced, 1.0 = falling)
  • fatigue_changed(level) — fatigue from repeated hits (0.0 = fresh, 1.0 = exhausted)
  • recovery_interrupted() — hit during get-up knocked character back down
  • pain_changed(level) — cumulative pain from sustained hits (0.0-1.0)
  • threat_anticipated(direction, urgency) — anticipate_threat() was called
  • region_injured(rig_name, severity) — bone sustained persistent injury

Optional component: add a PhysicsCollisionMonitor next to the controllers to emit body_impact(bone_name, velocity, contact_body) whenever ragdoll bodies strike the environment — handy for impact SFX, decals, or scoring. See INTEGRATION.md.

Important: All root movement and rotation must happen in _physics_process, not _process, to stay in sync with the spring resolver.

Demo Scenes

Run any scene from demo/ to see the plugin in action:

Scene What it shows
demo.tscn Kickback active ragdoll vs Godot's built-in PhysicalBoneSimulator3D
shooting_range.tscn FPS controller, 5 weapon profiles + ball-throw alt-fire (RMB)
tuning_playground.tscn Tuning Lab: Tank/Standard/Loose/Fragile/Protected presets + a Custom character with live sliders
signal_showcase.tscn Floating popups + log showing every signal
stress_test.tscn 20 characters, mass ragdoll, budget system
animated_npc.tscn Signal-driven NPC: patrol, hit, recover, resume
foot_ik_demo.tscn Foot IK on vs off over varied terrain — planting + anti-slide
euphoria_showcase.tscn Active resistance, stagger sway, pain, momentum, injuries

Collision Layers

Layer Purpose Used by
1 Environment (Godot's default layer) StaticBody3D (floors, walls); foot-IK and recovery ground rays
2 Projectiles / raycasts Demo ball-throw
3 Characters CharacterBody3D controllers
4 Active ragdoll bodies PhysicsRigBuilder
5 Godot built-in ragdoll bones (comparison demo only) PhysicalBoneSimulator3D

Layer names are set in project.godot; the constants live in KickbackLayers. Ragdoll bodies default to mask 15 (layers 1–4). Use KickbackRaycast, which targets the active-ragdoll layer (4) automatically.

Debug Tools

  • F3 — Cycle debug gizmos: Off → Dots → Wireframe + state → Full dashboard (panels, CoM, velocity)

    Debug gizmos — bone dots, wireframe, full dashboard with CoM and velocity vectors
  • Inspector — Select KickbackCharacter to see setup status and validation

  • Visible Collision Shapes (Debug menu) — See ragdoll collision shapes

License

MIT

About

Physics-based reactive characters for Godot 4.6+ (Euphoria-like hit reactions)

Topics

Resources

Contributing

Stars

30 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages