# Bevy

Bevy (0.7) を、非公式ブックを参照して学習したときのメモ。

基本的に 非公式ブック (opens new window) を使用して学習したことを記録する。

# 目的

Rustに入門したが、まだいまいち使えていない感じがするため、 複雑(であろう)ゲームプログラミングを題材に、 アプリケーションソフトウェアの設計・実装を経験したい。 Rustを公式ブックや市販の入門書を読んだ後、 AIZU ONLINE JUDGE の ADSL1 をやってみたり、 paiza の過去に説いた問題を Rust で解いてみたりした。 CUI上の単発コマンド的なアプリケーション実装方法は見えたが、 その他の領域での(例えばGUIアプリケーションの)実装はどのようなものか、 いまいち見えてこない。 Rustは "オブジェクトの海" に否定的な認識だが、それが使えない状況下で GUIアプリケーション等インタラクティブなソフトウェアをどのように実装するのか、 知りたい。

# 環境

WSL2 上のUbuntu 20.04 上で開発する。 ターゲットはまず Webassembly、つぎに Windows。

WSL2 上にはすでに rustup が入っている。

# 構築

非公式ブックのここ (opens new window) に詳しい。

rustup で Wasm32 をターゲットを追加する。

$ rustup target add wasm32-unknown-unknown

とりあえずの実行環境として "wasm-server-runner" を入れる。

$ cargo install wasm-server-runner

プロジェクトを作成。

$ cargo init --bin hello_bevy
$ cd hello_bevy

Cargo.toml を編集する。

[package]↲
name = "hello_bevy"↲
version = "0.1.0"↲
edition = "2021"↲
↲
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/man    ifest.html↲
↲
[dependencies]↲
bevy = "0.7"↲
↲
↲
[profile.dev]↲
opt-level=1↲
↲
[profile.dev.package."*"]↲
opt-level=3↲

.cargo/config を作成し、 run 時に "wasm-server-runner" を使用するよう設定する。

[target.wasm32-unknown-unknown]↲
runner = "wasm-server-runner"↲

src/main.rs の内容を編集する。

use bevy::prelude::*;↲
↲
fn main() {App::new().add_plugins(DefaultPlugins).run();}

ビルド時は target を指定する。初回のビルドはなかなか時間がかかる。

$ cargo run --release --target wasm32-unknown-unknown

表示されたURLへアクセスすると灰色の画面がでる。

# 未整理メモ

# 非公式ブックのNew to Bevy? Guided Tutorial! (opens new window) 読み下し

Bevy フレームワーク基本構成

  • ECS : Entity-Component-System。Bevyはアプリケーションのデータを Bevy ECS にて管理する。これは一種のDBMSと考えてよい(はず)。DBの基本構成要素が Entity と Component である。
  • Entity : Bevy上のなにかを表現するモノ。その実態は単純なID。後述のComponentを関連付けることができる。RDBでいえば組(タプル)、表でいえば行の立ち位置。
  • Component : Entityに関連付けるデータ。任意のRust型がComponentの型として定義できる。RDBでいえば属性(アトリビュート)、表でいえば列の立ち位置。
  • Resource : 単一のグローバルなデータ。Entityと関連しない。
  • System : Bevyが実行する関数。まさにシステム(の一部)を実現する。引数は特殊なルールに従う必要がある。指定可能な引数は Query(を通してComponent/Entity)・Resource・Command・Event。これらの相互作用を記述することができる。AppBundlerによりBevyに登録し、Bevyが実行する。
  • Query : Systemの引数として、参照したい Entityの Component を記述するモノ。
  • Command : ECS へのコマンド。Entity/Component・Resourceを生成・削除含め操作する。
  • Event : イベントの実装。システム間のデータフローに使う。
  • AppBundler : アプリビルダ。いわゆるビルダーパターンでアプリケーションを構築する。

# 背景色

(opens new window)

Resource ClearColor により背景色変更。

    App::new().insert_resource(ClearColor(Color::rgb(0.2, 0.8, 0.2))).add_plugins(DefaultPlugins).run();

# デバッグツール追加 bevy_editor_pls (opens new window)

crate.io に登録されてなさそうなので、GitHubリポジトリを直接参照。

Cargo.toml [dependencies] に以下追加。

bevy_editor_pls = { git = "https://github.com/jakobhellermann/bevy_editor_pls.git" }

プラグイン EditorPlugin を追加。

use bevy::prelude::*;
use bevy_editor_pls::prelude::*;

fn main() {
    App::new()
        .insert_resource(ClearColor(Color::rgb(0.2, 0.8, 0.2)))
        .add_plugins(DefaultPlugins)
        .add_plugin(EditorPlugin)
        .run();
}

# Bevy Lint (opens new window)

Bevy コードに対する lint らしい。

インストール手順 (opens new window) に従いインストール。

cargo install cargo-dylint dylint-link

openssl-sys v0.9.73 のインストールに失敗していたので、エラーメッセージに従い、 libssl-devpkg-config をインストール( apt install )。

コマンド実行時にもエラーがあったので、 libasound2-dev libudev-dev

# Examples (opens new window) を動かしてみる。

下記2個をコピペして動かしてみる。

アセットはプロジェクトディレクトリの assets/ 下に置くものらしい。

bevy_hello$ mkdir assets
bevy_hello$ cd assets
assets$ wget https://github.com/bevyengine/bevy/raw/latest/assets/branding/icon.png

以下はサンプルを切り貼りして作ったコード。

use bevy::prelude::*;
use bevy_editor_pls::prelude::*;

fn main() {
    App::new()
        .insert_resource(ClearColor(Color::rgb(0.4, 0.4, 0.8)))
        .add_plugins(DefaultPlugins)
        .add_plugin(EditorPlugin)
        .add_startup_system(setup)
        .add_system(sprite_movement)
        .run();
}

fn setup(mut commands: Commands,
         asset_server: Res<AssetServer>) {
    commands.spawn_bundle(OrthographicCameraBundle::new_2d());

    commands.spawn_bundle(SpriteBundle {
        sprite: Sprite {
            color: Color::rgb(0.25, 0.25, 0.25),
            custom_size: Some(Vec2::new(50.0, 50.0)),
            ..default()
        },
        ..default()
    });

    commands.spawn_bundle(SpriteBundle {
        texture: asset_server.load("icon.png"),
        transform: Transform::from_xyz(100., 0., 0.),
        ..default()
    })
    .insert(Direction::Up);

}

#[derive(Component)]
enum Direction {
    Up,
    Down,
}

fn sprite_movement(
    time: Res<Time>,
    mut sprite_position: Query<(&mut Direction, &mut Transform)>) {

    for (mut logo, mut transform) in sprite_position.iter_mut() {
        match *logo {
            Direction::Up => transform.translation.y += 150. * time.delta_seconds(),
            Direction::Down => transform.translation.y -= 150. * time.delta_seconds(),
        }

        if transform.translation.y > 200. {
            *logo = Direction::Down;
        } else if transform.translation.y < -200. {
            *logo = Direction::Up;
        } else {
            // nothing
        }
    }
}

# ビルドしたWasmをサーバで動かす

以下参考に・・・ Wasm (opens new window)

環境 にて作成した環境を引継ぎ使用する。以下その手順。

  1. ビルドして wasm ファイルを作成する
  2. wasmファイルを実行するための環境を作る
    • 今回は wasm-bindgen-cli (opens new window) を使用する wasm-bindgen-cli を使うと、wasmファイルを実行するために必要な js ファイルを作成してくれる
    • wasm-bindgen-cli の生成したjsを読み込む html を作成する
    • アセットファイルを配置する
  3. サーバの静的ファイルとして配置する

# ビルドして wasm ファイルを作成する

ビルド cargo build --target wasm32-unknown-unknown すると、 target 下に成果物ができる。

プロジェクト名を bevy_hello にしていると、以下にできている。 target/wasm32-unknown-unknown/debug/bevy_hello.wasm

本来は --release でビルドし、 target/wasm32-unknown-unknown/release/bevy_hello.wasm だが、今回はサボって debug ビルドのものを配置する。

# wasmファイルを実行するための環境を作る

今回は生成物を wasm-bindgen/wasm/ に出力するものとする。

wasm-bindgen-cli をインストールする。

$ cargo install wasm-bindgen-cli

wasm-bindgen-cli を実行し、wasm読み込みファイル一式を出力する。

$ wasm-bindgen --out-name hello --out-dir wasm-bindgen/wasm/target --target web target/wasm32-unknown-unknown/debug/bevy_hello.wasm

Example の WASM/Build&Run (opens new window)example HTML file (opens new window) をコピーして、 wasm-bindgen/wasm/index.html に配置し、その中のjs名を修正する。今回は hello.js に変更する。
























 




<html>
  <head>
    <meta charset="UTF-8" />
    <style>
      body {
        background: linear-gradient(
          135deg,
          white 0%,
          white 49%,
          black 49%,
          black 51%,
          white 51%,
          white 100%
        );
        background-repeat: repeat;
        background-size: 20px 20px;
      }
      canvas {
        background-color: white;
      }
    </style>
  </head>
  <script type="module">
    import init from './target/hello.js'
    init()
  </script>
</html>

アセットを使用していれば、 wasm-bindgen/wasm/assets/ 配下にアセットファイルをコピーする。

bevy_hello$ cp -r assets wasm-bindgen/wasm/

この時点で wasm-bindgen/ 以下は下記のようになっている(はず)。

bevy_hello$ cd wasm-bindgen
wasm-bindgen$ tree
.
└── wasm
    ├── assets
    │   └── icon.png
    ├── index.html
    └── target
        ├── hello.d.ts
        ├── hello.js
        ├── hello_bg.wasm
        └── hello_bg.wasm.d.ts

3 directories, 6 files

# サーバの静的ファイルとして配置する

wasm-bindgen/wasm/ 以下をサーバの静的ファイルとして丸っとコピーする。

bevy_hello$ cp -r wasm-bindgen/wasm/* <静的ファイル置き場所>

結果がこちら (opens new window)

# TODO

Last Updated: 5/28/2022, 6:45:42 PM