這章有點短,但因為接了鍵盤輸入又要介紹處理輸入的 system 的話,篇幅又太長了,以每章都介紹同樣內容的原則獨立出來;對應為 pong tutorial 的第三章前半部 。
我們上一章已經寫了幾個 entity 跟 component,這章要開始進到 system,用來操作 entity 跟 component 的內容,在每個 frame system 都會叫起來執行一次,不做事或者做點變動。
要接使用者的輸入,我們在 config 下面準備第二個設定檔:config/input.ron。
(
axes: {
"rotate": Emulated( neg: Key(Left), pos: Key(Right) ),
"accelerate": Emulated( neg: Key(Down), pos: Key(Up) ),
},
actions: {
"shoot": [[Key(Space)]]
},
)
amethyst 中輸入有兩種形式:axes 和 actions
- axes 模擬的是兩個不同方向的操作
如果是搖桿的話應該能讀到類比的輸入,鍵盤則是兩個按鍵的互斥的輸入,兩個按鍵只會判定有一個被按下 - actions 表示數位的輸入,只有按下跟放開兩種狀態
我們這裡創造兩組 axes 輸入命名為 rotate 跟 accelerate,綁定方向鍵;一個 action 輸入接空白鍵。
可以綁定的對象當然不限於鍵盤 ,比如說 axes 就能綁定鍵盤、控制器(也許是搖桿)、滑鼠、滑鼠滾輪等輸入; Key 能綁定什麼按鍵則請參考文件 。
要接輸入,我們要對遊戲的 main.rs 作些修改,加上新的 input_bundle:
use amethyst::input::{InputBundle, StringBindings},
let input_config_path = config_dir.join("input.ron");
let input_bundle = InputBundle::<StringBindings>::new()
.with_bindings_from_file(input_config_path)?;
let game_data = GameDataBuilder::default()
// Render, transform bundle here ...
.with_bundle(input_bundle)?
我們產生一個 InputBundle,並用字串來作為讀取 ron 檔案時的 key:也就是上面我們寫的 “rotate”, “accelerate” 等。
這裡我們準備好寫我們第一個 system 了,這裡我們先把所有的 system 都塞在一個 system.rs 裡面,如果分割得更清楚的話,也可以改用 system 的 module 把各系統分到不同的檔案裡面。
system.rs 的內容如下:
use amethyst::{
core::{Transform},
derive::{SystemDesc},
ecs::{Join, ReadStorage, WriteStorage, System, SystemData, Read},
input::{InputHandler, StringBindings},
};
先引入 system 所需要的 module。
use crate::component::{Ship};
#[derive(SystemDesc)]
pub struct ShipControlSystem;
impl<'s> System<'s> for ShipControlSystem {
type SystemData = (
WriteStorage<'s, Transform>,
ReadStorage<'s, Ship>,
Read<'s, InputHandler::<StringBindings>>,
);
fn run(&mut self,
(mut transforms,
ships,
input): Self::SystemData) {
for (_ship, _transform) in (&ships, &mut transforms).join() {
let rotate = input.axis_value("rotate");
if let Some(rotate) = rotate {
println!("{}", rotate);
}
}
}
}
這個 system 很簡單,去讀 input 的值然後印出來,同樣的 system 也只是一個 struct,我們要實作 System<'s> trait
,裡面帶了 run 這個函式,amethyst 引擎會在每個 frame 呼叫各 system 的 run。
實作 system 時也要指定對應的 SystemData,SystemData 的內容對應 world 裡面儲存的 entity、component 或是 resource。
以我們這個系統為例,它會去修改 transform component、讀取 ship component、讀取使用者輸入。
在 run 裡,for loop 在下一章會更深入的介紹,這裡我們就是去讀取 input 的 axis_value
,指定的 key 是 “rotate”,得到對應左右鍵的輸入值,rotate 的值會是 Some(-1)
, Some(0)
, Some(1)
其中一個。
最後一步我們要把系統註冊到 game data,在 main.rs 生成 game_data
的地方,加上新的 System
use crate::system::{ShipControlSystem};
let game_data = GameDataBuilder::default()
// Render, transform bundle here ...
.with_bundle(input_bundle)?
.with(ShipControlSystem, "ship_control_system", &["input_system"]);
執行遊戲的時候,試按左右鍵就能看到在終端機上輸出值的變化了。