2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SprocketAdvent Calendar 2019

Day 2

Rustでconrodを使って動く円の個数をボタンで増減させる

Last updated at Posted at 2019-12-01

はじめに

Rustでconrodを使って赤い円をアニメーションさせる からの続きです。

今回は、

  • 動かす円の個数を動的に変更する(widget ID の配列を使う)
  • ボタンによって上記を行う(UIイベントを処理する)

という辺りをやっていこうと思います。
この辺までできると割と色々と応用が効くように思います。

コード全体はこちらになります。

Rustでconrodを使って動く円の個数をボタンで増減させる

ポイント

動的に増減するWidget Objectは hogehoge[] で宣言する

今回は circles[] を宣言しています。すると内部でVector な感じで定義されるみたいです(Macroマジック)。

animation.rs
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 の 抜粋
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 です。

main.rs
    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系に渡す必要があるっぽいです。とりあえず、サンプルを参考に以下のように書いておくと良いみたいです。

main.rs
            // 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 で更新するとこんなものなのかなぁ、という気がしないでもないです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?