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?

Rust×Bevyゲーム開発レシピ: もっとProcessingの様に図形を描画しよう

Posted at

Bevyを使った個人でゲーム開発を行なっている登尾(のぼりお)です。

前回以下の記事でBevyを使ったProcessingライクな描画について記事を書きましたが、Mesh2dを使った描画方法で、あまりProcessing感がないなと反省しましたので、今回はさらにProcessingに寄せた図形の描画方法を解説してきます。

今回は以下のようなコードで図形を描画していてだいぶProcessing風な書き方ができていると思います。

fn user_sketch() {
    ellipse(200.0, 200.0, 400.0, 400.0, random_color());
    line(0.0, 0.0, 200.0, 200.0, Color::linear_rgb(1.0, 0.0, 0.0));
    rect(100.0, 100.0, 150.0, 100.0, Color::linear_rgb(1.0, 1.0, 0.0));
    ellipse(300.0, 250.0, 150.0, 250.0, Color::linear_rgb(1.0, 0.0, 1.0));
    triangle(
        100.0,
        250.0,
        50.0,
        350.0,
        300.0,
        350.0,
        Color::linear_rgb(1.0, 0.5, 0.0),
    );

    for _n in 0..11 {
        let mut r = rand::rng();
        let x1 = r.random_range(0.0..400.0);
        let y1 = r.random_range(0.0..400.0);
        let x2 = r.random_range(0.0..400.0);
        let y2 = r.random_range(0.0..400.0);
        line(x1, y1, x2, y2, random_color());
    }
}

実装の流れ

今回はコードの行数が300行近くになりましたので、先に全体のコードを先にご紹介します。

上記コードのポイントをかいつまんで紹介して行きます。

座標系の変換

前回同様キャンバスのサイズは400x400とし、また左上が起点になるようcanvas_to_worldを用意しています。

const CANVAS_W: f32 = 400.0;
const CANVAS_H: f32 = 400.0;

fn canvas_to_world(p: Vec2) -> Vec2 {
    Vec2::new(p.x - CANVAS_W * 0.5, CANVAS_H * 0.5 - p.y)
}

Processing風APIの用意

以下の部分でProcessing風なAPIのコマンドの受け皿を用意しています。今回は、line、Rect、Ellipse、Triangleの4つのみです。

#[derive(Clone)]
enum ProcessingCommand {
    Line {
        x1: f32,
        y1: f32,
        x2: f32,
        y2: f32,
        thickness: f32,
        color: Color,
    },
    Rect {
        x: f32,
        y: f32,
        w: f32,
        h: f32,
        color: Color,
    },
    Ellipse {
        cx: f32,
        cy: f32,
        w: f32,
        h: f32,
        color: Color,
    },
    Triangle {
        x1: f32,
        y1: f32,
        x2: f32,
        y2: f32,
        x3: f32,
        y3: f32,
        color: Color,
    },
}

それぞれを実際の関数として用意し、以下の場合であればlineの例で、この関数が呼ばれるたびにその内容を送信しています。

thread_local! {
    static TX: RefCell<Option<Sender<ProcessingCommand>>> = RefCell::new(None);
}
fn install_tx(sender: Sender<ProcessingCommand>) {
    TX.with(|c| *c.borrow_mut() = Some(sender));
}
fn send(cmd: ProcessingCommand) {
    TX.with(|c| {
        if let Some(tx) = &*c.borrow() {
            let _ = tx.send(cmd);
        }
    });
}

pub fn line(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
    send(ProcessingCommand::Line {
        x1,
        y1,
        x2,
        y2,
        thickness: 4.0,
        color: color,
    });
}

APIの呼び出し

先ほど定義したlineを呼び出している例です。Color部分はBevyのColorをそのまま利用できる様にしています。

fn user_sketch() {
    line(0.0, 0.0, 200.0, 200.0, Color::linear_rgb(1.0, 0.0, 0.0));
}

受信した情報をもとに描画

与えられたコマンドをもとに実際のMesh2dをspawnします。line関数であれば、以下の様にマッチしてMesh2dをspawnしています。

for cmd in drained {
    match cmd {
        ProcessingCommand::Line {
            x1,
            y1,
            x2,
            y2,
            thickness,
            color,
        } => {
            let a = canvas_to_world(Vec2::new(x1, y1));
            let b = canvas_to_world(Vec2::new(x2, y2));
            let delta = b - a;
            let len = delta.length();
            let angle = delta.to_angle();

            let line_mesh = meshes.add(Rectangle {
                half_size: Vec2::new(len * 0.5, thickness * 0.5),
            });
            commands.spawn((
                Mesh2d(line_mesh),
                MeshMaterial2d(materials.add(color)),
                Transform {
                    translation: Vec3::new((a.x + b.x) * 0.5, (a.y + b.y) * 0.5, 0.0),
                    rotation: Quat::from_rotation_z(angle),
                    ..default()
                },
            ));
        }
        ProcessingCommand::Rect { x, y, w, h, color } => {
          ...

おしまい

今回もこれまで同様以下の個人リポジトリで公開しています。コード量が多めでしたのでざっくり解説となりましたので、実際のコードと比較しながらの方が分かりやすいかと思います。

cloneした後に、

% cargo run --example processing_like2

で挙動を起動できます。

前回の記事よりもProcessingライクな使い方はできたかなと思いますが、あくまでもごく一部のAPIのみですので、ここから先はよりProcessing風APIをはやしたり、逆にBevyのAPIで描画することに興味を持ってもらえると幸いです。

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?