Skip to content

Add a 3D ragdoll physics demo #1234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions 3d/ragdoll_physics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Ragdoll Physics

This demo includes an example of ragdoll simulation for characters.

The ragdoll setup in this demo was performed by following the [Ragdoll system](https://docs.godotengine.org/en/stable/tutorials/physics/ragdoll_system.html)
tutorial. The character script also has an `initial_velocity` variable implemented,
which will give an impulse to all bones part of the ragdoll system.
This initial impulse is typically used in games to make ragdoll effects more impactful,
e.g. giving additional recoil after taking a punch.

Impact sounds are played according to the impact speed, and are played with
reduced pitch when slow motion mode is engaged. This uses
`AudioServer.playback_speed_scale` which affects all audio,
so in a more complex project, you would want to use
[audio buses](https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html)
which can make the effect only apply to certain sounds. This can be used to keep
music unaffected by the pitch shifting effect.

Character models use outlines provided by the BaseMaterial3D **Stencil > Mode**
property. The scene's static geometry is designed using CSG nodes and is baked to a
static mesh and collision to improve load times and allow for global illumination
with LightmapGI.

Controls:

- <kbd>Space</kbd>: Add a ragdoll at the mouse cursor position
- <kbd>Shift</kbd> (hold): Enable slow motion mode (1/4 speed)
- <kbd>R</kbd>: Reset ragdoll simulation and remove user-placed ragdolls
- <kbd>Right Mouse Button</kbd>: Orbit camera
- <kbd>Mouse Wheel</kbd>: Zoom

Language: GDScript

Renderer: Forward+

## Screenshots

![Screenshot](screenshots/ragdoll_physics.webp)
7 changes: 7 additions & 0 deletions 3d/ragdoll_physics/characters/mannequiny.LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# License for `mannequiny.glb`

Copyright (c) 2020 GDQuest and contributors (https://www.gdquest.com/)

Licensed under CC BY 4.0 International

Downloaded from <https://github.com/gdquest-demos/godot-3d-mannequin/issues/19#issuecomment-582235299>.
Binary file added 3d/ragdoll_physics/characters/mannequiny.glb
Binary file not shown.
60 changes: 60 additions & 0 deletions 3d/ragdoll_physics/characters/mannequiny.glb.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
[remap]

importer="scene"
importer_version=1
type="PackedScene"
uid="uid://s42h0rnuf2hf"
path="res://.godot/imported/mannequiny.glb-c63c284f6fd6b41bb34b1c5bea848ecd.scn"

[deps]

source_file="res://characters/mannequiny.glb"
dest_files=["res://.godot/imported/mannequiny.glb-c63c284f6fd6b41bb34b1c5bea848ecd.scn"]

[params]

nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=3
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={
"materials": {
"Azul_COLOR_0": {
"use_external/enabled": true,
"use_external/fallback_path": "res://materials/blue.tres",
"use_external/path": "uid://ch6ctajgm6nyy"
},
"Blanco_COLOR_0": {
"use_external/enabled": true,
"use_external/fallback_path": "res://materials/white.tres",
"use_external/path": "uid://dyij7l6ir0ixa"
},
"Negro_COLOR_0": {
"use_external/enabled": true,
"use_external/fallback_path": "res://materials/black.tres",
"use_external/path": "uid://brpxmsr7g2gh6"
}
}
}
gltf/naming_version=2
gltf/embedded_image_handling=1
37 changes: 37 additions & 0 deletions 3d/ragdoll_physics/characters/mannequiny_ragdoll.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
extends Node3D

const IMPACT_SOUND_SPEED_SMALL = 0.3
const IMPACT_SOUND_SPEED_BIG = 1.0

## The velocity to apply on the first physics frame.
@export var initial_velocity: Vector3

var has_applied_initial_velocity := false

# Used to play an impact sound on sudden velocity changes.
# We use the pelvis bone as it's close to the center of mass of the character model.
# For more detailed impact sounds, you could place multiple AudioStreamPlayer nodes as a child
# of each limb.
var previous_pelvis_speed := 0.0

@onready var pelvis := $"root/root_001/Skeleton3D/PhysicalBoneSimulator3D/Physical Bone pelvis"


func _ready() -> void:
$root/root_001/Skeleton3D/PhysicalBoneSimulator3D.physical_bones_start_simulation()
if not initial_velocity.is_zero_approx():
for physical_bone in $root/root_001/Skeleton3D/PhysicalBoneSimulator3D.get_children():
# Give the ragdoll an initial motion by applying velocity on all its bones upon being spawned.
physical_bone.apply_central_impulse(initial_velocity)


func _physics_process(_delta: float) -> void:
var pelvis_speed: float = pelvis.linear_velocity.length()
# Ensure the speed used to determine the threshold doesn't change with time scale.
var impact_speed := (previous_pelvis_speed - pelvis_speed) / Engine.time_scale
if impact_speed > IMPACT_SOUND_SPEED_BIG:
$ImpactSoundBig.play()
elif impact_speed > IMPACT_SOUND_SPEED_SMALL:
$ImpactSoundSmall.play()

previous_pelvis_speed = pelvis_speed
1 change: 1 addition & 0 deletions 3d/ragdoll_physics/characters/mannequiny_ragdoll.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://bvbikscp01gbc
Loading