Skip to content

Commit a48e920

Browse files
committed
Switch to Yarn Slinger for dialog
1 parent e46b015 commit a48e920

File tree

15 files changed

+659
-538
lines changed

15 files changed

+659
-538
lines changed

Cargo.lock

+570-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ bevy_rapier3d = { version = "0.23", features = [ "serde-serialize", "simd-stable
5252
bevy_editor_pls = { version = "0.7", optional = true }
5353
bevy_hanabi = "0.9"
5454

55+
# https://github.com/yarn-slinger/yarn-slinger/issues/172
56+
bevy_yarn_slinger = { git = "https://github.com/yarn-slinger/yarn-slinger", rev = "d0a4031e51622ad309b23b7dbc81230076af2bb4" }
57+
bevy_yarn_slinger_example_dialogue_view = { git = "https://github.com/yarn-slinger/yarn-slinger", rev = "d0a4031e51622ad309b23b7dbc81230076af2bb4" }
58+
5559
# keep the following in sync with Bevy's dependencies
5660
## https://github.com/bevyengine/bevy/blob/v0.12.1/crates/bevy_winit/Cargo.toml#L31
5761
winit = { version = "0.28.7", default-features = false }
File renamed without changes.

assets/dialogue/follower.yarn

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
title: Follower
2+
---
3+
Hello World! To continue the dialogue, click with your mouse or press the spacebar.
4+
These are options. You can select one by clicking on it or pressing the corresponding number on your keyboard.
5+
-> Some cool option
6+
-> Some other cool option
7+
Now we'll jump to another node!
8+
<<jump AnotherNode>>
9+
10+
===

src/file_system_interaction/asset_loading.rs

-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::file_system_interaction::config::GameConfig;
22
use crate::file_system_interaction::level_serialization::SerializedLevel;
3-
use crate::world_interaction::dialog::Dialog;
43
use crate::GameState;
54
use anyhow::Result;
65
use bevy::prelude::*;
@@ -16,7 +15,6 @@ use iyes_progress::{ProgressCounter, ProgressPlugin};
1615

1716
pub(crate) fn loading_plugin(app: &mut App) {
1817
app.add_plugins(RonAssetPlugin::<SerializedLevel>::new(&["lvl.ron"]))
19-
.add_plugins(RonAssetPlugin::<Dialog>::new(&["dlg.ron"]))
2018
.add_plugins(TomlAssetPlugin::<GameConfig>::new(&["game.toml"]))
2119
.add_plugins(ProgressPlugin::new(GameState::Loading).continue_to(GameState::Menu))
2220
.add_loading_state(
@@ -26,7 +24,6 @@ pub(crate) fn loading_plugin(app: &mut App) {
2624
.load_collection::<SceneAssets>()
2725
.load_collection::<AnimationAssets>()
2826
.load_collection::<LevelAssets>()
29-
.load_collection::<DialogAssets>()
3027
.load_collection::<TextureAssets>()
3128
.load_collection::<ConfigAssets>(),
3229
)
@@ -67,12 +64,6 @@ pub(crate) struct LevelAssets {
6764
pub(crate) levels: HashMap<String, Handle<SerializedLevel>>,
6865
}
6966

70-
#[derive(AssetCollection, Resource, Clone)]
71-
pub(crate) struct DialogAssets {
72-
#[asset(path = "dialogs", collection(typed, mapped))]
73-
pub(crate) dialogs: HashMap<String, Handle<Dialog>>,
74-
}
75-
7667
#[derive(AssetCollection, Resource, Clone)]
7768
pub(crate) struct TextureAssets {
7869
#[asset(path = "textures/stone_alley_2.jpg")]
@@ -96,7 +87,6 @@ fn show_progress(
9687
scene_assets: Option<Res<SceneAssets>>,
9788
animation_assets: Option<Res<AnimationAssets>>,
9889
level_assets: Option<Res<LevelAssets>>,
99-
dialog_assets: Option<Res<DialogAssets>>,
10090
texture_assets: Option<Res<TextureAssets>>,
10191
config_assets: Option<Res<ConfigAssets>>,
10292
) {
@@ -119,7 +109,6 @@ fn show_progress(
119109
ui.checkbox(&mut scene_assets.is_some(), "Scenes");
120110
ui.checkbox(&mut animation_assets.is_some(), "Animations");
121111
ui.checkbox(&mut level_assets.is_some(), "Levels");
122-
ui.checkbox(&mut dialog_assets.is_some(), "Dialogs");
123112
ui.checkbox(&mut texture_assets.is_some(), "Textures");
124113
ui.checkbox(&mut config_assets.is_some(), "Config");
125114
});

src/file_system_interaction/config.rs

-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub(crate) struct GameConfig {
88
pub(crate) camera: Camera,
99
pub(crate) characters: Characters,
1010
pub(crate) player: Player,
11-
pub(crate) dialog: Dialog,
1211
}
1312

1413
#[derive(Debug, Clone, PartialEq, Reflect, Serialize, Deserialize, Default)]
@@ -76,9 +75,3 @@ pub(crate) struct Player {
7675
pub(crate) min_fov: f32,
7776
pub(crate) max_fov: f32,
7877
}
79-
80-
#[derive(Debug, Clone, PartialEq, Reflect, Serialize, Deserialize, Default)]
81-
#[reflect(Serialize, Deserialize)]
82-
pub(crate) struct Dialog {
83-
pub(crate) base_letters_per_second: f32,
84-
}

src/file_system_interaction/game_state_serialization.rs

-15
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::file_system_interaction::level_serialization::{CurrentLevel, WorldLoa
22
use crate::level_instantiation::spawning::GameObject;
33
use crate::player_control::player_embodiment::Player;
44
use crate::world_interaction::condition::ActiveConditions;
5-
use crate::world_interaction::dialog::{CurrentDialog, DialogEvent};
65
use crate::GameState;
76
use anyhow::{Context, Error, Result};
87
use bevy::prelude::*;
@@ -45,8 +44,6 @@ struct SaveModel {
4544
#[serde(default, skip_serializing_if = "ActiveConditions::is_empty")]
4645
conditions: ActiveConditions,
4746
player_transform: Transform,
48-
#[serde(default, skip_serializing_if = "Option::is_none")]
49-
dialog_event: Option<DialogEvent>,
5047
}
5148

5249
#[sysfail(log(level = "error"))]
@@ -55,7 +52,6 @@ fn handle_load_requests(
5552
mut load_events: EventReader<GameLoadRequest>,
5653
mut loader: EventWriter<WorldLoadRequest>,
5754
mut spawner: EventWriter<SpawnEvent<GameObject, Transform>>,
58-
mut dialog_event_writer: EventWriter<DialogEvent>,
5955
) -> Result<()> {
6056
for load in load_events.read() {
6157
let path = match load
@@ -96,9 +92,6 @@ fn handle_load_requests(
9692
loader.send(WorldLoadRequest {
9793
filename: save_model.scene,
9894
});
99-
if let Some(dialog_event) = save_model.dialog_event {
100-
dialog_event_writer.send(dialog_event);
101-
}
10295
commands.insert_resource(save_model.conditions);
10396

10497
spawner.send(
@@ -128,22 +121,14 @@ fn read_last_save() -> Result<Option<PathBuf>, Error> {
128121
fn handle_save_requests(
129122
mut save_events: EventReader<GameSaveRequest>,
130123
conditions: Res<ActiveConditions>,
131-
dialog: Option<Res<CurrentDialog>>,
132124
player_query: Query<&GlobalTransform, With<Player>>,
133125
current_level: Res<CurrentLevel>,
134126
) -> Result<()> {
135-
let dialog = dialog.map(|dialog| dialog.clone());
136127
for save in save_events.read() {
137128
for player in &player_query {
138-
let dialog_event = dialog.clone().map(|dialog| DialogEvent {
139-
dialog: dialog.id,
140-
source: dialog.source,
141-
page: Some(dialog.current_page),
142-
});
143129
let save_model = SaveModel {
144130
scene: current_level.scene.clone(),
145131
conditions: conditions.clone(),
146-
dialog_event,
147132
player_transform: player.compute_transform(),
148133
};
149134
let serialized = match ron::to_string(&save_model) {

src/file_system_interaction/level_serialization.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::file_system_interaction::asset_loading::LevelAssets;
22
use crate::level_instantiation::spawning::GameObject;
33
use crate::world_interaction::condition::ActiveConditions;
4-
use crate::world_interaction::dialog::CurrentDialog;
54
use crate::world_interaction::interactions_ui::InteractionOpportunities;
65
use anyhow::{Context, Result};
76
use bevy::prelude::*;
@@ -132,7 +131,6 @@ fn load_world(
132131
});
133132
commands.insert_resource(InteractionOpportunities::default());
134133
commands.insert_resource(ActiveConditions::default());
135-
commands.remove_resource::<CurrentDialog>();
136134

137135
info!("Successfully loaded scene \"{}\"", load.filename,)
138136
}

src/level_instantiation/spawning/objects/npc.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::level_instantiation::spawning::objects::GameCollisionGroup;
33
use crate::level_instantiation::spawning::GameObject;
44
use crate::movement::general_movement::{CharacterAnimations, CharacterControllerBundle, Model};
55
use crate::movement::navigation::Follower;
6-
use crate::world_interaction::dialog::{DialogId, DialogTarget};
6+
use crate::world_interaction::dialog::DialogTarget;
77
use bevy::prelude::*;
88
use bevy_rapier3d::prelude::*;
99
use std::f32::consts::TAU;
@@ -32,7 +32,8 @@ pub(crate) fn spawn(
3232
aerial: animations.character_running.clone(),
3333
},
3434
DialogTarget {
35-
dialog_id: DialogId::new("follower"),
35+
speaker: "The Follower".to_string(),
36+
node: "Follower".to_string(),
3637
},
3738
GameObject::Npc,
3839
))

src/player_control/actions.rs

-21
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,6 @@ pub(crate) enum PlayerAction {
6262
NumberedChoice0,
6363
}
6464

65-
impl PlayerAction {
66-
pub(crate) fn numbered_choice(index: u8) -> Self {
67-
match index {
68-
0 => PlayerAction::NumberedChoice0,
69-
1 => PlayerAction::NumberedChoice1,
70-
2 => PlayerAction::NumberedChoice2,
71-
3 => PlayerAction::NumberedChoice3,
72-
4 => PlayerAction::NumberedChoice4,
73-
5 => PlayerAction::NumberedChoice5,
74-
6 => PlayerAction::NumberedChoice6,
75-
7 => PlayerAction::NumberedChoice7,
76-
8 => PlayerAction::NumberedChoice8,
77-
9 => PlayerAction::NumberedChoice9,
78-
_ => panic!(
79-
"Numbered choice index out of range: got {}, expected 0-9",
80-
index
81-
),
82-
}
83-
}
84-
}
85-
8665
#[derive(Debug, Clone, Eq, PartialEq, Hash, Actionlike, Reflect, Default)]
8766
pub(crate) enum CameraAction {
8867
#[default]

src/player_control/camera/focus.rs

+18-13
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
11
use crate::player_control::camera::IngameCamera;
22
use crate::player_control::player_embodiment::Player;
3-
use crate::world_interaction::dialog::CurrentDialog;
3+
use crate::world_interaction::dialog::DialogTarget;
44
use anyhow::Result;
55
use bevy::prelude::*;
66
use bevy_mod_sysfail::*;
7+
use bevy_yarn_slinger_example_dialogue_view::SpeakerChangeEvent;
78

89
#[sysfail(log(level = "error"))]
910
pub(crate) fn set_camera_focus(
1011
mut camera_query: Query<&mut IngameCamera>,
11-
current_dialog: Option<Res<CurrentDialog>>,
12+
mut speaker_change_events: EventReader<SpeakerChangeEvent>,
1213
player_query: Query<&Transform, With<Player>>,
13-
non_player_query: Query<&GlobalTransform, Without<Player>>,
14+
dialog_targets: Query<(&Transform, &DialogTarget), Without<Player>>,
1415
) -> Result<()> {
15-
for mut camera in camera_query.iter_mut() {
16-
for player_transform in player_query.iter() {
17-
if let Some(ref active_dialogue) = current_dialog {
18-
let dialog_target_transform = non_player_query
19-
.get(active_dialogue.source)?
20-
.compute_transform();
21-
camera.secondary_target = Some(dialog_target_transform);
22-
} else {
23-
camera.secondary_target = None;
16+
for event in speaker_change_events.read() {
17+
for mut camera in camera_query.iter_mut() {
18+
for player_transform in player_query.iter() {
19+
if event.speaking {
20+
for (dialog_target_transform, dialog_target) in dialog_targets.iter() {
21+
if dialog_target.speaker == event.character_name {
22+
camera.secondary_target = Some(*dialog_target_transform);
23+
camera.target = *player_transform;
24+
}
25+
}
26+
} else {
27+
camera.secondary_target = None;
28+
}
29+
camera.target = *player_transform;
2430
}
25-
camera.target = *player_transform;
2631
}
2732
}
2833
Ok(())

src/player_control/player_embodiment.rs

+27-19
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ use crate::player_control::actions::{DualAxisDataExt, PlayerAction};
55
use crate::player_control::camera::{CameraUpdateSystemSet, IngameCamera, IngameCameraKind};
66
use crate::util::smoothness_to_lerp_factor;
77
use crate::util::trait_extension::{F32Ext, TransformExt, Vec3Ext};
8-
use crate::world_interaction::dialog::CurrentDialog;
8+
use crate::world_interaction::dialog::DialogTarget;
99
use crate::GameState;
1010
use anyhow::{Context, Result};
1111
use bevy::prelude::*;
1212
use bevy_kira_audio::AudioInstance;
1313
use bevy_mod_sysfail::*;
1414
use bevy_rapier3d::prelude::*;
15+
use bevy_yarn_slinger_example_dialogue_view::SpeakerChangeEvent;
1516
use leafwing_input_manager::prelude::ActionState;
1617
use serde::{Deserialize, Serialize};
1718
use std::ops::DerefMut;
@@ -27,7 +28,7 @@ pub(crate) fn player_embodiment_plugin(app: &mut App) {
2728
handle_jump,
2829
handle_horizontal_movement,
2930
handle_speed_effects,
30-
rotate_to_speaker.run_if(resource_exists::<CurrentDialog>()),
31+
rotate_to_speaker,
3132
control_walking_sound,
3233
handle_camera_kind,
3334
)
@@ -147,28 +148,35 @@ fn handle_speed_effects(
147148
fn rotate_to_speaker(
148149
time: Res<Time>,
149150
mut with_player: Query<(&mut Transform, &Velocity), With<Player>>,
150-
without_player: Query<&Transform, Without<Player>>,
151-
current_dialog: Res<CurrentDialog>,
151+
speakers: Query<(&Transform, &DialogTarget), Without<Player>>,
152+
mut speaker_change_event: EventReader<SpeakerChangeEvent>,
152153
config: Res<GameConfig>,
153154
) {
154155
#[cfg(feature = "tracing")]
155156
let _span = info_span!("rotate_to_speaker").entered();
156-
let Ok(speaker_transform) = without_player.get(current_dialog.source) else {
157-
return;
158-
};
159-
let dt = time.delta_seconds();
157+
for speaker_change in speaker_change_event.read() {
158+
if !speaker_change.speaking {
159+
continue;
160+
}
161+
let dt = time.delta_seconds();
160162

161-
for (mut transform, velocity) in with_player.iter_mut() {
162-
let horizontal_velocity = velocity.linvel.split(transform.up()).horizontal;
163-
if horizontal_velocity.is_approx_zero() {
164-
let up = transform.up();
165-
let target_rotation = transform
166-
.horizontally_looking_at(speaker_transform.translation, up)
167-
.rotation;
168-
let smoothness = config.player.rotate_to_speaker_smoothness;
169-
let factor = smoothness_to_lerp_factor(smoothness, dt);
170-
let rotation = transform.rotation.slerp(target_rotation, factor);
171-
transform.rotation = rotation;
163+
for (mut transform, velocity) in with_player.iter_mut() {
164+
for (speaker_transform, dialog_target) in speakers.iter() {
165+
if dialog_target.speaker != speaker_change.character_name {
166+
continue;
167+
}
168+
let horizontal_velocity = velocity.linvel.split(transform.up()).horizontal;
169+
if horizontal_velocity.is_approx_zero() {
170+
let up = transform.up();
171+
let target_rotation = transform
172+
.horizontally_looking_at(speaker_transform.translation, up)
173+
.rotation;
174+
let smoothness = config.player.rotate_to_speaker_smoothness;
175+
let factor = smoothness_to_lerp_factor(smoothness, dt);
176+
let rotation = transform.rotation.slerp(target_rotation, factor);
177+
transform.rotation = rotation;
178+
}
179+
}
172180
}
173181
}
174182
}

0 commit comments

Comments
 (0)