Issues with instantiating a child and signals

Godot Version

4.2.1

Question

I am tinkering on a little pong clone and have been stuck trying to implement my idea of acceleration physics. So whats happening is that the ball has 3 variables: speed, acceleration and acceleration increment. The physics that accelerate the ball look like this:

velocity = direction * speed * delta
speed += acceleration
acceleration += acceleration_increment

What i want to do now is to reset acceleration back to its original value (1.0) whenever the upper or bottom wall of the screen is touched. But there seems to be an issue whenever the ball respawns from flying out of bounds left or right, as it suddenly doesnt reset acceleration anymore. The code looks like this:

func _on_top_body_entered(body):
body.direction.y *= -1
body_touched.emit()

(… repeat for bottom body)

func _on_left_body_entered(body):
body.queue_free()
var e = preload(“res://scenes/ball.tscn”).instantiate()
e.global_position = Vector2(640, 360)
add_child(e)
p2_score += 1

(…repeat for right body)

The body_touched signal is sent to the ball.gd script where it then assigns acceleration to 1.0.
As i said, this works while the ball has not been re-added via the on_left/right_body_entered function in the main script.

I assume this is because the child added by this function is not identical to the ball.tscn that the signal is sent to, which means: The respawned ball.tscn doesnt receive the signal to reset its acceleration.

I dont really know how to fix this issue and any help would be appreciated.

Can you please let us know which nodes the scripts are attached to? What’s your scene tree hierarchy?

I assume this is because the child added by this function is not identical to the ball.tscn that the signal is sent to

This is likely the case, and the solution will depend on the answer to the above.

Screenshot 2023-12-18 230435

The relevant nodes are Ball and Main

In your Main script you could connect a function responsible for spawning a new ball to the body_touched signal. It sounds like you may be giving the Ball node responsibility to creating a new ball at the moment?

var ball_scene = preload(“res://scenes/ball.tscn”)

@onready var top_wall: Area2D = $Top
@onready var bottom_wall: Area2D = $Bottom

func _ready() -> void:
  top_wall.body_entered.connect(_on_body_entered_wall)
  bottom_wall.body_entered.connect(_on_body_entered_wall)
  
func _on_body_entered_wall(ball: CharacterBody2D) -> void:
  ball.queue_free()
  _spawn_ball()

func _spawn_ball() -> void:
  var ball = ball_scene.instantiate()
  ball.global_position = Vector2(640, 360)
  add_child(e)

You’ll also need to check that the body entering the wall isn’t a player paddle this way however.

An alternative could be to emit a signal from the ball when it “died” that the Main script is connected to. It then spawns the new ball.

Hmm, no, the ball node is not involved in creating a new ball. It’s all handled via the on_left/right_body_entered function

func _on_left_body_entered(body):
body.queue_free()
var e = preload(“res://scenes/ball.tscn”).instantiate()
e.global_position = Vector2(640, 360)
add_child(e)

I’m pretty new to godot so i have to wrap my head around what you’ve proposed there.
This is like its own exercise for me haha. Before rewriting my entire code, i would maybe hope to try to find out where exactly my mistake lies in the current approach.

Just to clarify my issue a bit further, and i do apologize if i’m a bit convoluted in how i display my information, as this is the first time i ever asked for advice regarding “coding”, but here’s an example of what happens when the ball is respawned:


Instead of there being a ball node, there is a @CharacterBody2D node that is an instance of the ball.tscn
When this ball leaves the field again, it respawns and is now shown again as Ball in the scene tree, except it still doesnt reset the acceleration variable.

I feel like this may be relevant to my issue?

Can you please share the contents of your Ball and Main scripts?

Of course, i appreciate the effort to help

main.gd

extends Node2D

var side = 'p1'
var p1_score = 0
var p2_score = 0
signal body_touched

func _process(delta):
	$Label.text = str(p1_score)
	$Label2.text = str(p2_score)

func _on_top_body_entered(body):
	body.direction.y *= -1
	body_touched.emit()
	
func _on_bottom_body_entered(body):
	body.direction.y *= -1
	body_touched.emit()


func _on_left_body_entered(body):
	body.queue_free()
	var e = preload("res://scenes/ball.tscn").instantiate()
	e.global_position = Vector2(640, 360)
	add_child(e)
	p2_score += 1


func _on_right_body_entered(body):
	body.queue_free()
	var e = preload("res://scenes/ball.tscn").instantiate()
	e.global_position = Vector2(640, 360)
	add_child(e)
	p1_score += 1



ball.gd

extends CharacterBody2D

var speed = 25000.0
var direction = Vector2.ZERO
var acceleration = 1.0  # Starting acceleration
var acceleration_increment = 0.1  # The amount acceleration increases each frame while in the air

func _ready():
	direction.y = [1].pick_random()
	direction.x = [1, -1].pick_random()


func _physics_process(delta):
	if direction:
		velocity = direction * speed * delta
		speed += acceleration
		acceleration += acceleration_increment
	
		print(acceleration)
	
	else:
		velocity = velocity.move_toward(Vector2.ZERO, speed)

		
	move_and_slide()


func _on_main_body_touched():
	acceleration = 1.0

I’ve formatted the code. For the future, you can add syntax highlighting by putting the code between ``` lines like this:

```gdscript
func _ready():
    print("hello world")
```

then the forum will render it with syntax highlighting like this:

func _ready():
    print("hello world")

Can you upload your project anywhere for us to look at? It might make it easier to fix this :slight_smile:

In the _ready() you are giving direction a value different from zero, to it will start to increase acceleration the moment the physics_process loop starts. When the ball is spawned again, does it start acceleration from 1.0 or from the last value from the previou ball scene?

You mentioned that when the ball is respawned the first time with a different name (CharacterBody2D@2). This means that when the ball is respawned, the previous ball wasn’t freed yet. Then when spawned for a second time, it is created with the “Ball” name. I think you might be having issues with the object being freed. You can try to separate freeing the ball object from instantiating.

Create a signal “ball_removed” in the ball scene and connect it with the main script. Emit it after queue_free and then, in the function called from this ball_removed signal, instantiate the new ball. For example:

# not sure about the order of events, you might need to
# emit the signal before queue_free the body
func _on_left_body_entered(body):
	body.queue_free()
	body.ball_removed.emit()

# you might need to add a parameter to know which side 
# has scored and then a if to add point to the correct score
func _on_ball_removed(): 
	e.global_position = Vector2(640, 360)
	add_child(e)
	p2_score += 1