在遊戲開發中,得分系統和音效系統是提升遊戲體驗的關鍵要素。本文將詳細介紹如何在 Amethyst 遊戲引擎中實作這兩個系統。首先,我們會建立一個 WinnerSystem 來判斷遊戲勝負並更新得分,接著使用 Amethyst 的 UI 系統顯示得分。然後,我們將建立一個獨立的音訊模組來載入和管理音效和背景音樂,並將它們整合到遊戲中,使遊戲更加生動有趣。最後,我們將總結整個實作過程,並提供一些額外的技巧和建議,幫助讀者更好地理解和應用這些技術。
遊戲得分系統的建構與實作
在前面的章節中,我們已經成功地建立了一個可玩的遊戲,但得分仍然需要手動記錄。為了提升遊戲的體驗,我們將透過新增一個名為WinnerSystem的系統來自動記錄得分。
WinnerSystem 的基本架構
首先,我們需要建立WinnerSystem的基本架構,如下所示:
// src/systems/winner.rs
use amethyst::{
core::transform::Transform,
core::SystemDesc,
derive::SystemDesc,
ecs::prelude::{Join, System, SystemData, World, WriteStorage},
};
use crate::catvolleyball::{Ball, ARENA_HEIGHT, ARENA_WIDTH};
#[derive(SystemDesc)]
pub struct WinnerSystem;
impl<'s> System<'s> for WinnerSystem {
type SystemData = (WriteStorage<'s, Ball>, WriteStorage<'s, Transform>);
fn run(&mut self, (mut balls, mut transforms): Self::SystemData) {
// 實作 WinnerSystem 的邏輯
}
}
內容解密:
WinnerSystem是一個用於判斷遊戲勝負的系統。- 它需要存取
Ball和Transform元件,以便取得球的位置和速度等資訊。 run函式是系統的核心,負責實作判斷勝負的邏輯。
勝負判斷邏輯的實作
在run函式中,我們將實作勝負判斷的邏輯:
impl<'s> System<'s> for WinnerSystem {
type SystemData = (WriteStorage<'s, Ball>, WriteStorage<'s, Transform>);
fn run(&mut self, (mut balls, mut transforms): Self::SystemData) {
for (ball, transform) in (&mut balls, &mut transforms).join() {
let ball_x = transform.translation().x;
let ball_y = transform.translation().y;
if ball_y <= ball.radius {
// 球觸地,判斷得分
if ball_x <= (ARENA_WIDTH / 2.0) {
println!("右邊球員得分");
} else {
println!("左邊球員得分");
}
// 重置球的位置和速度
transform.set_translation_x(ARENA_WIDTH / 2.0);
transform.set_translation_y(ARENA_HEIGHT / 2.0);
ball.velocity[0] = -ball.velocity[0];
ball.velocity[1] = 0.0;
}
}
}
}
內容解密:
- 當球的Y座標小於或等於其半徑時,表示球觸地,得分條件觸發。
- 根據球的X座標判斷是哪一邊的球員得分。
- 重置球的位置到場地中央,並改變其X軸速度方向,以模擬發球。
使用 UI 系統顯示得分
為了更好地展示得分,我們需要使用 Amethyst 的 UI 系統。首先,新增UiBundle到遊戲資料建構器中:
// src/main.rs
use amethyst::ui::{RenderUi, UiBundle};
fn main() -> amethyst::Result<()> {
// ...
let game_data = GameDataBuilder::default()
// ...
.with_bundle(UiBundle::<StringBindings>::new())?
.with_bundle(
RenderingBundle::<DefaultBackend>::new()
// ... 其他外掛
.with_plugin(RenderUi::default()),
)?;
// ...
}
內容解密:
UiBundle提供了UI功能,必須新增到遊戲資料建構器中。RenderUi外掛使遊戲能夠渲染UI元素。
建立 ScoreBoard 和 ScoreText
接下來,建立一個ScoreBoard結構體來儲存得分,並在WinnerSystem中使用它:
// src/catvolleyball.rs
#[derive(Default)]
pub struct ScoreBoard {
pub score_left: i32,
pub score_right: i32,
}
並修改WinnerSystem以更新ScoreBoard:
impl<'s> System<'s> for WinnerSystem {
type SystemData = (
WriteStorage<'s, Ball>,
WriteStorage<'s, Transform>,
Write<'s, ScoreBoard>,
);
fn run(&mut self, (mut balls, mut transforms, mut score_board): Self::SystemData) {
for (ball, transform) in (&mut balls, &mut transforms).join() {
// ...
if ball_y <= ball.radius {
if ball_x <= (ARENA_WIDTH / 2.0) {
score_board.score_right = (score_board.score_right + 1).min(999);
} else {
score_board.score_left = (score_board.score_left + 1).min(999);
}
// ...
}
}
}
}
內容解密:
ScoreBoard結構體儲存兩邊球員的得分。- 在
WinnerSystem中,根據得分條件更新ScoreBoard中的得分。
初始化 UiText 實體
最後,建立一個ScoreText結構體來持有顯示得分的UiText實體,並在遊戲開始時初始化它們:
// src/catvolleyball.rs
pub struct ScoreText {
pub p1_score: Entity,
pub p2_score: Entity,
}
fn initialize_scoreboard(world: &mut World) {
// 載入字型並建立 UiTransform 和 UiText 實體
// ...
}
內容解密:
ScoreText結構體用於儲存代表兩邊球員得分的UI實體。initialize_scoreboard函式負責初始化這些UI實體,包括設定它們的位置、大小和顯示的文字。
透過上述步驟,我們成功地實作了遊戲的自動得分系統,並將得分顯示在螢幕上,大大增強了遊戲的互動性和使用者經驗。
在遊戲中加入音效與音樂
現在已經有了一個完整的遊戲,但如果沒有音效和背景音樂,就會感覺不夠完整。要在遊戲中播放音樂,需要在 src/main.rs 中的 GameData 新增 AudioBundle。
載入音訊檔案
首先,需要將音訊檔案載入到適合 Amethyst 音訊系統的資料結構中。可以將它們新增到 src/catvolleyball.rs 檔案中,但由於該檔案已經相當龐大,因此可以建立一個新的 Rust 模組來存放所有與音訊相關的程式碼,並在 src/catvolleyball.rs 和 src/main.rs 中參照它。
建立音訊模組
建立一個名為 src/audio.rs 的檔案,並在其中建立一些函式來載入背景音樂。
// src/audio.rs
use amethyst::{
assets::{AssetStorage, Loader},
audio::{output::Output, AudioSink, OggFormat, Source, SourceHandle},
ecs::prelude::{World, WorldExt},
};
use std::iter::Cycle;
use std::vec::IntoIter;
pub struct Music {
pub music: Cycle<IntoIter<SourceHandle>>,
}
pub struct Sounds {
pub score_sfx: SourceHandle,
pub bounce_sfx: SourceHandle,
}
/// 載入 ogg 音訊軌道。
fn load_audio_track(loader: &Loader, world: &World, file: &str) -> SourceHandle {
loader.load(file, OggFormat, (), &world.read_resource())
}
pub fn initialize_audio(world: &mut World) {
use crate::catvolleyball::{AUDIO_BOUNCE, AUDIO_MUSIC, AUDIO_SCORE};
let (sound_effects, music) = {
let mut sink = world.write_resource::<AudioSink>();
// 音樂聲音有點大,降低音量。
sink.set_volume(0.25);
let loader = world.read_resource::<Loader>();
let music = AUDIO_MUSIC
.iter()
.map(|file| load_audio_track(&loader, &world, file))
.collect::<Vec<_>>()
.into_iter()
.cycle();
let music = Music { music };
let sound = Sounds {
bounce_sfx: load_audio_track(&loader, &world, AUDIO_BOUNCE),
score_sfx: load_audio_track(&loader, &world, AUDIO_SCORE),
};
(sound, music)
};
world.insert(sound_effects);
world.insert(music);
}
載入音訊檔案的解說
- 建立
Music和Sounds結構體:Music結構體用於存放背景音樂的迭代器,而Sounds結構體則用於存放音效。 load_audio_track函式:該函式用於載入 ogg 格式的音訊檔案。initialize_audio函式:該函式用於初始化音訊系統,包括載入背景音樂和音效,並將它們插入到World中。
在遊戲中使用音訊
在 src/catvolleyball.rs 中,需要呼叫 initialize_audio 函式來初始化音訊系統。
// src/catvolleyball.rs
use crate::audio::initialize_audio;
pub const AUDIO_MUSIC: &'static [&'static str] = &[
"./audio/Computer_Music_All-Stars_-_Wheres_My_Jetpack.ogg",
"./audio/Computer_Music_All-Stars_-_Albatross_v2.ogg",
];
pub const AUDIO_BOUNCE: &'static str = "./audio/bounce.ogg";
pub const AUDIO_SCORE: &'static str = "./audio/score.ogg";
pub struct CatVolleyball;
impl SimpleState for CatVolleyball {
fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) {
// ...
initialize_audio(world);
}
}
在 GameData 中新增 AudioBundle
最後,需要在 src/main.rs 中的 GameData 新增 AudioBundle。
// src/main.rs
use amethyst::audio::{AudioBundle};
fn main() -> amethyst::Result<()> {
// ...
let game_data = GameDataBuilder::default()
// ...
.with_bundle(AudioBundle::default())?
// ...
}
新增音訊後的遊戲體驗
新增音訊後,遊戲的體驗將會大大提升。背景音樂和音效將會使遊戲更加生動和有趣。
重點回顧
- 建立音訊模組:建立一個新的 Rust 模組來存放所有與音訊相關的程式碼。
- 載入音訊檔案:使用
Loader和OggFormat來載入 ogg 格式的音訊檔案。 - 初始化音訊系統:使用
initialize_audio函式來初始化音訊系統,包括載入背景音樂和音效。 - 在遊戲中使用音訊:在
src/catvolleyball.rs中呼叫initialize_audio函式來初始化音訊系統,並在GameData中新增AudioBundle。
內容解密:
上述步驟闡述瞭如何在 Amethyst 遊戲引擎中新增和管理音訊資源,包括建立獨立的音訊模組、載入特定格式的音訊檔案,以及如何將這些音訊資源整合到遊戲邏輯中,最終提升遊戲的整體體驗。透過這種方式,開發者可以更方便地管理和使用音訊資源,使遊戲開發過程更加高效和有序。