2020/02/02

RustでSDL2 その2

step3 画像の表示

前回はウィンドウの表示までできましたが、今回は画像を表示してみようと思います。C、NimではSDLの初期化、ウィンドウ作成、レンダラー作成、画像を読み込んでサーフェスへ、サーフェスからテクスチャ作成、サーフェス解放、レンダラーで表示、諸々クリーンアップといった流れになっていましたので、同じようにやってみます。

use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::image::{LoadSurface, InitFlag};
use sdl2::rect::Rect;
use sdl2::surface::Surface;
use std::time::Duration;
use std::thread;
use std::path::Path;

const TITLE: &'static str = "My SDL Window";
const WINDOW_WIDTH: u32 = 640;
const WINDOW_HEIGHT: u32 = 480;


fn main() -> Result<(), String> {
    let sdl_ctx = sdl2::init()?;
    let video_subsys = sdl_ctx.video()?;
    let _image_ctx = sdl2::image::init(InitFlag::PNG)?;

    let window = video_subsys.window(TITLE, WINDOW_WIDTH, WINDOW_HEIGHT)
        .position_centered()
        .build()
        .map_err(|e| e.to_string())?;

    let mut canvas = window.into_canvas()
        .accelerated()
        .build()
        .map_err(|e| e.to_string())?;

    let img = Path::new("imgs/newyearalien2.png");
    let surface = Surface::from_file(&img)?;
    let dest = Rect::new(
        ((WINDOW_WIDTH - 300) / 2) as i32,
        ((WINDOW_HEIGHT - 300) / 2) as i32,
        300,
        300
    );

    let texture_creator = canvas.texture_creator();
    let tex = texture_creator.create_texture_from_surface(&surface)
        .map_err(|e| e.to_string())?;
    
    let mut event_pump = sdl_ctx.event_pump()?;

    'running: loop {
        canvas.copy(&tex, None, dest)?;
        canvas.present();

        for ev in event_pump.poll_iter() {
            match ev {
                Event::Quit {..} |
                    Event::KeyDown {keycode: Some(Keycode::Escape), ..} =>
                    break 'running,
                _ => {}
            }
        }
        thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
    }

    Ok(())
}
sdl2::imageを使う場合、公式のexamplesのドキュメントにもありますが、まずCargo.tomlのdependenciesを以下のように変更します(バージョンは作成当時は0.32.2でしたが、現在(2020/1/31)は0.33.0となっています)

[dependencies.sdl2]
version = "0.32.2"
default-features = false
features = ["image"]
これでsdl2::imageを使用することができます。
全体としては大体最初に述べたものと同じような流れで書くことができました。Rustに入門したての方々のために書いておきますが、ループのところに'running: loopというコードがありますが、これはsdl2クレートの機能ではありません。Rustではこのようにループブロックにラベルを付けることができ、そのラベルをbreakの際などの制御にも使うことができます。sdl2に戻ります。ちょっと違うのは、Rectですが、これは今後使うので、先に書いておきました。CにおけるIMG_LoadSurface::from_fileという関数がありましたので、そちらを使用しました。

step4 アニメーション

続いては、先程表示した画像を上昇させていくアニメーションですが、ソースコードはほとんど変わりませんので、部分的に掲載します。

    let mut dest = Rect::new(
        ((WINDOW_WIDTH - 300) / 2) as i32,
        480,
        300,
        300
    );

    let texture_creator = canvas.texture_creator();
    let tex = texture_creator.create_texture_from_surface(&surface)
        .map_err(|e| e.to_string())?;

    let mut event_pump = sdl_ctx.event_pump()?;
    'running: loop {
        canvas.clear();
        canvas.copy(&tex, None, dest)?;
        canvas.present();

        for ev in event_pump.poll_iter() {
            match ev {
                Event::Quit { .. }
                | Event::KeyDown { keycode: Some(Keycode::Escape), ..} =>
                    break 'running,
                _ => {}
            }
        }
        if dest.y >= -(dest.height() as i32) {
            dest.set_y(dest.y - SCROLL_SPEED / 60);
        } else {
            break 'running;
        }

        thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
    }

    Ok(())
画像の最初の表示位置をRectの部分で変更しています。その方がアニメーションが分かりやすいかなと思いまして。基本的に座標x、yは左上が開始位置になっていますので、表示位置などはそれをもとに計算します。今回はここまで。