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
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.
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?
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.