1
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 1 year has passed since last update.

先月に技術書典15や技書博9で頒布した『ゆめみ大技林 '23 (2)』で、Rustでクリエイティブコーディングをする記事を書きました。この内容はQiitaにも投稿しています。

そこで取り上げたNannouについて、本記事では追加のtipsを紹介します。Nannouを知っていることが前提となります。Nannouについて知りたい方は、上述の記事をぜひ読んでみてください。

ウィンドウサイズを指定する

nannou:app 関数を使う方法では、次のようにウィンドウを生成するコードを自分で書いていました。

fn main() {
    nannou::app(model).run();
}

fn model(app: &App) -> Model {
    let _window = app.new_window().view(view).build().unwrap();

    // 後略
}

この際に、次のように size を使ってウィンドウのサイズを指定できます。

fn model(app: &App) -> Model {
    let _window = app
        .new_window()
        .size(408, 408) // サイズを指定
        .view(view)
        .build()
        .unwrap();

    // 後略
}

ウィンドウを408x408の正方形のサイズにしています。これは筆者が作った次のコードからの抜粋です。

クリエイティブコーディングでは座標指定での描画をよく使うので、必要に応じてウィンドウサイズを指定すると便利です。

キーが押されたときの処理を書く

Nannouでは、単に描画するだけでなく、ユーザーの入力を受け付けることもできます。例えば、キーが押されたときに何か処理をしたい場合は、次のように key_pressed を使います。

fn model(app: &App) -> Model {
    let _window = app
        .new_window()
        .key_pressed(key_pressed) // キー押下イベントを処理する関数を指定
        .view(view)
        .build()
        .unwrap();

    // 後略
}

fn key_pressed(_app: &App, model: &mut Model, key: Key) {
    if key == Key::Space {
        // スペースキーが押されたときの処理
    }
}

筆者は、スペースキーが押されたときにアニメーションを開始する処理をよく書いていて、例えば次のコードでも書いています。

これは、作成したアニメーションを動画としてキャプチャしたいときに使っています。ウィンドウを表示して動画キャプチャの準備を整えたら、スペースキーを押してアニメーションを開始するという流れです。

更新処理がどのくらいの頻度で呼ばれるか確認する

Nannouの update 関数や view 関数は実行中に繰り返し呼ばれます。これによってアニメーションが実現できるわけです。このとき、これらの関数がどのくらいの頻度で呼ばれているかは気になる点でしょう。次のようにデバッグ出力すると確認できます。

fn update(app: &App, model: &mut Model, _update: Update) {
    println!("duration {:?}", app.duration);
}

app.duration はNannouの Time 型の値です。

since_start がプログラムが開始してからの経過時間、since_prev_update が前回の更新からの経過時間です。筆者の環境では、次のように表示されました(一部抜粋)。

duration Time { since_start: 1.098403958s, since_prev_update: 16.951666ms }
duration Time { since_start: 1.114849167s, since_prev_update: 16.445209ms }
duration Time { since_start: 1.131892958s, since_prev_update: 17.043791ms }
duration Time { since_start: 1.148883375s, since_prev_update: 16.990417ms }
duration Time { since_start: 1.164966542s, since_prev_update: 16.083167ms }

上記のログで since_prev_update の値を観察すると、最大と最小で1ms程度の差は出ていますが、平均すると16.7ms程度のようです。つまり、毎秒60回であろうと推測できます。

なお、Time 型には updates_per_second という、毎秒の更新回数を直接表示してくれそうなメソッドがあります。これもデバッグ出力してみましょう。

fn update(app: &App, model: &mut Model, _update: Update) {
    println!("updates_per_second {:?}", app.duration.updates_per_second());
}

筆者の環境では、次のように表示されました(一部抜粋)。

updates_per_second 62.5
updates_per_second 62.5
updates_per_second 58.82353
updates_per_second 62.5
updates_per_second 62.5

この値は since_prev_update の値をもとに計算されているようで、どのくらい信頼できる値なのかはっきりしません。ただ、やはり更新頻度は毎秒60回くらいだろうとは推測できます。

生成される乱数を固定する

クリエイティブコーディングでは乱数を使うことがよくあります。Nannouでは乱数を簡単に利用できます。例えば次のようにします。

fn update(_app: &App, model: &mut Model, _update: Update) {
    let angle = random_range(0.0, 2.0 * PI);
}

この方法を使うと毎回異なる乱数が生成されます。通常はそれで問題ありませんが、あえて毎回同じ乱数を生成したい場合もあります。例えば、乱数を使ったアニメーションだが同じ動きを何度も再現したいという場合などです。同じ乱数を生成するには、乱数生成器のシード値を明示的に指定します。

use nannou::rand::prelude::*;

struct Model {
    rng: StdRng,
}

fn model(app: &App) -> Model {
    Model {
        rng: StdRng::seed_from_u64(53),
    }
}

fn update(app: &App, model: &mut Model, _update: Update) {
    let angle = model.rng.gen_range(0.0..2.0 * PI);
}

StdRng::seed_from_u64 はシード値を指定して乱数生成器を生成する関数です。同じシード値を指定すると、生成される乱数が同じになります。シード値を変えると、生成される乱数も変わります。

上述のコードは筆者が作った次のコードからの抜粋です。

動画キャプチャの方法を試行錯誤している際に同じ動きを何度も再現したかったので、この方法を使いました。

まとめ

Rust Nannouを使うときのtipsを紹介しました。ゆめみ大技林を見てクリエイティブコーディングに興味を持った方の参考になれば幸いです。

1
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
1
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?