Skip to content

Instantly share code, notes, and snippets.

@majikayogames
Last active November 11, 2025 03:30
Show Gist options
  • Select an option

  • Save majikayogames/cf013c3091e9a313e322889332eca109 to your computer and use it in GitHub Desktop.

Select an option

Save majikayogames/cf013c3091e9a313e322889332eca109 to your computer and use it in GitHub Desktop.
Godot 4 CharacterBody3D to RigidBody3D Push Interaction Script
# CC0/public domain/use for whatever you want no need to credit
# Call this function directly before move_and_slide() on your CharacterBody3D script
func _push_away_rigid_bodies():
for i in get_slide_collision_count():
var c := get_slide_collision(i)
if c.get_collider() is RigidBody3D:
var push_dir = -c.get_normal()
# How much velocity the object needs to increase to match player velocity in the push direction
var velocity_diff_in_push_dir = self.velocity.dot(push_dir) - c.get_collider().linear_velocity.dot(push_dir)
# Only count velocity towards push dir, away from character
velocity_diff_in_push_dir = max(0., velocity_diff_in_push_dir)
# Objects with more mass than us should be harder to push. But doesn't really make sense to push faster than we are going
const MY_APPROX_MASS_KG = 80.0
var mass_ratio = min(1., MY_APPROX_MASS_KG / c.get_collider().mass)
# Optional add: Don't push object at all if it's 4x heavier or more
if mass_ratio < 0.25:
continue
# Don't push object from above/below
push_dir.y = 0
# 5.0 is a magic number, adjust to your needs
var push_force = mass_ratio * 5.0
c.get_collider().apply_impulse(push_dir * velocity_diff_in_push_dir * push_force, c.get_position() - c.get_collider().global_position)
@413x1nkp
Copy link

413x1nkp commented Jul 25, 2024

For anyone in need, here's a rough 1:1 translation to C#

	private void PushAwayRigidBodies() {
		for (int i = 0; i < GetSlideCollisionCount(); i++) {
			KinematicCollision3D CollisionData = GetSlideCollision(i);

			GodotObject UnkObj = CollisionData.GetCollider();
			
			if (UnkObj is RigidBody3D) {
				RigidBody3D Obj = UnkObj as RigidBody3D;

				// Objects with more mass than us should be harder to push.
				// But doesn't really make sense to push faster than we are going
				float MassRatio = Mathf.Min(1.0f, Mass / Obj.Mass);

				// Optional add: Don't push object at all if it's 4x heavier or more
				if (MassRatio < 0.25f) continue;

				Vector3 PushDir = -CollisionData.GetNormal();

				// How much velocity the object needs to increase to match player velocity in the push direction
				float VelocityDiffInPushDir = Velocity.Dot(PushDir) - Obj.LinearVelocity.Dot(PushDir);

				// Only count velocity towards push dir, away from character
				VelocityDiffInPushDir = Mathf.Max(0.0f, VelocityDiffInPushDir);

				PushDir.Y = 0; // Don't push object from above/below

				float PushForce = MassRatio * PushForceScalar;
				Obj.ApplyImpulse(PushDir * VelocityDiffInPushDir * PushForce, CollisionData.GetPosition() - Obj.GlobalPosition);
			}
		}
	}

I'm not an expert, this translation might not be perfect, but it does seem to work as intended.
NOTE: I moved the player's MASS and PushForceScalar's declarations to the public fields of the class, here:

public partial class Player : CharacterBody3D {

	// . . .

	[Export] public float Mass = 80.0f;

	[Export] public float PushForceScalar = 5.0f;

	// . . .

@t0qen
Copy link

t0qen commented Apr 7, 2025

Thank you !

@Vterebenin
Copy link

Vterebenin commented Aug 12, 2025

TY author, solved my issues.

I've translated this to godotrust if anyone needs it:

    #[func]
    fn _push_away_rigid_bodies(&mut self) {
        for slide_idx in 0..self.base().get_slide_collision_count() {
            let collision = self.base_mut().get_slide_collision(slide_idx).unwrap();
            let Some(rigid_body) = collision
                .get_collider()
                .map(|collider| collider.try_cast::<RigidBody3D>())
            else {
                continue;
            };
            if let Ok(mut rigid_body) = rigid_body {
                let mut push_dir = -collision.get_normal();
                let mut velocity_diff_in_push_dir = self.base().get_velocity().dot(push_dir)
                    - rigid_body.get_linear_velocity().dot(push_dir);
                velocity_diff_in_push_dir = velocity_diff_in_push_dir.max(0.);
                let my_mass_kg = 20.;
                let mass_ratio = (my_mass_kg / rigid_body.get_mass()).min(1.);
                if mass_ratio < 0.25 {
                    continue;
                }
                push_dir.y = 0.;
                let push_force = mass_ratio * 5.;
                rigid_body.apply_impulse(push_dir * velocity_diff_in_push_dir * push_force);
            }
        }
    }

also im no expert in rust, but this should work

@sanderfoobar
Copy link

Works quite well :-)

Thanks

2025-11-11.05-27-22.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment