And this is the player’s code in full:
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
# 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.
# 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
if Input.is_action_pressed("move_left"):
$AnimatedSprite2D.flip_h = true
if Input.is_action_pressed("move_right"):
$AnimatedSprite2D.flip_h = false
if (Input.is_action_pressed("jump") and jumptimertimedout == true):
var 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
position = position.clamp(Vector2.ZERO, screen_size)
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 SnailPowerup:
body.queue_free()
Globals.slowedtime = 1
$SlowDownTimer.start()
if body is ShieldPowerup:
body.queue_free()
Globals.playerhasshield = true
That is as I asked, one key difference is this line
if (Input.is_action_pressed("jump") and jumptimertimedout == true):
# was: var jump_tween := create_tween()
jump_tween = create_tween()
jump_tween.tween_property(self, "position", position + Vector2(0, -40), 0.5).set_trans(Tween.TRANS_QUAD)
jumptimertimedout = false
We remove var
and the colon from the previous jump_tween, otherwise it still makes a new variable and does not use the global one we just made. You might have gotten a “Shadowing” warning from this.
Oh, that makes sense - when I declare the global variable there’s no need for the old one anymore
It works great for the most part, except after I lose a life and restart I can’t jump anymore - why is that?
It’s weird - it works correctly on this particular time I run the program, but it’s like sometimes it won’t jump randomly.
I think it might be because I am in mid tween when I die when it stops working?
1 Like
Which shouldn’t be the cause, because the tween kill code you gave me should mean the tween gets deleted.
There’s also the line about “await tween finished then set jumpertimedout to true” however, so I think that’s probably what’s wrong - the tween never finishes, so jumpertimedout is still false and the condition is never met.
That oversight can be fixed pronto.
1 Like
Totally right, I forgot about that await
won’t finish. Might have to also manually set jumptimertimedout = true
after using jump_tween.kill()
That’s what I did already, maybe not in that exact order but I discovered that part of the solution myself.
Now the game works flawlessly, after I have added the snail powerups to their own group and did the same logic on the snail group as the mob group so the game resets 100% when you die.
1 Like
My next plan is to add score as you progress in the game, like how if you are at a thorn’s horizontal location/lined up with it add points, then add an extra score powerup and an extra life powerup before finishing off the game with sound, music and more polished animations for dying and getting a game over.
Okay, so now I want to do it this way for adding 1 score point when you are lined up horizontally with a mob/thorn:
extends Node2D
@export var mob_scene: PackedScene
@export var snailpowerup_scene: PackedScene
@export var shieldpowerup_scene: PackedScene
@onready var shield: Shield = $Shield
@onready var player: Player = $Player
@onready var player_rabbit: Player_Rabbit = $Player_Rabbit
var score: int
var leftorright: int
var flipped: float
# Called when the node enters the scene tree for the first time.
func _ready():
get_tree().call_group("mobs", "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))
$MobSpawnTimer.start()
$ProgressTimer.start()
$SnailTimer.start()
$ShieldTimer.start()
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
if player.x == mob.x:
score += 1
$HUD.update_score(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))
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))
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))
if Globals.playerhasshield == true:
$Shield.show()
shield.position = player.position
if Globals.playerhasshield == false:
$Shield.hide()
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()
player_rabbit.queue_free()
game_over()
func game_over():
$HUD.show_game_over()
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 = 0
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")
Globals.lives -= 1
if player.jump_tween.is_valid():
player.jump_tween.kill()
player.position.x = 160
player.position.y = 288
if player_rabbit.jump_tween.is_valid():
player_rabbit.jump_tween.kill()
player_rabbit.position.x = 196
player_rabbit.position.y = 318
player.jumptimertimedout = true
player_rabbit.jumptimertimedout = true
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")
Globals.lives -= 1
if player.jump_tween.is_valid():
player.jump_tween.kill()
player.position.x = 160
player.position.y = 288
player.jumptimertimedout = true
player_rabbit.jumptimertimedout = true
if player_rabbit.jump_tween.is_valid():
player_rabbit.jump_tween.kill()
player_rabbit.position.x = 196
player_rabbit.position.y = 318
Unfortunately, I am told “identifier mob not declared in the current scope” doing it this way, so I tried instantiating the mob at the very top of the code with the other script-wide variables but got a crash that I couldn’t instantiate a null value, and I also know it wouldn’ t be good to update the score only when the mob timer has timed out, I want it to update every single time the player passes through the thorns.
Does anyone know the appropriate place in the code to put the “add 1 to score”?
I also know I can’t just instantiate a mob in the same way I can for the player, because it’s not a child of the scene tree as you had me remove it from being a child of the scene tree very specifically earlier.
1 Like
I think adding another area 2d to the mob would be easiest, when the player enters the area above or below the mob they get a point.
I was actually thinking of that - that makes the most sense.
The only catch is how do I make the area not be on the mob also, or is that not possible in the Godot Engine and I can only make rectangle areas and can’t delete parts of them?
1 Like
I suppose if I really, really wanted to precise about not touching the mob and earning a point still (because you shouldn’t earn a point for dying), I could make two areas rather than one, one above and one below the hit box
A new Area2D within the Mob. You could give the “PointZone” area2d a script and use body is PointZone
or just filter by name body.name == "PointZone"
Yeah, I get that, but the problem is if I use one area that checks both above and below the mob and I am limited to only making perfect rectangles without cutting any parts out, then that means the rectangle will also be right on the mob itself and I don’t want to give points for touching a mob and getting killed.
So that’s why I think it would be a smarter move to do two areas rather than one
Hi everyone,
In my latest upload I demonstrate that I followed the directions about adding a new Area2D within the Mob Area2D, and used the “filter by name” idea, but weirdly I get the error about “Could not find type “Mob_BottomArea”” and “Could not find type “Mob_TopArea”” in the current scope, even though I defined the areas and the collision capsules inside them in the same way as the original Mob.
Is there something else I am missing or doing wrong?
I figured it out - it was because I didn’t attach a script defining them as classes first.