Need Help with Timer Not Acting Properly

In particular, the compiler cannot find the property “height” on base “Vector2”.

Vector2 has an X and Y component. Sprite height you might be better off eyeballing it, the real code looks like var height = $Sprite2D.get_rect().size.y

Oh, that makes a lot more sense, just like position has an X and Y component cause I think it’s also a Vector2

1 Like

Okay, so after I change the height to y on the screen size it doesn’t give the red highlight error anymore, but when I run the program I get a crash that $AnimatedSprite2D.height is an invalid index, so what’s the proper index I should use?

For the animated sprite, and not the screen_size

I would make an educated guess it has to do with the scale and position, but can’t quite put my finger on it…

Oh wait, you actually gave the solution earlier to the sprite thing

Okay, here is the code in full for Player.gd now:

extends Area2D
class_name Player
signal hit

@export var speed: float = 400 # how fast the player will move in pixels per second.
var jump_tween: Tween
var screen_size: Vector2 # size of the game window.
var jumptimertimedout: bool = true
var sprite_height: Vector2 # Get height of sprite object

# Called when the node enters the scene tree for the first time.
func _ready():
	screen_size = get_viewport_rect().size # Get size of viewport rectangle.
	sprite_height = $Sprite2D.get_rect().size.y

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	
	position.y += 120 * delta
	
	$AnimatedSprite2D.play()	
	$AnimatedSprite2D.animation = "fly"
	$AnimatedSprite2D.flip_v = false
	$Sprite2D.flip_v = false
	
	if Input.is_action_pressed("move_left"):
		$AnimatedSprite2D.flip_h = true
		$Sprite2D.flip_h = true
		$Sprite2D.position.x = 0
	if Input.is_action_pressed("move_right"):
		$AnimatedSprite2D.flip_h = false
		$Sprite2D.flip_h = false
		$Sprite2D.position.x = 30
	if (Input.is_action_pressed("jump") and jumptimertimedout == true):
		jump_tween = create_tween()
		jump_tween.tween_property(self, "position", position + Vector2(0, -40), 0.5).set_trans(Tween.TRANS_QUAD)
		jumptimertimedout = false
		await jump_tween.finished	
		jumptimertimedout = true
		
	if position.y < 0:
		position.y = 0
		
	if position.y + sprite_height > screen_size.y:
		position.y = screen_size.y - sprite_height
	
func start(pos):
	position = pos
	show()
	$CollisionShape2D.disabled = true

func _on_area_entered(body) -> void:
	if body is Mob:
		if Globals.playerhasshield == false:
			hit.emit()
		if Globals.playerhasshield == true:
			body.queue_free()
			Globals.playerhasshield = false
	if body is ScorePowerup:
		body.queue_free()
		Globals.score += 10
	if body is LifePowerup:
		body.queue_free()
		Globals.lives += 1
	if body is ShieldPowerup:
		body.queue_free()
		Globals.playerhasshield = true
	if body is SnailPowerup:
		body.queue_free()
		Globals.slowedtime = true
		$SlowDownTimer.start()

I get “Invalid operands “float” and “Vector2” for “+” operator.” even though I thought both things are of the same type, namely that they are both referencing the y coordinate only of a Vector2.

What should I type instead?

I decided if it wasn’t going to translate the variable properly, I could eliminate the placeholder variable entirely and just use what the variable represents directly - now I have no red highlights.

And now it works perfectly! Hallelujah.

Now all I have to do is add the final touches and my game will be ready for all to play.

1 Like
var sprite_height: Vector2 # Get height of sprite object

Maybe this line should be : float instead? though you are setting the variables correctly, I feel like the error should be earlier and different.

Super!

Yeah, I figured with the comment feature I don’t need to use placeholder variables because I can explain using comments what the long-winded version means if anyone reads the code and wants to know.

Okay, this is the code after I added some for playing music and sound effects in gamenode.gd:

extends Node2D

@export var mob_scene: PackedScene
@export var snailpowerup_scene: PackedScene
@export var shieldpowerup_scene: PackedScene
@export var scorepowerup_scene: PackedScene
@export var lifepowerup_scene: PackedScene
@onready var shield: Shield = $Shield
@onready var player: Player = $Player
var leftorright: int
var flipped: float

# Called when the node enters the scene tree for the first time.
func _ready():
	$Music.play()
	get_tree().call_group("mobs", "queue_free")
	get_tree().call_group("snails", "queue_free")
	get_tree().call_group("shields", "queue_free")
	get_tree().call_group("scores", "queue_free")
	get_tree().call_group("lives", "queue_free")
	leftorright = Globals.leftorright
	$Shield.hide()
	$MobSpawnTimer.set_wait_time(randf_range(6.0, 8.0)) # Set mob spawn timer to random value between 1 and 4.
	$ProgressTimer.set_wait_time(5.0)
	$SnailTimer.set_wait_time(randf_range(4.0, 10.0))
	$ShieldTimer.set_wait_time(randf_range(10.0, 20.0))
	$ScoreTimer.set_wait_time(randf_range(6.0, 15.0))
	$LifeTimer.set_wait_time(randf_range(15.0, 40.0))
	$MobSpawnTimer.start()
	$ProgressTimer.start()
	$SnailTimer.start()
	$ShieldTimer.start()
	$ScoreTimer.start()
	$LifeTimer.start()

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	$HUD.update_score(Globals.score)
	if Globals.progress_stage == 5:
		$MobSpawnTimer.set_wait_time(randf_range(4.0, 5.0))
		$SnailTimer.set_wait_time(randf_range(5.0, 8.0))
		$ShieldTimer.set_wait_time(randf_range(10.0, 15.0))
		$ScoreTimer.set_wait_time(randf_range(6.0, 10.0))
		$LifeTimer.set_wait_time(randf_range(15.0, 30.0))
	if Globals.progress_stage == 10:
		$MobSpawnTimer.set_wait_time(randf_range(2.0, 4.0))
		$SnailTimer.set_wait_time(randf_range(3.0, 5.0))
		$ShieldTimer.set_wait_time(randf_range(9.0, 12.0))
		$ScoreTimer.set_wait_time(randf_range(3.0, 6.0))
		$LifeTimer.set_wait_time(randf_range(12.0, 24.0))
	if Globals.progress_stage == 15:
		$MobSpawnTimer.set_wait_time(1.0)
		$SnailTimer.set_wait_time(randf_range(1.0, 3.0))
		$ShieldTimer.set_wait_time(randf_range(6.0, 10.0))
		$ScoreTimer.set_wait_time(randf_range(2.0, 4.0))
		$LifeTimer.set_wait_time(randf_range(9.0, 18.0))
	if Globals.playerhasshield == true:
		$Shield.show()
		shield.position = player.position
	if Globals.playerhasshield == false:
		$Shield.hide()
	if Globals.lives >= 3:
		Globals.lives = 3
	if Globals.lives == 3:
		$HUD.get_node("Life1").show()
		$HUD.get_node("Life2").show()
		$HUD.get_node("Life3").show()
	if Globals.lives == 2:
		$HUD.get_node("Life1").show()
		$HUD.get_node("Life2").show()
		$HUD.get_node("Life3").hide()
	if Globals.lives == 1:
		$HUD.get_node("Life1").show()
		$HUD.get_node("Life2").hide()
		$HUD.get_node("Life3").hide()
	if Globals.lives == 0:
		$HUD.get_node("Life1").hide()
		$HUD.get_node("Life2").hide()
		$HUD.get_node("Life3").hide()
		player.queue_free()
		game_over()


func game_over():
	$HUD.show_game_over()
	$Music.stop()

func _on_mob_spawn_timer_timeout():
	if Globals.leftorright == 1:
		flipped = randf_range(0.0, 2.0)
		
		var mob = mob_scene.instantiate()
	
		mob.position.x = 1110
		
		if flipped < 1.0:
			mob.rotation = 0.0
			mob.position.y = randf_range(560, 600)
			
		if flipped >= 1.0:
			mob.rotation = 270.0
			mob.position.y = randf_range(15, 45)
	
		add_child(mob)
		
		
func _on_progress_timer_timeout():
	if Globals.leftorright == 1:
		Globals.progress_stage += 1


func _on_snail_timer_timeout():
	if Globals.leftorright == 1:
		var snailpowerup = snailpowerup_scene.instantiate()
		
		snailpowerup.position.x = 1110
		
		snailpowerup.position.y = randf_range(50, 540)
	
		add_child(snailpowerup)


func _on_slow_down_timer_timeout():
	Globals.slowedtime = false


func _on_shield_timer_timeout():
	if Globals.leftorright == 1:
		var shieldpowerup = shieldpowerup_scene.instantiate()
		
		shieldpowerup.position.x = 1110
		
		shieldpowerup.position.y = randf_range(20, 500)
	
		add_child(shieldpowerup)


func _on_player_area_entered(body) -> void:
	if body is Mob:
		get_tree().call_group("mobs", "queue_free")
		get_tree().call_group("snails", "queue_free")
		get_tree().call_group("shields", "queue_free")
		get_tree().call_group("scores", "queue_free")
		get_tree().call_group("lives", "queue_free")
		Globals.slowedtime = false
		$Player/SlowDownTimer.stop()
		Globals.lives -= 1
		if player.jump_tween.is_valid():
			player.jump_tween.kill()
		player.position.x = 160
		player.position.y = 288
		$DeathSound.play()
		
		player.jumptimertimedout = true
		
	if body is Mob_BottomArea:
		Globals.score += 1
		body.queue_free()
		
	if body is Mob_TopArea:
		Globals.score += 1
		body.queue_free()
		
func _on_player_rabbit_area_entered(body) -> void:
	if body is Mob:
		get_tree().call_group("mobs", "queue_free")
		get_tree().call_group("snails", "queue_free")
		get_tree().call_group("shields", "queue_free")
		get_tree().call_group("scores", "queue_free")
		get_tree().call_group("lives", "queue_free")
		Globals.slowedtime = false
		$Player/SlowDownTimer.stop()
		Globals.lives -= 1
		if player.jump_tween.is_valid():
			player.jump_tween.kill()
		player.position.x = 160
		player.position.y = 288
		$DeathSound.play()
		player.jumptimertimedout = true
		
	if body is Mob_BottomArea:
		Globals.score += 1
		body.queue_free()
		
	if body is Mob_TopArea:
		Globals.score += 1
		body.queue_free()
		
	if body is SnailPowerup:
		$PowerupSound.play()
		
	if body is ShieldPowerup:
		$PowerupSound.play()
		
	if body is ScorePowerup:
		$PowerupSound.play()
		
	if body is LifePowerup:
		$PowerupSound.play()


func _on_score_timer_timeout():
	if Globals.leftorright == 1:
		var scorepowerup = scorepowerup_scene.instantiate()
		
		scorepowerup.position.x = 1110
		
		scorepowerup.position.y = randf_range(15, 430)
	
		add_child(scorepowerup)


func _on_life_timer_timeout():
	if Globals.leftorright == 1:
		var lifepowerup = lifepowerup_scene.instantiate()
		
		lifepowerup.position.x = 1110
		
		lifepowerup.position.y = randf_range(60, 310)
	
		add_child(lifepowerup)

It works good at playing music and the death sound, but it doesn’t play the powerup sound even though I made sure to test it actually plays in the scene editor, and it also crashes that “player.queue_free()” is a call to something that has been previously freed, even though I don’t think there is any way that could happen in the latest code I have for everything else.

The demonstration of the crash and how it doesn’t play the sounds is at the very first post per usual.

Okay, I figured out by myself how to make the sound for the powerup play correctly - it was because I had the script only on the rabbit collision and not the player, and on top of that the rabbit is obsolete after the change so I deleted that code now.

However, it still doesn’t explain why the game engine crashes and thinks the player body is already freed when I can’t find a specific call to free it anywhere else in the code after triple checking… does anyone have any ideas?

you are doing player.queue_free() in the _process function, it’s going to try deleting the player every frame. This will fail after frame 2 since the player doesn’t exist, can’t delete nothing.

A good way to resolve this would be to move your life counter/game over checks only when health is reduced

Oh, that makes sense - although it is true it is nowhere else in the code, it is also true that cause it is called every frame and it will try deleting it every frame, which won’t work after the first time.

Now it works perfectly! Thank you so much!

My final touch before publishing the game officially is to add the transition/tween to the game over screen, as well as a fade to darkness when you get a game over and display your high score, to make the game look more presentable/polished.

Okay, so I tried adding the tween to the HUD in the same way you told me to do for the player, like this:

extends CanvasLayer

var gameover_tween: Tween

func show_message(text):
	$Message.text = text
	$Message.show()
	
func show_game_over():
	show_message("Game Over")
	gameover_tween = create_tween()
	gameover_tween.tween_property(self, "position", position + Vector2(0, -40), 0.5).set_trans(Tween.TRANS_QUAD)
	
func update_score(score):
	$ScoreLabel.text = str(Globals.score)
# Called when the node enters the scene tree for the first time.

However, I am told “identifier “position” not declared in current scope.” I am assuming this is because the CanvasLayer doesn’t have a position property, while the Area2D does?

1 Like

If I am getting a thumbs up because my answer is correct, does anyone know the proper thing I should use in place of the position?

I tried location instead, but that was incorrect - what do you guys know is the correct answer? Looking it up in the API for CanvasLayer doesn’t give any clues, sadly.