Godot Version
Godot 4.3
Question
I’ve been following the Squash the creeps 3d tutorial and I’ve encountered an issue with detecting colliders in groups.
Specifically, the player isn’t detecting when it lands on top of a mob.
I think I’ve narrowed it down to one block of code:
# If the collider is with a mob
# BUG: for some reason this is never true
if collision.get_collider().is_in_group("mob"):
var mob = collision.get_collider()
# we check that we are hitting it from above.
if Vector3.UP.dot(collision.get_normal()) > 0.1:
# If so, we squash it and bounce.
mob.squash()
target_velocity.y = bounce_impulse
# Prevent further duplicate calls.
break
No matter what I seem to do I can’t get that block of code to run; it never seems to pass the first if statement.
I’ve checked all the layers and masks, and they’re all defined the same as the tutorial. I’ve also made sure the root node of mob.tscn is part of the “mob” group (case sensitive). I’ve also tried swapping the mob group between scene and global group types to no success.
I know the colliders themselves are working because when I turn on the player mask for the mobs, the player and mobs collide with each other, even if the if statement still doesn’t pass.
As an interesting note, the player still collides with the ground even when I disable the world mask on the player node. I don’t know if that’s relevant to the current problem, I just thought it was worth mentioning.
I’ll copy all the code for both the player and the mob here, even if it should be almost completely identical to what’s in the tutorial.
Player:
extends CharacterBody3D
# How fast the player moves in meters per second.
@export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
@export var fall_acceleration = 75
# The vertical impulse that is applied when jumping
@export var jump_impulse = 20
# same as jump but from the bounce instead
@export var bounce_impulse = 16
var target_velocity = Vector3.ZERO
func _physics_process(delta):
# We create a local variable to store the input direction.
var direction = Vector3.ZERO
# We check for each move input and update the direction accordingly.
if Input.is_action_pressed("move_right"):
direction.x += 1
if Input.is_action_pressed("move_left"):
direction.x -= 1
if Input.is_action_pressed("move_back"):
# Notice how we are working with the vector's x and z axes.
# In 3D, the XZ plane is the ground plane.
direction.z += 1
if Input.is_action_pressed("move_forward"):
direction.z -= 1
# Checks if were going in a direction and then normalizes the vector
if direction != Vector3.ZERO:
direction = direction.normalized()
# Setting the basis property will affect the rotation of the node.
$Pivot.basis = Basis.looking_at(direction)
# Ground Velocity
target_velocity.x = direction.x * speed
target_velocity.z = direction.z * speed
# Vertical Velocity
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
# Jumping
if is_on_floor() and Input.is_action_just_pressed("jump"):
target_velocity.y = jump_impulse
# Iterate through all collisions that occurred this frame
for index in range(get_slide_collision_count()):
# We get one of the collisions with the player
var collision = get_slide_collision(index)
# If the collision is with ground
if collision.get_collider() == null:
continue
# If the collider is with a mob
# BUG: for some reason this is never true
print(collision.get_collider().get_groups())
if collision.get_collider().is_in_group("mob"):
var mob = collision.get_collider()
# we check that we are hitting it from above.
if Vector3.UP.dot(collision.get_normal()) > 0.1:
# If so, we squash it and bounce.
mob.squash()
target_velocity.y = bounce_impulse
# Prevent further duplicate calls.
break
# Moving the Character
velocity = target_velocity
move_and_slide()
Mob:
extends CharacterBody3D
# Minimum speed of the mob in meters per second.
@export var min_speed = 10
# Maximum speed of the mob in meters per second.
@export var max_speed = 18
# to tell other scripts an enemy died
signal squashed
func _physics_process(_delta):
move_and_slide()
# This function will be called from the Main scene.
func initialize(start_position, player_position):
# We position the mob by placing it at start_position
# and rotate it towards player_position, so it looks at the player.
player_position.y = start_position.y
look_at_from_position(start_position, player_position, Vector3.UP)
# Rotate this mob randomly within range of -45 and +45 degrees,
# so that it doesn't move directly towards the player.
rotate_y(randf_range(-PI / 4, PI / 4))
# We calculate a random speed (integer)
var random_speed = randi_range(min_speed, max_speed)
# We calculate a forward velocity that represents the speed.
velocity = Vector3.FORWARD * random_speed
# We then rotate the velocity vector based on the mob's Y rotation
# in order to move in the direction the mob is looking.
velocity = velocity.rotated(Vector3.UP, rotation.y)
func _on_visible_on_screen_notifier_3d_screen_exited():
queue_free()
func squash():
squashed.emit()
queue_free()