Player needs to collide with enemy but not push it

Godot Version

v4.5

Question

I have a large enemy, let's call it the boss, which can move towards the player, and our player which can also move.

I need the player to effectively be pushed by the enemy if it they collide (think monster hunter dynamics where a tail can push you around if the monster spins).

My setup based on prior research is as such

Layers

Layer 1: Level

Layer 2: Player

Layer 3: Enemy

Layer 4: EnemyPhysical (static)

Mask Setup

Player → CharacterBody3D, Layer 2 (Player), Mask 1 (level) and 4 (EnemyPhysical)

Boss → CharacterBody3D, Layer 3 (Enemy), Mask 1 (level)

The boss also has a StaticBody3D as it’s child, Layer 4 (Enemy Physical), Mask 2 (Player)

So in theory the player should interact with the static body in layer 4, but instead it walks right through the boss.

My collision shapes are correct and have been double checked.

Additionally I have a RigidBody3D object in the scene acting as a crate/box; this is pushed by the enemy exactly as I envision. Just not the player.

I hope that this is explained in an understandable manner, please let me know if I need to elaborate on any points!

Many thanks,

Ben

Additional Info:

Movement is done using MoveAndSlide in ProcessPhysics, not a separate translation system.

I think the PlayerBlocker should be a RigidBody3D with “freeze” checked and “freeze mode” set to kinematic, and not a static body (since it has to move around with the enemy, so it’s not really static).

Unfortunately this yields the same result. The player passes straight through.

As far as i know there is no function implemented, you need to write your own.

Check for the collision (i guess it is already triggered if you have set everything right) then
get the movement vector of the tail (in your example) and add this to the player velocity.
For sure you want to implement a “push” so you need to add some amount to the velocity, maybe a falloff or something, block the player input for a given time after collision etc.

I remembered there’s another type of physics object in godot called AnimatableBody3D, which, if I understand it correctly, is supposed to work as a “static” body that can move around, if that makes sense.

The documentation describes it as “A 3D physics body that can’t be moved by external forces. When moved manually, it affects other bodies in its path.”

So try to change the PlayerBlocker node type from StaticBody3D to AnimatableBody3D. Not sure if it will work but worth the shot.

I can definitely fall back to a method like this, it’s not ideal though when dealing with physics engines in my experience. It’s a shame there’s no simple out of the box solution for this.

AnimateableBody3D doesn’t seem to do the job either unfortunately!

That’s weird… Even if the enemy is standing still and you walk up to it, the player goes through it?

Correct, that’s what’s really bugging me with this!

If you copy that staticbody or animatablebody outside of the enemy scene (place it directly in the level) does the player collide with it then?

Amarc, this is not intended with move_and_slide.
But Savvy666861 you got the point, move_and_slide is just a simple approach.

see the docs: “The move_and_slide() method is intended to simplify the collision response in the common case where you want one body to slide along the other. It is especially useful in platformers or top-down games, for example.”

I guess you need to go for Rigidbody, for your enemy and your player. There you have all the options a physic engine has, mass, force etc.
Lucky you, there was already someone take care about, see this:

You problem is that your Player and Enemy objects are not on the same layers, and that’s why your player is walking right through. CharacterBody3D objects can, in certain cases, be pushed through StaticBody3D objects even when they should collide. This happens when they all have the same physics priority, and say an AnimateableBody3D platform pushes the CharacterBody3D against a StaticBody3D wall every frame. This is likely why you’re having the problems you are having.

So let’s take a look at what’s changing and modify it.

Original Setup

Player Layers: 2
Player Masks: 1, 4

Enemy Layers: 3
Enemy Masks: 1

StaticBody3D Layers: 4
StaticBody3D Masks: 2

First step, simplify this. Get rid of Layer 4. There is no reason to add this layer in. Next, get rid of the StaticBody3D. It’s, as you’ve seen from your discussion so far, overcomplicating things trying to solve this with an extra Body. Then you have a few options for layers and masks.

Option 1

Players and Enemies detect each other.

Player Layers: 2
Player Masks: 1, 3

Enemy Layers: 3
Enemy Masks: 1, 2

Option 2

Players and Enemies are also on the Environment (Level) layer.

Player Layers: 1, 2
Player Masks: 1

Enemy Layers: 1, 3
Enemy Masks: 1

Pick one that appeals to you.

Then handle the pushing mechanic in code. Enemy code:

class_name Enemy extends CharacterBody3D

@export var push_force = 2.0

func _physics_process(delta):
	# Your existing enemy code here.
	move_and_slide()

	var push_force = 2.0
	
	for i in get_slide_collision_count():
		var collision = get_slide_collision(i)
		if collision.get_collider() is CharacterBody3D: #You could detect Player instead if you don't want to push other enemies
			var collider = collision.get_collider()
			# Push in the direction of the collision normal
			var push_dir = -collision.get_normal()
			collider.velocity += push_dir * push_force

Turning your CharacterBody3D objects into RigidBody3D objects is going to cause you all sorts of other issues.

This is likely my best approach for my use case, they were currently not on matching layers in an attempt to make one touch the other but no the reverse, but the reverse force is something I will try.

As noted previously by @andi_godot, this would also allow for a kind of “push me out of the way” system rather than a “glued to the movement” effect.

I suppose this system could also allow for force to be a factor, potentially launching the player back if significant force is applied.

As a minor addition to this, one of the problems causing the player not to collide with the enemy was that I had copied the original collider from the player to the enemy without making it unique (or duplicated the scenes in some way) meaning that they were ‘sharing’ a collider. I think this caused them to never see one another as collision targets.

Sharing a CollisionShape3D was not the issue. I do it all the time.

1 Like