はじめに
Rustでconrodを使って赤い円をアニメーションさせる からの続きです。
今回は、
- 動かす円の個数を動的に変更する(widget ID の配列を使う)
- ボタンによって上記を行う(UIイベントを処理する)
という辺りをやっていこうと思います。
この辺までできると割と色々と応用が効くように思います。
コード全体はこちらになります。
Rustでconrodを使って動く円の個数をボタンで増減させる
ポイント
動的に増減するWidget Objectは hogehoge[] で宣言する
今回は circles[]
を宣言しています。すると内部でVector
な感じで定義されるみたいです(Macroマジック)。
widget_ids! {
struct Ids {
circles[],
button_up,
button_down,
}
}
ButtonイベントのHandlingとWidget Objectの増減のさせかた
Buttonイベントは以下のようにHandlingするっぽいです。
なんか斬新。
for click in widget::Button::new()...set() { /*クリックされたときの処理*/ }
Widget Object (今回のcircles) を増減させるときは、
ids.circles.resize(size, id_generator)
を使うようです。
この size
を実際の円のサイズを渡せば良いです。生成されるコードを見ると、
- 5 -> 10 に増えるとき: 後ろに5個IDが追加されているっぽい
-
10 -> 5 に減るとき: 後ろの5個のIDが削除されているっぽい
です。
あとは、初めてのIteratorって感じで iter_mut().enumerate()
を見つけるのに苦労したりしました...
animation.rs の 抜粋
pub fn next_frame(&mut self, ref mut ui: conrod_core::UiCell) {
self.n_frame += 1;
let (width, height) = (ui.win_w, ui.win_h);
for (i, ball) in self.balls.iter_mut().enumerate() {
ball.x += ball.dx;
ball.y += ball.dy;
if ball.x < -width/2. || width/2. < ball.x {
ball.dx = -ball.dx;
}
if ball.y < -height/2. || height/2. < ball.y {
ball.dy = -ball.dy;
}
widget::Circle::fill(10.)
.x_y(ball.x, ball.y)
.color(conrod_core::color::RED)
.set(self.ids.circles[i], ui);
}
for _click in widget::Button::new()
.mid_top()
.w_h(100.0, 40.0)
.label("↑増やす")
.label_font_size(12)
.set(self.ids.button_up, ui) {
self.balls.push(Ball::new(&mut self.thread_rng, width, height));
self.ids.circles.resize(self.balls.len(), &mut ui.widget_id_generator());
}
for _click in widget::Button::new()
.w_h(100.0, 40.0)
.label("↓減らす")
.label_font_size(12)
.set(self.ids.button_down, ui) {
self.balls.pop();
self.ids.circles.resize(self.balls.len(), &mut ui.widget_id_generator());
}
}
Buttonラベルに文字を使うためのFontを設定する
こんな感じで設定できるみたいです。 find_folder
は 外部crate です。
let assets = find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap();
let font_path = assets.join("fonts/ipam.ttf");
ui.fonts.insert_from_file(font_path).unwrap();
winit イベントを conrod イベントに変換する
winit というベースとしているライブラリのイベントを、conrod用に変換してUI系に渡す必要があるっぽいです。とりあえず、サンプルを参考に以下のように書いておくと良いみたいです。
// Use the `winit` backend feature to convert the winit event to a conrod one.
if let Some(event) = event::convert_event(event.clone(), &display) {
ui.handle_event(event);
}
さいごに
とりあえず動いたので良しとします。
実行効率の良いコードなのか、というと円が少ない状態でもそれなりにCPUリソースを消費(10%~15%)しているので判断しづらいですが、 400x400 を 60fps で更新するとこんなものなのかなぁ、という気がしないでもないです。