9
2

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 5 years have passed since last update.

Rustで書かれたゲームエンジンamethyst勉強記#2

Last updated at Posted at 2019-04-28

<< [#1 ウィンドウの作成](https://qiita.com/9laceef/items/380ac0944199de7aad8e)  [System編](https://qiita.com/9laceef/items/b1e9496717e0a60e9b7e) >>   [番外編(画像の色を変化させる)](https://qiita.com/9laceef/items/50fe0a262fb5f15315ab) >>

ご注意!

この記事の内容は2019/06/17現在の最新版amethystクレート(v0.11.0)では仕様が変わっており動きません!随時更新していきますので、しばしお待ちください。
もし更新が待てなかったり直接尋ねたいことなどありましたらコメントにてお願いいたします。

はじめに

前回の記事(ウィンドウ作成編)にたくさんのいいねがついていました。ありがとうございます。
モチベが上がったので、頑張って続けていこうと思います。

今回は、前回作成したウィンドウに画像を表示してみようと思います。サンプルコードexamples/02_image/にあたります。

main.rs

今回からはコード量が多くなるので、部分的に説明してから最後に全部見せようと思います。

struct ExampleState;

impl SimpleState for ExampleState {
    fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) {
        let world = data.world;
        init_camera(world);
        init_image(world);
    }
}

これは前回も紹介した、SinpleStateトレイトを実装したExampleStateを定義しています。今回はhandle_eventメソッドではなく、on_startメソッドのみ実装します。
data.worldにより、この世界への可変参照を得ます。その後それを用いてカメラと画像の登録をします。

fn init_camera(world: &mut World) {
    let camera = Camera::from(Projection::orthographic(
        -250.0, 250.0, -250.0, 250.0
    ));
    let mut transform = Transform::default();
    transform.set_xyz(250.0, 250.0, 1.0);
    world
        .create_entity()
        .with(camera)
        .with(transform)
        .build();
}

先ほどの世界への可変参照を引数として、カメラの登録を行います。
Camera::from()によりCamera構造体を作成します。引数となるのはProjection列挙子(=>ドキュメント)で、PerspectiveOrthograpgicの二つがあり、それぞれProjection::perspecive()Projection::orthographic()で作成できます。前者は遠近感有の描画、後者は無しの描画をします。今回は後者で問題ありません。
Projection::orthographic()により、描画範囲をカメラから見て上下左右250pxずつにします。引数は、(左、右、下、上)となっています。この世界では右・上が正になっています(関数によっては例外がありますがここでは省略します。後ほど出てきます)。
カメラに(0,0)から(500,500)までを描画してもらうために、オブジェクトの座標を(250,250)に設定します。TransformはUnityのtransformコンポーネントとと同じように、rotationやscaleも持っています。
さて、これがこの関数の最後です。
create_entity()で空のエンティティを作成し、with()で様々なコンポーネントを付与していきます。やっぱりUnityみたいですね。最後にbuild()で登録完了です。

fn init_image(world: &mut World) {
    let loader = world.read_resource::<Loader>();
    let texture_storage = world.read_resource::<AssetStorage<Texture>>();
    let texture_handle = loader.load(
        "logo.png",
        PngFormat,
        TextureMetadata::srgb_scale(),
        (),
        &texture_storage,
    );
    
    let mut transform = Transform::default();
    transform.set_xyz(250.0, 250.0, 0.0);
    world
        .create_entity()
        .with(texture_handle)
        .with(transform)
        .build();
}

load_resource()により、型パラメータで指定したリソースを得ることができ、今回必要なLoaderAssetStorageを取得しています。
load()によってTextureHandleを作成しており、ここでは画像パスと画像形式などを設定します。しかしなぜ画像のファイル名だけでよいのでしょう? 答えは前回ちょっとだけ触れましたが、"ゲームの場所"を決めていたんですね。Application::new()に与えた第一引数です。普通のパス指定では./examples/02_image/logo.pngになるはずなのですが(後ほどコード全体を紹介します)、./examples/02_image/にゲームが存在しているので、logo.pngだけで良いということなのです。
後は先ほどのようにエンティティを登録して終了です。

fn main() -> amethyst::Result<()> {
    amethyst::start_logger(Default::default());

    let config = DisplayConfig::load("./examples/02_image/config.ron");
    let pipe = Pipeline::build().with_stage(
        Stage::with_backbuffer()
            .clear_target([0.0, 0.0, 0.0, 1.0], 1.0)
            .with_pass(DrawFlat2D::new().with_transparency( // ... (1)
                ColorMask::all(),
                ALPHA,
                Some(DepthMode::LessEqualWrite),
            )),
    );
    let render_bundle = RenderBundle::new(pipe, Some(config));

    let transform_bundle = TransformBundle::new(); // ... (2)

    let game_data = GameDataBuilder::new()
        .with_bundle(render_bundle)?
        .with_bundle(transform_bundle)?;

    let mut game = Application::new("./examples/02_image/", ExampleState, game_data)?;

    game.run();

    Ok(())
}

main関数も所々変わっています。
(1)では、png画像のため透過度を有効にしています。今後png画像を利用する場合はこのままコピペでいいでしょう。
(2)では、新しくTransfromBundleを追加しています。Transformを利用するために必要になります。

これでコードの説明は終了です。完成したプログラムはこちらになります。

// examples/02_image/main.rs

use amethyst::{
    prelude::*,
    core::{
        Transform, TransformBundle
    },
    renderer::{
        DisplayConfig, Pipeline, Stage, DrawFlat2D, ColorMask, ALPHA, DepthMode,
        Camera, Projection,
        Texture, PngFormat, TextureMetadata,
        RenderBundle
    },
    assets::{
        Loader, AssetStorage
    },
};

struct ExampleState;

impl SimpleState for ExampleState {
    fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) {
        let world = data.world;
        init_camera(world);
        init_image(world);
    }
}

fn main() -> amethyst::Result<()> {
    amethyst::start_logger(Default::default());

    let config = DisplayConfig::load("./examples/02_image/config.ron");
    let pipe = Pipeline::build().with_stage(
        Stage::with_backbuffer()
            .clear_target([0.0, 0.0, 0.0, 1.0], 1.0)
            .with_pass(DrawFlat2D::new().with_transparency(
                ColorMask::all(),
                ALPHA,
                Some(DepthMode::LessEqualWrite),
            )),
    );
    let render_bundle = RenderBundle::new(pipe, Some(config));

    let transform_bundle = TransformBundle::new();

    let game_data = GameDataBuilder::new()
        .with_bundle(render_bundle)?
        .with_bundle(transform_bundle)?;

    let mut game = Application::new("./examples/02_image/", ExampleState, game_data)?;

    game.run();

    Ok(())
}

fn init_camera(world: &mut World) {
    let camera = Camera::from(Projection::orthographic(
        -250.0, 250.0, -250.0, 250.0
    ));
    let mut transform = Transform::default();
    transform.set_xyz(250.0, 250.0, 1.0);
    world
        .create_entity()
        .with(camera)
        .with(transform)
        .build();
}

fn init_image(world: &mut World) {
    let texture_handle = {
        let loader = world.read_resource::<Loader>();
        let texture_storage = world.read_resource::<AssetStorage<Texture>>();
        loader.load(
            "logo.png",
            PngFormat,
            TextureMetadata::srgb_scale(),
            (),
            &texture_storage,
        )
    };

    let mut transform = Transform::default();
    transform.set_xyz(250.0, 250.0, 0.0);
    world
        .create_entity()
        .with(texture_handle)
        .with(transform)
        .build();
}

お疲れさまでした!
cargo run --example 02でamethystのロゴがでっかく表示されたら成功です。

9
2
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
9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?