Trying to spawn another ball as the current one exits screen

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

So I have two scripts making up a simple brick breaker game. The idea is that when the ball exits the screen, the counter gets “reset” back to 1, and I should be able to do a left mouse button click after that, and it should spawn a new ball. I had it working previously where I would spawn several balls at once before any of them exited the screen, but that’s not what I’m going for - just one ball on the screen at one time. I am following a tutorial from youtube, but this is just something extra that wasn’t covered and I am trying to get it to work.

Paddle script:

extends KinematicBody2D

const BALL_SCENE = preload("res://Mini Scenes/Ball.tscn")
var current_ball_count = 1
var ball = BALL_SCENE.instance()


func _ready():
	set_physics_process(true)
	set_process_input(true) #alows
	
func _input(event):
	if(current_ball_count == 1 && event is InputEventMouseButton && event.is_pressed()):
		current_ball_count += 1
		print("event triggered", current_ball_count)
		var ball = BALL_SCENE.instance()
		ball.set_position(get_position()-Vector2(0, 16))
		get_tree().get_root().add_child(ball)


func _physics_process(delta: float):
	var y = get_position().y
	var mouse_x = get_viewport().get_mouse_position().x - 320
	set_position(Vector2(mouse_x,y))
	
func check_if_dead():
	if ball.dead_ball == true:
		current_ball_count -= 1
		print(current_ball_count)

Ball script:

extends RigidBody2D

export var speedup = 4
const MAX_SPEED = 400
var dead_ball = false

func _ready() -> void:
	set_physics_process(true)
	
func _physics_process(delta: float):
	delta*100
	var bodies = get_colliding_bodies()
	var ball_amount = null
	
	for body in bodies:
		if body.is_in_group("Bricks"):
			get_node("/root/World")._score += 5
			body.queue_free()
			print("Brick Killed")
			
		if body.get_name() == "Paddle":
			var speed = get_linear_velocity().length()
			var direction = get_position() - body.get_node("Anchor").get_global_position()
			var velocity = direction.normalized() * min(speed+speedup, MAX_SPEED)
			set_linear_velocity(velocity)
			print(str(speed+speedup))
			
func _on_VisibilityNotifier2D_screen_exited():
	queue_free()
	dead_ball = true
	print(dead_ball, "Ball is dead")

The above scripts results in the following outputs:

event triggered2

TrueBall is dead

After the ball leaves the screen I am unable to click again to spawn a new ball (current_ball_count -= 1 at the bottom of paddle script seems to be getting ignored.)

Sorry if this is a bit much but I have been scratching my head for some time now, and trying different approaches. Perhaps I am simply approaching this the wrong way.Trying to wrap my head around all of this so any help would be great. Thank you!

current_ball_count -= 1 at the bottom of paddle script seems to be getting ignored

From the scripts you provided it looks like you’re never even calling check_if_dead?

njamster | 2020-04-27 12:21

I have tried calling it both in _input(event) and _ready() but the check_if_dead function still doesn’t seem to work.

I think I might scrap this logic and just have a title screen show after the ball leaves the screen, giving the player the option to start a new game.

eviking | 2020-04-27 13:46

:bust_in_silhouette: Reply From: njamster

Well, if you call it in _ready it’s called once when the scene starts. That obviously not enough! But even if you call it in _input (i.e. before each click is evaluated) you’re only keeping track of one ball, namely: the last one you instanced. So if you spawn a second ball and then the first one leaves the screen, the ball count will not be updated. Furthermore, your function assumes that the ball object still exists when it’s executed. However, that might not be the case because you free (i.e. delete) the balls once the leave the screen. Your script is simply not able to keep the ball count up to date!

Simply have each ball emit a signal once it leaves the screen:

signal screen_left()

func _on_VisibilityNotifier2D_screen_exited():
    emit_signal("screen_left")
    queue_free()

And connect this signal to a function keeping track of the ball count:

var current_ball_count = 0

func _ready():
    spawn_ball(Vector2(100, 100))
    spawn_ball(Vector2(200, 200))

func spawn_ball(at_position):
    var ball = BALL_SCENE.instance()
    ball.global_position = at_position
    ball.connect("screen_left", self, "_on_screen_left")
    get_tree().get_root().add_child(ball)
    current_ball_count += 1

func _on_screen_left():
    current_ball_count -= 1

This is perfect! Thank you so much. I have one question about signals - Are they just like functions where you can just create them and give them whatever name you want, and then connect them to wherever you wish? I assume you just created signal screen_left()?

eviking | 2020-04-27 21:22

I assume you just created signal screen_left()?

Correct. You can only emit signals from a script if they are defined in that script. Either explicitly (like I did with the line signal screen_left()) or implicitly (because a script extends a class and that class might define signals on its own). You can name a signal whatever you want, as long as the name isn’t already defined in the script. It’s also possible to pass arguments to a signal, if they were defined that way:

signal screen_left(position)

func _on_VisibilityNotifier2D_screen_exited():
    emit_signal("screen_left", self.global_position)

and then connect them to wherever you wish?

You don’t connect a signal to somewhere. You connect to a signal from somewhere. That’s the whole point: decoupling things! If you know the scene tree, you could also call the _on_screen_left-method directly from the ball scene without using a signal. However, that encodes two strong assumptions in your script: a) there is another node which implements a certain function _on_screen_left in its script and b) the path to reach that node won’t change. If either of those is violated, the script will stop working. Whereas, if you would have emitted a signal, that would continue to work, as it doesn’t concern a node which other nodes might be connecting to it.

njamster | 2020-04-28 10:29

This is great. Thanks so much. Your code provided worked as you explained.

I’ll definitely research more into signals as I’m really new to all of this. Any help goes a long way.

eviking | 2020-04-28 13:55