Skip to content

Commit fea4fc6

Browse files
committed
Dodge-the-creeps: use type-safe signals
1 parent a5c457d commit fea4fc6

File tree

4 files changed

+32
-25
lines changed

4 files changed

+32
-25
lines changed

dodge-the-creeps/godot/Main.tscn

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ stream = ExtResource("5")
4848
[node name="DeathSound" type="AudioStreamPlayer" parent="."]
4949
stream = ExtResource("6")
5050

51-
[connection signal="hit" from="Player" to="." method="game_over"]
5251
[connection signal="timeout" from="MobTimer" to="." method="on_mob_timer_timeout"]
5352
[connection signal="timeout" from="ScoreTimer" to="." method="on_score_timer_timeout"]
5453
[connection signal="timeout" from="StartTimer" to="." method="on_start_timer_timeout"]
55-
[connection signal="start_game" from="Hud" to="." method="new_game" flags=3]

dodge-the-creeps/rust/src/hud.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ pub struct Hud {
99

1010
#[godot_api]
1111
impl Hud {
12+
// Public signal, since it's used by Main struct.
1213
#[signal]
13-
fn start_game();
14+
pub fn start_game();
1415

1516
#[func]
1617
pub fn show_message(&self, text: GString) {
@@ -51,11 +52,7 @@ impl Hud {
5152
let mut button = self.base().get_node_as::<Button>("StartButton");
5253
button.hide();
5354

54-
// Note: this works only because `start_game` is a deferred signal.
55-
// This method keeps a &mut Hud, and start_game calls Main::new_game(), which itself accesses this Hud
56-
// instance through Gd<Hud>::bind_mut(). It will try creating a 2nd &mut reference, and thus panic.
57-
// Deferring the signal is one option to work around it.
58-
self.base_mut().emit_signal("start_game", &[]);
55+
self.signals().start_game().emit();
5956
}
6057

6158
#[func]

dodge-the-creeps/rust/src/main_scene.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use crate::hud::Hud;
2-
use crate::mob;
3-
use crate::player;
1+
use crate::{hud, mob, player};
42

53
use godot::classes::{Marker2D, PathFollow2D, RigidBody2D, Timer};
64
use godot::prelude::*;
@@ -14,6 +12,7 @@ use std::f32::consts::PI;
1412
pub struct Main {
1513
mob_scene: OnReady<Gd<PackedScene>>,
1614
player: OnReady<Gd<player::Player>>,
15+
hud: OnReady<Gd<hud::Hud>>,
1716
music: OnReady<Gd<AudioStreamPlayer>>,
1817
death_sound: OnReady<Gd<AudioStreamPlayer>>,
1918
score: i64,
@@ -27,34 +26,51 @@ impl INode for Main {
2726
Self {
2827
mob_scene: OnReady::new(|| load("res://Mob.tscn")),
2928
player: OnReady::node("Player"),
29+
hud: OnReady::node("Hud"),
3030
music: OnReady::node("Music"),
3131
death_sound: OnReady::node("DeathSound"),
3232
score: 0,
3333
base,
3434
}
3535
}
3636

37-
fn ready(&mut self) {}
37+
fn ready(&mut self) {
38+
// The OnReady instances are now initialized, we can access them like normal fields.
39+
40+
// Get a Gd<Main> pointer to this instance.
41+
let main = self.to_gd();
42+
43+
// Connect Player::hit -> Main::game_over.
44+
self.player
45+
.signals()
46+
.hit()
47+
.connect_obj(&main, Self::game_over);
48+
49+
// Connect Hud::start_game -> Main::new_game.
50+
self.hud
51+
.signals()
52+
.start_game()
53+
.connect_obj(&main, Self::new_game);
54+
}
3855
}
3956

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

4764
score_timer.stop();
4865
mob_timer.stop();
4966

50-
let mut hud = self.base().get_node_as::<Hud>("Hud");
51-
hud.bind_mut().show_game_over();
67+
self.hud.bind_mut().show_game_over();
5268

5369
self.music.stop();
5470
self.death_sound.play();
5571
}
5672

57-
#[func]
73+
// No #[func].
5874
pub fn new_game(&mut self) {
5975
let start_position = self.base().get_node_as::<Marker2D>("StartPosition");
6076
let mut start_timer = self.base().get_node_as::<Timer>("StartTimer");
@@ -64,8 +80,7 @@ impl Main {
6480
self.player.bind_mut().start(start_position.get_position());
6581
start_timer.start();
6682

67-
let mut hud = self.base().get_node_as::<Hud>("Hud");
68-
let hud = hud.bind_mut();
83+
let hud = self.hud.bind_mut();
6984
hud.update_score(self.score);
7085
hud.show_message("Get Ready".into());
7186

@@ -84,8 +99,7 @@ impl Main {
8499
fn on_score_timer_timeout(&mut self) {
85100
self.score += 1;
86101

87-
let mut hud = self.base().get_node_as::<Hud>("Hud");
88-
hud.bind_mut().update_score(self.score);
102+
self.hud.bind_mut().update_score(self.score);
89103
}
90104

91105
#[func]
@@ -117,8 +131,5 @@ impl Main {
117131
};
118132

119133
mob.set_linear_velocity(Vector2::new(range, 0.0).rotated(real::from_f32(direction)));
120-
121-
let mut hud = self.base().get_node_as::<Hud>("Hud");
122-
hud.connect("start_game", &mob.callable("on_start_game"));
123134
}
124135
}

dodge-the-creeps/rust/src/player.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ pub struct Player {
1212

1313
#[godot_api]
1414
impl Player {
15+
// Public signal, since it's used by Main struct.
1516
#[signal]
16-
fn hit();
17+
pub fn hit();
1718

1819
#[func]
1920
fn on_player_body_entered(&mut self, _body: Gd<PhysicsBody2D>) {
2021
self.base_mut().hide();
21-
self.base_mut().emit_signal("hit", &[]);
22+
self.signals().hit().emit();
2223

2324
let mut collision_shape = self
2425
.base()

0 commit comments

Comments
 (0)