How do I create a timer in Godot? - timer

How do I create a timer in Godot which destroys the script's object after a given amount of time? I am looking to remove bullets from a game after a while to reduce lag.

There is a Timer node that you can use. You can add it as a child, set the wait time (in seconds) - you probably to set it as one shot and auto start - connect the "timeout" signal to your script, and on the method call queue_free to have the Node (and children, which includes the Timer) freed safely.
You can do that from code too, if that is what you prefer. So, let us go over what I just said, but instead of doing it from the editor, let us see the equivalent code:
Create a Timer, add it as a child:
var timer := Timer.new()
add_child(timer)
Set the wait time (in seconds):
timer.wait_time = 1.0
Set as oneshot:
timer.one_shot = true
Instead of setting it to auto start (which would be timer.autostart = true, let us start it:
timer.start()
Connect the "timeout" signal to a method. In this case, I'll call the method "_on_timer_timeout":
timer.connect("timeout", self, "_on_timer_timeout")
func _on_timer_timeout() -> void:
pass
Then in that method _on_timer_timeout, call queue_free:
timer.connect("timeout", self, "_on_timer_timeout")
func _on_timer_timeout() -> void:
queue_free()

You may want to use the SceneTreeTimer, like in the following code:
func die(delay: float):
yield(get_tree().create_timer(delay), "timeout")
queue_free()
Please refer to Godot Engine's documentation.

In Godot 4, there's an easier way to do this:
# Do some action
await get_tree().create_timer(1.0).timeout # waits for 1 second
# Do something afterwards
queue_free() # Deletes this node (self) at the end of the frame
However, if you do this in the _process() or _physics_process() functions, more timers get created every frame, which causes several runs to occur all at once before then running the following code. To handle this, simply track whether a timed event is happening.
Example in the _process() with simple attack logic:
var attack_started = false;
func _process(delta):
if attack_started:
print("Not attacking, attack code running in background")
return
else:
attack_started = true
prepare_attack()
await get_tree().create_timer(1.0).timeout # wait for 1 second
begin_attack()
attack_started = false
This await keyword works with everything that emits signals, including collision events!
FYI: yield was replaced with await in Godot 4, and await really just waits for a signal/callback to complete:
await object.signal
get_tree().create_timer(5.0) will create a timer that runs for 5 seconds, and then has a timeout callback/signal you can tap into.

Related

Godot: Getting a node results in Nil Value

i am trying to get into Godot. In my project i want to access a Timer called Duration Timer in the Dash Script attached to the Dash Node.
Screenshot - Godot get Duration Timer
Already, i tried 3 ways of accessing the Node seen in line 4,5 and 6 of the screenshot.
onready var duration_timer = $DurationTimer
#onready var duration_timer = get_node("DurationTimer")
#onready var duration_timer = get_node("Movement/Dash/DurationTimer")
All 3 lines result in the highlighted errormessage
"Invalid set index 'wait_time' (on base:
'Nil') with value of type 'int'.
The error appears in line 14 by trying to set a wait_time to the Timer element.
duration_timer.wait_time = 1
Does someone have a clue why i cant get the Timer element?
I don't know what is causing the problem.
I do not think get_node("DurationTimer") or $DurationTimer are wrong. But if that is the problem, we can solve it by using a NodePath:
export var duration_timer_path:NodePath
onready var duration_timer = get_node(duration_timer_path)
Then set the NodePath from the inspector panel.
If the path is not wrong. It could be that the code is running before _ready. or that something (I don't know what) removed the timer or set it to null. You can add an breakpoint and check on the remote tab of the scene tree panel when the game is running from the editor, to see if the timer is there.
We can sidestep the problem by making a new timer if we don't have one. For example:
if not is_instance_valid(duration_timer):
duration_timer = Timer.new()
duration_timer.name = "DurationTimer"
add_child(duration_timer)
# use duration_timer here
However, if the timer is there but we had failed to get it, we would have two timers in the scene tree. You could also check in the remote tab of the scene tree panel if this resulted in two timers.
So, let us try to get the node again, in case we failed the first time. And let us put it all in a method. Something like this:
func get_duration_timer() -> Timer:
if is_instance_valid(duration_timer):
return duration_timer
duration_timer = get_node("DurationTimer") # or use a NodePath
if is_instance_valid(duration_timer):
return duration_timer
duration_timer = Timer.new()
duration_timer.name = "DurationTimer"
add_child(duration_timer)
return duration_timer
In fact, if we are in this scenario, then onready is not helping us. So we could have duration_timer defined without onready and without initialization. And always use the method to get the timer.
If we are going to always use the method, why not let the method add the timer instead of adding it in the scene tree? We could then remove the step of trying to get it:
func get_duration_timer() -> Timer:
if is_instance_valid(duration_timer):
return duration_timer
duration_timer = Timer.new()
duration_timer.name = "DurationTimer"
add_child(duration_timer)
return duration_timer
What we have here is a lazy initialization pattern: The timer is created when we first need it, on demand, not before.

What does the error "Exceeded maximum allowed number of Trackers" mean?

I'm following this tutorial to implement object tracking on iOS 11. I'm able to track objects perfectly, until a certain point, then this error appears in the console.
Throws: Error Domain=com.apple.vis Code=9 "Internal error: Exceeded maximum allowed number of Trackers for a tracker type: VNObjectTrackerType" UserInfo={NSLocalizedDescription=Internal error: Exceeded maximum allowed number of Trackers for a tracker type: VNObjectTrackerType}
Am I using the API incorrectly, or perhaps Vision has trouble handling too many consecutive object tracking tasks? Curious if anyone has insight into why this is happening.
It appears that you hit the limit on the number of trackers that can be active in the system. First thing to note is that a new tracker is created every time a new observation, with new -uuid property is used. You should be recycling the initial observation you use when you started the tracker, until you no longer want to use it, by feeding what you got from “results” for time T into the subsequent request you make for time T+1. When you no longer want to use that tracker (maybe the confidence score gets too low), there is a “lastFrame” property that can be set, which lets the Vision framework know that you are done with that tracker. Trackers also get released when the sequence request handler is released.
To track the rectangle you feed consequent observations to the same VNSequenceRequestHandler instance, say, handler. When the rectangle is lost, i.e. the new observation is nil in your handler function / callback, or you are getting some other tracking error, just re-instantiate the handler and continue, e.g. (sample code to show the idea):
private var handler = VNSequenceRequestHandler()
// <...>
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard
let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
let lastObservation = self.lastObservation
else {
self.handler = VNSequenceRequestHandler()
return
}
let request = VNTrackObjectRequest(detectedObjectObservation: lastObservation, completionHandler: self.handleVisionRequestUpdate)
request.trackingLevel = .accurate
do {
try self.handler.perform([request], on: pixelBuffer)
} catch {
print("Throws: \(error)")
}
}
Note that handler is var, not a constant.
Also, you may re-instantiate the handler in actual handler function (like func handleVisionRequestUpdate(_ request: VNRequest, error: Error?)) in case new observation object is invalid.
My problem with this was that I had a function that called perform... on the same VNSequenceRequestHandler that the tracking was also calling perform on, because of that I was processing too many try self.visionSequenceHandler.perform(trackRequests, on: ciimage) concurrently. Make sure the VNSequenceRequestHandler is not getting hit at the same time by multiple performs....

How to implement a resetable countdown timer with a GenServer in Elixir or Erlang

How do we implement a reset-able countdown timer with a GenServer?
1) perform a task after fixed amount of time, say every 60 seconds
2) have a way to reset the countdown back to 60 seconds before the timer elapses
I have looked at How to perform actions periodically with Erlang's gen_server? but it doesn't quite cover the aspect of resting the timer before the countdown elapses.
Thanks.
How can I schedule code to run every few hours in Elixir or Phoenix framework? defines how to do a periodic job.
To implement a cancel using that as a base you could do the following:
defmodule MyApp.Periodically do
use GenServer
def start_link(_name \\ nil) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def reset_timer() do
GenServer.call(__MODULE__, :reset_timer)
end
def init(state) do
timer = Process.send_after(self(), :work, 60_000)
{:ok, %{timer: timer}}
end
def handle_call(:reset_timer, _from, %{timer: timer}) do
:timer.cancel(timer)
timer = Process.send_after(self(), :work, 60_000)
{:reply, :ok, %{timer: timer}}
end
def handle_info(:work, state) do
# Do the work you desire here
# Start the timer again
timer = Process.send_after(self(), :work,60_000)
{:noreply, %{timer: timer}}
end
# So that unhanded messages don't error
def handle_info(_, state) do
{:ok, state}
end
end
This maintains a reference to the timer, allowing it to be cancelled. Every time the :work message is received, a new timer is created and stored in the state of the GenServer.
If you did it in Erlang, as per the other question you referenced...
You save the timer reference and call erlang:cancel_timer/1 to stop it from firing (if it hasn't already). You have to watch out for this 'fired already' race condition, where the trigger message is already in your message queue when you cancel the timer. You might or might not care about this, but if it's important that you never carry out the action after the trigger has been cancelled then you need to create a reference (or you could use a counter) when you setup a timed message, and when you get a trigger you have to check that it relates to the latest one.
The code from the other question then becomes:
-define(INTERVAL, 60000). % One minute
init(Args) ->
...
% Start first timer
MyRef = erlang:make_ref(),
{ok, TRef} = erlang:send_after(?INTERVAL, self(), {trigger, MyRef}),
...
{ok, State#your_record{
timer = TRef,
latest = MyRef
}}.
% Trigger only when the reference in the trigger message is the same as in State
handle_info({trigger, MyRef}, State = #your_record{latest=MyRef}) ->
% Do the action
...
% Start next timer
MyRef = erlang:make_ref(),
{ok, TRef} = erlang:send_after(?INTERVAL, self(), trigger),
...
{ok, State#your_record{
timer = TRef,
latest = MyRef
}}.
% Ignore this trigger, it has been superceeded!
handle_info({trigger, _OldRef}, State) ->
{ok, State}.
And something like this to reset the timer:
handle_info(reset, State = #your_record{timer=TRef}) ->
% Cancel old timer
erlang:cancel_timer(TRef),
% Start next timer
MyNewRef = erlang:make_ref(),
{ok, NewTRef} = erlang:send_after(?INTERVAL, self(), trigger),
{ok, State#your_record{
timer = NewTRef,
latest = MyNewRef
}}.
A call might be more appropriate for the cancel function, but that depends on your app so it's up to you.
Technically, it's not necessary to cancel the timer, because once you've created a new state with your new reference, even if the old timer carries on, it'll be ignored when it fires, but I think it's best to tidy up really.

Is there a way to schedule a task at a specific time or with an interval?

Is there a way to run a task in rust, a thread at best, at a specific time or in an interval again and again?
So that I can run my function every 5 minutes or every day at 12 o'clock.
In Java there is the TimerTask, so I'm searching for something like that.
You can use Timer::periodic to create a channel that gets sent a message at regular intervals, e.g.
use std::old_io::Timer;
let mut timer = Timer::new().unwrap();
let ticks = timer.periodic(Duration::minutes(5));
for _ in ticks.iter() {
your_function();
}
Receiver::iter blocks, waiting for the next message, and those messages are 5 minutes apart, so the body of the for loop is run at those regular intervals. NB. this will use a whole thread for that single function, but I believe one can generalise to any fixed number of functions with different intervals by creating multiple timer channels and using select! to work out which function should execute next.
I'm fairly sure that running every day at a specified time, correctly, isn't possible with the current standard library. E.g. using a simple Timer::periodic(Duration::days(1)) won't handle the system clock changing, e.g. when the user moves timezones, or goes in/out of daylight savings.
For the latest Rust nightly-version:
use std::old_io::Timer;
use std::time::Duration;
let mut timer1 = Timer::new().unwrap();
let mut timer2 = Timer::new().unwrap();
let tick1 = timer1.periodic(Duration::seconds(1));
let tick2 = timer2.periodic(Duration::seconds(3));
loop {
select! {
_ = tick1.recv() => do_something1(),
_ = tick2.recv() => do_something2()
}
}

erlang timer gets timeout

I have an erlang module with behaviour gen_server.
Now, I have:
init(_Args) ->
erlang:send_after(?PROCESS_STATE_INTERVAL,self(),processState),
{ok, []}.
and
handle_info(processState, _State)->
{ok, NewState} = gen_server:call(self(), {updateLvls}), %works fine, tested
timer:send_after(?PROCESS_STATE_INTERVAL,self(),processState),
{noreply, NewState}.
When I start it with something like {ok, Test}=gen_server:start_link({local,challenge_manager},challenge_manager,[],[]). after a few seconds I get ** exception error: {timeout,{gen_server,call,[<0.329.0>,{updateLvls}]}}
Am I doing something wrong??
You cannot call your own gen_server from within itself. That will result in a dead lock (which is what you see). The server process is busy handling your first request (since you haven't returned yet) and will queue the second request (which is made from the handling of the first), thus dead lock.
To solve this, either create a library function which both handle_call and handle_info uses, or take a look at the reply/2 function which will let you do asynchronous replies (if you return {noreply, ...} from your handle_call function).

Resources