简易语句拆分
use bevy::prelude::*;
use bevy_flash::{
FlashPlugin,
player::{Flash, FlashPlayer},
};
use serde::Deserialize;
#[derive(Resource)]
struct GameConfig {
player_speed: f32,
volume: f32,
}
#[derive(Resource)]
struct DialogueData {
dialogues: Vec<Dialogue>,
current_index: usize,
}
#[derive(Resource)]
struct GameFont {
handle: Handle<Font>,
}
#[derive(Debug, Deserialize, Clone)]
struct Dialogue {
character: String,
text: String,
portrait: String,
#[serde(default)]
bgm: Option<String>,
#[serde(default)]
background: Option<String>,
#[serde(default)]
swf: Option<String>,
#[serde(default)]
label: Option<String>,
#[serde(default)]
jump: Option<String>,
#[serde(default)]
pause: Option<bool>,
}
#[derive(Component)]
struct TypewriterText {
full_text: String,
current_char_index: usize,
timer: Timer,
is_complete: bool,
}
impl TypewriterText {
fn new(text: String, chars_per_second: f32) -> Self {
Self {
full_text: text,
current_char_index: 0,
timer: Timer::from_seconds(1.0 / chars_per_second, TimerMode::Repeating),
is_complete: false,
}
}
}
#[derive(Component)]
struct DialogueTextEntity;
#[derive(Component)]
struct CharacterNameEntity;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(FlashPlugin)
.add_systems(Startup, setup_camera)
.add_systems(Startup, load_font.after(setup_camera))
.add_systems(Startup, load_dialogues.after(load_font))
.add_systems(Update, (handle_click_input, typewriter_animation_system))
.run();
}
fn setup_camera(mut commands: Commands) {
commands.spawn(Camera2d);
}
fn load_font(mut commands: Commands, asset_server: Res<AssetServer>) {
let font_handle = asset_server.load("two.TTF");
commands.insert_resource(GameFont {
handle: font_handle,
});
println!("字体 two.TTF 已加载");
}
fn load_dialogues(mut commands: Commands, font: Res<GameFont>) {
let sample_dialogues = vec![
Dialogue {
character: "Alice".to_string(),
text: "你好,欢迎来到这个世界!这是一个美丽的地方,充满了奇迹和冒险。".to_string(),
portrait: "alice.png".to_string(),
bgm: Some("intro.mp3".to_string()),
background: Some("forest.jpg".to_string()),
swf: None,
label: Some("start".to_string()),
jump: None,
pause: Some(false),
},
Dialogue {
character: "Bob".to_string(),
text: "你好 这是一个什么样子的数据".to_string(),
portrait: "bob.png".to_string(),
bgm: None,
background: None,
swf: Some("animation.swf".to_string()),
label: None,
jump: Some("chapter2".to_string()),
pause: Some(true),
},
Dialogue {
character: "Charlie".to_string(),
text: "记住,勇气和智慧是你最好的伙伴。祝你好运!".to_string(),
portrait: "charlie.png".to_string(),
bgm: None,
background: None,
swf: None,
label: None,
jump: None,
pause: Some(false),
},
];
commands.insert_resource(DialogueData {
dialogues: sample_dialogues,
current_index: 0,
});
println!("对话数据已加载");
spawn_initial_dialogue(&mut commands, &font);
}
fn spawn_initial_dialogue(commands: &mut Commands, font: &Res<GameFont>) {
commands.spawn((
Text::new("点击鼠标或按空格键开始对话"),
TextFont {
font: font.handle.clone(),
font_size: 24.0,
..default()
},
TextColor(Color::WHITE),
Transform::from_translation(Vec3::new(0.0, 200.0, 0.0)),
DialogueTextEntity,
));
}
fn handle_click_input(
mouse_input: Res<ButtonInput<MouseButton>>,
keyboard_input: Res<ButtonInput<KeyCode>>,
mut dialogue_data: ResMut<DialogueData>,
mut commands: Commands,
dialogue_query: Query<Entity, With<DialogueTextEntity>>,
character_query: Query<Entity, With<CharacterNameEntity>>,
mut typewriter_query: Query<&mut TypewriterText>,
font: Res<GameFont>,
) {
let should_advance = mouse_input.just_pressed(MouseButton::Left) ||
keyboard_input.just_pressed(KeyCode::Space);
if should_advance {
let mut has_active_typewriter = false;
for mut typewriter in typewriter_query.iter_mut() {
if !typewriter.is_complete {
typewriter.is_complete = true;
typewriter.current_char_index = typewriter.full_text.chars().count();
has_active_typewriter = true;
return;
}
}
if !has_active_typewriter {
for entity in dialogue_query.iter() {
commands.entity(entity).despawn();
}
for entity in character_query.iter() {
commands.entity(entity).despawn();
}
if dialogue_data.current_index < dialogue_data.dialogues.len() {
let current_dialogue = &dialogue_data.dialogues[dialogue_data.current_index];
spawn_dialogue_text(&mut commands, current_dialogue, &font);
dialogue_data.current_index += 1;
} else {
dialogue_data.current_index = 0;
spawn_dialogue_end_text(&mut commands, &font);
}
}
}
}
fn spawn_dialogue_text(commands: &mut Commands, dialogue: &Dialogue, font: &Res<GameFont>) {
commands.spawn((
Text::new(format!("{}:", dialogue.character)),
TextFont {
font: font.handle.clone(),
font_size: 32.0,
..default()
},
TextColor(Color::srgb(1.0, 0.8, 1.0)),
Transform::from_translation(Vec3::new(1300.0, 1300.0, 0.0)),
CharacterNameEntity,
));
commands.spawn((
Text::new(""),
TextFont {
font: font.handle.clone(),
font_size: 28.0,
..default()
},
TextColor(Color::WHITE),
Node {
position_type: PositionType::Absolute,
top: Val::Px(50.0),
left: Val::Px(50.0),
..default()
},
TypewriterText::new(dialogue.text.clone(), 30.0),
DialogueTextEntity,
));
println!("显示对话 - 角色: {}, 文本: {}", dialogue.character, dialogue.text);
}
fn spawn_dialogue_end_text(commands: &mut Commands, font: &Res<GameFont>) {
commands.spawn((
Text::new("对话结束,点击重新开始"),
TextFont {
font: font.handle.clone(),
font_size: 24.0,
..default()
},
TextColor(Color::srgb(0.8, 0.8, 0.8)),
Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
DialogueTextEntity,
));
}
fn typewriter_animation_system(
time: Res<Time>,
mut typewriter_query: Query<(&mut TypewriterText, &mut Text)>,
) {
for (mut typewriter, mut text) in typewriter_query.iter_mut() {
if !typewriter.is_complete {
typewriter.timer.tick(time.delta());
if typewriter.timer.just_finished() {
let chars: Vec<char> = typewriter.full_text.chars().collect();
if typewriter.current_char_index < chars.len() {
typewriter.current_char_index += 1;
let displayed_text: String = chars.iter()
.take(typewriter.current_char_index)
.collect();
**text = displayed_text;
if typewriter.current_char_index >= chars.len() {
typewriter.is_complete = true;
}
}
}
}
}
}