Skip to content

dodge-the-creeps: use native Rust signals for timers #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions dodge-the-creeps/godot/Main.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,4 @@ stream = ExtResource("5")
[node name="DeathSound" type="AudioStreamPlayer" parent="."]
stream = ExtResource("6")

[connection signal="timeout" from="MobTimer" to="." method="on_mob_timer_timeout"]
[connection signal="timeout" from="ScoreTimer" to="." method="on_score_timer_timeout"]
[connection signal="timeout" from="StartTimer" to="." method="on_start_timer_timeout"]
57 changes: 42 additions & 15 deletions dodge-the-creeps/rust/src/main_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,35 @@ impl INode for Main {
.signals()
.start_game()
.connect_obj(&main, Self::new_game);

// Connect Main.ScoreTimer::timeout -> Main::on_score_timer_timeout.
self.score_timer()
.signals()
.timeout()
.connect_obj(&main, Self::on_score_timer_timeout);

// Connect Main.MobTimer::timeout -> Main::on_mob_timer_timeout.
self.mob_timer()
.signals()
.timeout()
.connect_obj(&main, Self::on_mob_timer_timeout);

// Main.StartTimer::timeout -> Main::on_start_timer_timeout is set up in the Editor's Inspector UI, but could be done here as well,
// as follows. Note that signal handlers connected via Rust do not need a #[func] annotation, they can remain entirely visible to Godot.
//
// self.start_timer()
// .signals()
// .timeout()
// .connect_obj(&main, Self::on_start_timer_timeout);
}
}

#[godot_api]
impl Main {
// No #[func] here, this method is directly called from Rust (via type-safe signals).
fn game_over(&mut self) {
let mut score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
let mut mob_timer = self.base().get_node_as::<Timer>("MobTimer");

score_timer.stop();
mob_timer.stop();
self.score_timer().stop();
self.mob_timer().stop();

self.hud.bind_mut().show_game_over();

Expand All @@ -75,12 +92,11 @@ impl Main {
// No #[func].
pub fn new_game(&mut self) {
let start_position = self.base().get_node_as::<Marker2D>("StartPosition");
let mut start_timer = self.base().get_node_as::<Timer>("StartTimer");

self.score = 0;

self.player.bind_mut().start(start_position.get_position());
start_timer.start();
self.start_timer().start();

let hud = self.hud.bind_mut();
hud.update_score(self.score);
Expand All @@ -89,22 +105,20 @@ impl Main {
self.music.play();
}

#[func]
fn on_start_timer_timeout(&self) {
let mut mob_timer = self.base().get_node_as::<Timer>("MobTimer");
let mut score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
mob_timer.start();
score_timer.start();
#[func] // needed because connected in Editor UI (see ready).
fn on_start_timer_timeout(&mut self) {
self.mob_timer().start();
self.score_timer().start();
}

#[func]
// No #[func], connected in pure Rust.
fn on_score_timer_timeout(&mut self) {
self.score += 1;

self.hud.bind_mut().update_score(self.score);
}

#[func]
// No #[func], connected in pure Rust.
fn on_mob_timer_timeout(&mut self) {
let mut mob_spawn_location = self
.base()
Expand Down Expand Up @@ -134,4 +148,17 @@ impl Main {

mob.set_linear_velocity(Vector2::new(range, 0.0).rotated(real::from_f32(direction)));
}

// These timers could also be stored as OnReady fields, but are now fetched via function for demonstration purposes.
fn start_timer(&self) -> Gd<Timer> {
self.base().get_node_as::<Timer>("StartTimer")
}

fn score_timer(&self) -> Gd<Timer> {
self.base().get_node_as::<Timer>("ScoreTimer")
}

fn mob_timer(&self) -> Gd<Timer> {
self.base().get_node_as::<Timer>("MobTimer")
}
}
9 changes: 7 additions & 2 deletions dodge-the-creeps/rust/src/player.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use godot::classes::{AnimatedSprite2D, Area2D, CollisionShape2D, IArea2D, Input, PhysicsBody2D};
use godot::classes::{AnimatedSprite2D, Area2D, CollisionShape2D, IArea2D, Input};
use godot::prelude::*;

#[derive(GodotClass)]
Expand All @@ -17,7 +17,7 @@ impl Player {
pub fn hit();

#[func]
fn on_player_body_entered(&mut self, _body: Gd<PhysicsBody2D>) {
fn on_player_body_entered(&mut self, _body: Gd<Node2D>) {
self.base_mut().hide();
self.signals().hit().emit();

Expand Down Expand Up @@ -55,6 +55,11 @@ impl IArea2D for Player {
let viewport = self.base().get_viewport_rect();
self.screen_size = viewport.size;
self.base_mut().hide();

// Signal setup
self.signals()
.body_entered()
.connect_self(Self::on_player_body_entered);
}

// `delta` can be f32 or f64; #[godot_api] macro converts transparently.
Expand Down
Loading