body_entered signal is not triggering consistently

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By KJSav

I’m new to Godot (and game development in general), and I’m trying to set up a simple game where the player can hit a ball up into the air. Everytime the player hits the ball, a score ticks up and an impulse is applied to the ball. Everything is working, but the method for triggering the body_entered signal in my ball.gd script doesn’t fire consistently. Here is the signal function in my ball.gd script:

func _on_body_entered(body):
	# Check if the collided body is the character
	print("Body entered: " + body.name)
	if body.is_in_group("Hitter"):
		print("Character Entered")
		# Calculate the direction from the character to the ball
		var direction = (global_position - body.global_position).normalized()
		
		# Define the impulse force
		var impulse_force = ((body.velocity.length() + base_impulse) * impulse_modifier)
		print(impulse_force)
		
		# Apply the impulse force to the ball in the calculated direction
		apply_central_impulse(direction * impulse_force)
		
		# Increment the score variable by 1
		score += 1
		print("Score: ", score)
		emit_signal("score_changed", score)

This function is properly called 80% of the time. It is consistently called less when the ball is close to the ground, or the character hits the ball at the side. But that’s not always so, as occasionally those sorts of hits do work. No errors are showing up in the debugger. I really have no leads as to the bug, so I decided to post. Any help would be much appreciated!

Problem is likely in physics configuration of these objects, not necessarily code. Could be a few things, although most like fast (ish) moving objects and/or collision shape sizes.

Troubleshooting physics issues — Godot Engine (stable) documentation in English

Try continuous collision detection first.

spaceyjase | 2023-05-14 17:17

Thanks for your reply. I tried a couple of the solutions on the page you linked (turned on CCD on the ball, made sure my collisions weren’t scaled, and double checked distance from world origin). Nothing has worked. My game is just 2D right now, and my two shapes are just circles. Here is the rest of my ball.gd script, in case this context matters:

extends RigidBody2D

# Exported variables
@export var custom_gravity_scale = .6
@export var custom_linear_damping = 0
@export var custom_angular_damping = 0
@export var custom_bounce = 20

#Custom Signals
signal score_changed(new_score)

#Variables
var score = 0
var velocity_modifier = .3
var impulse_modifier = .8
var base_impulse = 50

# Called when the node enters the scene tree for the first time.
func _ready():
	# Set the physics properties
	print("Object Ready")
	self.gravity_scale = custom_gravity_scale
	self.linear_damp = custom_linear_damping
	self.angular_damp = custom_angular_damping
	
	# Create a new PhysicsMaterial with the specified bounce value
	var physics_material = PhysicsMaterial.new()
	physics_material.bounce = custom_bounce

	# Assign the PhysicsMaterial to the Ball node
	self.physics_material_override = physics_material
	
	


func _on_body_entered(body):
	# Check if the collided body is the character
	print("Body entered: " + body.name)
	if body.is_in_group("Hitter"):
		print("Character Entered")
		# Calculate the direction from the character to the ball
		var direction = (global_position - body.global_position).normalized()
		
		# Define the impulse force
		var impulse_force = ((body.velocity.length() * velocity_modifier) + base_impulse) * impulse_modifier
		print(impulse_force)
		
		# Apply the impulse force to the ball in the calculated direction
		apply_central_impulse(direction * impulse_force)
		
		# Increment the score variable by 1
		score += 1
		print("Score: ", score)
		emit_signal("score_changed", score)

KJSav | 2023-05-15 05:16

Seems like you’re on the right track, checked all the things… how are you moving the ‘player’, could the movement code be fighting with physics?

spaceyjase | 2023-05-15 09:37

Thank you for your time! Here is my code for the character.gd:

extends CharacterBody2D

# Exported variables
@export var speed = 400
@export var max_jump_force = 6000
@export var gravity = 5000
@export var ground_friction = 4
@export var min_ground_time = 0.2
@export var max_mouse_hold_time = 1


# Internal variables
var time_since_on_ground = 0.0
var mouse_hold_time = 0.0

# Add a new variable to store the AnimationPlayer reference
@onready var animation_player = $AnimationPlayer

func _process(delta):
	# Update mouse_hold_time if the mouse button is being held
	if Input.is_action_pressed("mouse_click") and time_since_on_ground >= min_ground_time:
		mouse_hold_time += delta
		# Start animation if it's not already playing
		if animation_player.current_animation != "Jump_Anim":
			animation_player.play("Jump_Anim")
	else:
		mouse_hold_time = 0.0

func _physics_process(delta):
	# Update the time_since_on_ground variable
	if is_on_floor():
		time_since_on_ground += delta
	else:
		time_since_on_ground = 0.0

	# Check for mouse click release and if the character has been on the ground long enough
	if Input.is_action_just_released("mouse_click") and time_since_on_ground >= min_ground_time:
		# Calculate the jump force based on the mouse hold time, capped by max_mouse_hold_time
		var jump_force = max_jump_force * min(mouse_hold_time / max_mouse_hold_time, 1.0)
		# print("Jump force value:", jump_force)

		# Get the global mouse position
		var mouse_position = get_global_mouse_position()

		# Calculate the direction from the character to the mouse position
		var direction = (mouse_position - global_position).normalized()

		# Calculate the jump velocity based on the direction and adjusted jump force
		velocity = direction * jump_force
		
		# Play the extending animation
		animation_player.rever("Jump_Anim")
		
		# Reset the ducking animation to its first frame
		animation_player.stop(true)

	# Apply gravity
	velocity.y += gravity * delta

	# Apply ground friction
	if is_on_floor():
		velocity.x -= velocity.x * ground_friction * delta

	# Apply movement
	move_and_slide()

You can ignore the animation stuff. I’m still working on that. I turned off the animation and I was still having issues with the ‘body entered’ not triggering so I don’t think it’s that. I’m using a mouse click to have the character jump. The character has to be on the ground in order to jump. It might be something here. I’d be curious if you notice anything that might be affecting the ‘body entered’ triggering consistently.

KJSav | 2023-05-15 15:53

Hello, I’m having the same issue in my game in godot 4.2.2. Was a solution found? Would someone be interested in seeing a simplified version of my project?