New to Godot - Playing sound on a collision

Godot Version

4.2.2 (Stable)

Question

Hi,
I’m new to Godot and have tried a simple project which has gone well up to this point, I have a simple breakout clone (Arkanoid), I have managed to get a sound effect playing when the ball hits the paddle and when the ball hits a brick, I would like a sound effect to play when the ball hits a wall.

When i use
print(collider)
I can see the names of the walls the ball is hitting (UpperWall, RightWall and LeftWall) unfortunately these walls do not exist in the current scope, how can I reference these walls in my ball script?

LeftWall:<RigidBody2D#40030438871>
UpperWall:<RigidBody2D#40097547739>
RightWall:<RigidBody2D#39963330003>

I have a main scene which has 3 instances of my wall scene, each are named up as UpperWall, LeftWall and Rightwall
image

Currently i’m using an if statement to check the what the collider is eg

if (collider is Brick):
			$Brick_Hit.play()

If i remove Brick to let the IDE show me the known objects I can see my Paddle and Brick but the walls are not listed.

Any help would be greatly appreciated
Thanks

how do you do this? show the code for this

detecting a wall should be pretty easy, something like this will do:

for i in get_slide_collision_count():
	var collision = get_slide_collision(i)
	if collision.get_collider() is RigidBody2D:
		if collision.get_collider().is_in_group("wall"):
			$Brick_Hit.play()

remember to assign the Left, Right, Upper Walls in wall group

put above code in _physics_process(delta): of Ball

1 Like

Thanks for the reply zdrmlpzdrmlp,

My ball.tscn has 3 audio stream player nodes (one for each sound effect)
image

My ball script has the following code to ascertain what the ball has collided with and play the relevant sound effect.

func _physics_process(delta):
var collision = move_and_collide(velocity * ball_speed * delta)
if (!collision):
return

var collider = collision.get_collider()
print(collider) #debug only prints the object it had collided with
if collider is Brick:
collider.decrease_level()
if (collider is Brick or collider is Paddle):
ball_collision(collider)
if (collider is Paddle):
$Paddle_Bounce.play()
if (collider is Brick):
$Brick_Hit.play()
else:
velocity = velocity.bounce(collision.get_normal())

I can detect the wall with
print(collider) and the console shows the UpperWall, LeftWall and Rightwall strings when the ball collides with them.

I tried your method of creating a group called “walls” from my main.tscn my _physics_process(delta) of ball.gd now looks like this

func _physics_process(delta):
	var collision = move_and_collide(velocity * ball_speed * delta)
	if (!collision):
		return
	for i in get_slide_collision_count():
		var collision2 = get_slide_collision(i)
		if collision2.get_collider() is RigidBody2D:
			if collision2.get_collider().is_in_group("walls"):
				print("Wall Hit")
				$Brick_Hit.play()	
	var collider = collision.get_collider()
	print(collider) #debug only prints the object it had collided with
	if collider is Brick:
		collider.decrease_level()
	if (collider is Brick or collider is Paddle):
		ball_collision(collider)
		if (collider is Paddle):
			$Paddle_Bounce.play()
		if (collider is Brick):
			$Brick_Hit.play()
		
	else:
		velocity = velocity.bounce(collision.get_normal())

I had to rename the variable “collision” to “collision2” due to the variable collision being in use already

I stuck a breakpoint at for i in get_slide_collision_count():
once it stops here after stepping through it skips over
this whole section

	for i in get_slide_collision_count():
		var collision2 = get_slide_collision(i)
		if collision2.get_collider() is RigidBody2D:
			if collision2.get_collider().is_in_group("walls"):
				print("Wall Hit")
				$Brick_Hit.play()

Thanks

since you are using move_and_collide, then what you do is correct, to detect by just .get_collider()

my collision code is for move_and_slide, so it’s not gonna fit well with what you have now with move_and_collide()

i see you use Paddle and Brick, is that the class_name of the paddle.gd and the walls.gd?

wont this just add the

$Brick_Hit.play()

it will play, no?

Hi zdrmlpzdrmlp,
Paddle is the class name for my ball paddle and Brick is the class name for an individual brick (which lives in brick.gd and brick.tscn)

I have a wall.tscn which contains a rigidbody2d , collisionshape2d and a texturerect node, in my main.tscn i have imported the wall.tscn 3 times and renamed each wall instance the following UpperWall, RightWall and LeftWall, in my ball script printing the collider shows the ball collision names of each wall.

When the ball hits a brick a different sound effect does indeed play (on every collider.decrease_level() )

Thanks

the wall.tscn’s script has a class_name of Brick?

No,
The walls.tscn script seems to be default

extends Node

# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass

My brick.gd script has a classname of Brick

extends RigidBody2D

class_name Brick

signal brick_destroyed

var level = 1

@onready var sprite_2d = $Sprite2D
@onready var collision_shape_2d = $CollisionShape2D

var sprites: Array[Texture2D] = [
	#level 0 brick - 1 life
	preload("res://Assets/Graphics/element_yellow_rectangle_glossy.png"),
	#level 1 brick - 2 lives
	preload("res://Assets/Graphics/element_blue_rectangle_glossy.png"),
	#level 2 brick - 3 lives
	preload("res://Assets/Graphics/element_purple_rectangle_glossy.png"),
	#level 3 brick - 4 lives
	preload("res://Assets/Graphics/element_green_rectangle_glossy.png"),
	#level 4 brick - 5 lives
	preload("res://Assets/Graphics/element_grey_rectangle_glossy.png"),
	#level 5 brick - 6 lives
	preload("res://Assets/Graphics/element_red_rectangle_glossy.png")
]

func get_size():
	return collision_shape_2d.shape.get_rect().size
	

func set_level(new_level: int):
	level = new_level
	sprite_2d.texture = sprites[new_level - 1]
	
func decrease_level():
	if level > 1:
		set_level(level - 1)
	else: 
		fade_out()
		
		
func fade_out():
	collision_shape_2d.disabled = true
	var tween = get_tree().create_tween()
	tween.tween_property(sprite_2d, "modulate", Color.TRANSPARENT, .5)
	tween.tween_callback(destroy)
	
func destroy():
	queue_free()
	brick_destroyed.emit()
	
func get_width():
	return get_size().x

and the brick.gd is attached to the base node of UpperWall, RightWall and LeftWall or not?

Good morning zdrmlpzdrmlp,
I have just uploaded it to github, please could you have a look? (might be easier)

Thankyou!

hi thanks for sharing the project
here what i tested
image
there indeed should have the sound be playing now, i dont change anything

ok i forgot you asked for the wall to have sound, let me try do that now

1 Like

Add a wall Script and Attach the script to the Wall node in wall scene:

in the ball.gd script:
change to this:

extends CharacterBody2D

class_name Ball

signal life_lost


const VELOCITY_LIMIT = 40


@export var ball_speed = 15
@export var lifes = 3
@export var death_zone: DeathZone
@export var ui: UI

var speed_up_factor = 1.05
var start_position: Vector2
var last_collider_id
# Define the sound effect
var hitSound = load("res://Assets/sound/ball_bounce.wav")
@onready var collision_shape_2d = $CollisionShape2D




func _ready():
	ui.set_lifes(lifes)
	start_position = position
	death_zone.life_lost.connect(on_life_lost)

func _physics_process(delta):
	var collision = move_and_collide(velocity * ball_speed * delta)
	if (!collision):
		return
		
	var collider = collision.get_collider()
	print(collider) #debug only prints the object it had collided with
	if collider is Brick:
		collider.decrease_level()
	if (collider is Brick or collider is Paddle):
		ball_collision(collider)
		if (collider is Paddle):
			$Paddle_Bounce.play()
		if (collider is Brick):
			$Brick_Hit.play()
	else:
		velocity = velocity.bounce(collision.get_normal())
	if collider is Wall:
		$Wall_Hit.play()
		
	
func start_ball():
	position = start_position
	randomize()
	
	velocity = Vector2(randf_range(-1, 1), randf_range(-.1, -1)).normalized() * ball_speed

func on_life_lost():
	lifes -= 1
	if lifes == 0:
		ui.game_over()
	else:
		life_lost.emit()
		reset_ball()
		ui.set_lifes(lifes)

func reset_ball():
	position = start_position
	velocity = Vector2.ZERO

func ball_collision(collider):
	
	var ball_width = collision_shape_2d.shape.get_rect().size.x
	var ball_center_x = position.x
	print(collider.get_width())
	var collider_width = collider.get_width()
	var collider_center_x = collider.position.x
	
	var velocity_xy = velocity.length()
	
	var collision_x = (ball_center_x - collider_center_x) / (collider_width / 2)
	
	var new_velocity = Vector2.ZERO
	
	new_velocity.x = velocity_xy * collision_x
	
	if collider.get_rid() == last_collider_id && collider is Brick:
		new_velocity.x = new_velocity.rotated(deg_to_rad(randf_range(-45, 45))).x * 10
	else: 
		last_collider_id == collider.get_rid()
	
	new_velocity.y = sqrt(absf(velocity_xy* velocity_xy - new_velocity.x * new_velocity.x)) * (-1 if velocity.y > 0 else 1)
	var speed_multiplier = speed_up_factor if collider is Paddle else 1
	
	velocity = (new_velocity * speed_multiplier).limit_length(VELOCITY_LIMIT)

zdrmlpzdrmlp,
Thankyou!
That worked perfectly, thankyou for taking the time to help me as well, after rereading the above solution it now makes sense logically.

The issue I had was a simple one… I had forgot to attach the script to the wall scene :frowning: , I became too focused on the fact the wall script existed that I just forgot to check it was actually attached.

Following your steps fixed this for me!

1 Like