I've been attempting to make a system in which you can use an ability with 3 charges. Once you use one, Only that one will recharge, When you use another, The recharge progress from the first will go to the second one, etc. I, However, Have not been able to accomplish this.
Attempted: Using conditionals and awaits to create cooldowns based off of the amount of charges currently left
Result: Charges going above the maximum wanted amount when using charges before full recharge.
extends RigidBody2D
@export var RotationSpeed = 4.2
@export var JumpPower = 1000
@onready var jumpcount: int = 3
@onready var timerRunning: bool = false
var animationTime: float
var continue1: bool = true
var continue2: bool = true
func _ready():
Gwobaw.Player = self
$Sprite2D/GPUParticles2D.z_index = -5
$Sprite2D/GPUParticles2D.emitting = true
func Jump():
if(jumpcount > 0):
if $RayCast2D.is_colliding():
var enemy = $RayCast2D.get_collider()
enemy.Health -= 100
linear_velocity = -transform.y * JumpPower
$Sprite2D/GPUParticles2D.emitting = true
$Sprite2D/GPUParticles2D.z_index = -1
jumpcount -= 1
func _process(delta):
$CanvasLayer/Label.text = str(jumpcount)
if Input.is_action_pressed("ui_right"):
angular_velocity = RotationSpeed
if Input.is_action_pressed("ui_left"):
angular_velocity = -RotationSpeed
if Input.is_action_just_pressed("Boost"):
if(jumpcount == 3):
Jump()
await get_tree().create_timer(5).timeout
if(continue1 == true):
jumpcount -= 1
elif(jumpcount == 2):
Jump()
continue1 = false
await get_tree().create_timer(5).timeout
if(continue2 == true):
jumpcount += 1
await get_tree().create_timer(5).timeout
jumpcount += 1
continue1 = true
elif(jumpcount == 1):
continue2 = false
Jump()
await get_tree().create_timer(5).timeout
jumpcount += 1
await get_tree().create_timer(5).timeout
jumpcount += 1
await get_tree().create_timer(5).timeout
jumpcount += 1
continue2 = true
2 Answers 2
An alternative approach, that I often use, would be to do the bookkeeping manually with Time.get_ticks_msec() (or ..._usec() if you need it to be ultra precise).
get_ticks_msec returns how long your game has been running, in milliseconds, and maxes out at 500 million years (according to the docs). The basic idea here would be to record the time of your jump/boost, and use how long it has been since then to perform some logic like recharging in _process().
An implementation of this idea for your many-jumps mechanic could look like the following. I have set up a simple Control scene with 2 child labels, Label and Label2, for visualization. This should be very straight forward to port to your specific problem. What I've done is create an array, times_of_jumps, of length max_jumps, that keeps track of when last your 3 jumps were taken. Once a jump is ready, we change that value to a zero. This gives us the ability to then just look for a zero to decide if we can jump. In _process(), we can then check each nonzero number to see if it has been enough time to cool down.
extends Control
var max_jumps:int = 3
var jump_cooldown_ms:float = 1000
var times_of_jumps:Array = []
func _ready() -> void:
fill_jump_array()
displaystuff()
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_up"):
jump()
refill_jumps()
displaystuff()
func fill_jump_array() -> void:
for i in range(max_jumps):
times_of_jumps.append(0)
func displaystuff() -> void:
$Label.text = "Jump times: "+str(times_of_jumps)
var available = 0
for t in times_of_jumps:
if t == 0:
available+=1
$Label2.text = "Jumps remaining: "+str(available)
func jump() -> void:
for i in range(times_of_jumps.size()):
if times_of_jumps[i] < 1:
times_of_jumps[i] = Time.get_ticks_msec()
# put your non-time-related jumping logic here
displaystuff()
return
func refill_jumps() -> void:
for i in range(times_of_jumps.size()):
if times_of_jumps[i] != 0 and (Time.get_ticks_msec() - times_of_jumps[i]) > jump_cooldown_ms:
times_of_jumps[i] = 0
displaystuff()
This keeps an updated track on jump times, and solves the problem of having more than allowed jumps available. Plus it does so without any of the inherent ambiguity from await.
Comments
If the charges are identical (I suppose?), you could just do this:
extends RigidBody2D
@export var RotationSpeed = 4.2
@export var JumpPower = 1000
@onready var maxCharges:int = 3
@onready var jumpcount: int = 3
@onready var timerRunning: bool = false
var animationTime: float
var continue1: bool = true
var continue2: bool = true
var chargeCooldownTimer:Timer = Timer.new()
var chargeCooldownDuration:float = 5.0 # seconds
func _ready():
Gwobaw.Player = self
$Sprite2D/GPUParticles2D.z_index = -5
$Sprite2D/GPUParticles2D.emitting = true
chargeCooldownTimer.connect("timeout", self._on_recharge_jump)
chargeCooldownTimer.one_shot = true
func _on_recharge_jump():
if (jumpcount < maxCharges):
jumpcount += 1
# check if we need to increase charges
if (jumpcount >= maxCharges):
# we already have max amount of charges
chargeCooldownTimer.stop()
else:
# continue recharging
chargeCooldownTimer.start(chargeCooldownDuration)
func Jump():
if(jumpcount > 0):
if $RayCast2D.is_colliding():
var enemy = $RayCast2D.get_collider()
enemy.Health -= 100
linear_velocity = -transform.y * JumpPower
$Sprite2D/GPUParticles2D.emitting = true
$Sprite2D/GPUParticles2D.z_index = -1
jumpcount -= 1
func _process(delta):
$CanvasLayer/Label.text = str(jumpcount)
if Input.is_action_pressed("ui_right"):
angular_velocity = RotationSpeed
if Input.is_action_pressed("ui_left"):
angular_velocity = -RotationSpeed
if Input.is_action_just_pressed("Boost"):
if (jumpcount > 0):
Jump();
# if this is first jump, start recharging process
if (jumpcount == maxCharges-1):
chargeCooldownTimer.start(chargeCooldownDuration)
Explore related questions
See similar questions with these tags.