2020/01/30

RustでSDL2 その1

step1 初期化とクリーンアップ

C、Nimに続き、RustでもSDL2を試してみました。
Rustではsdl2クレートを使わせてもらいました。使った実感としましては、ところどころ違いがあり、ソースコードを結構読みましたが、大体同じ知識で使えるという感じでした。C、Nimと同様step1~7まで作ったものはGitHubに置いておきましたが、一応解説ということで記事にしました。

まずは動作確認としてstep1の初期化とクリーンアップのコードです。

extern crate sdl2;
use sdl2;

fn main() {
    let sdl_ctx = sdl2::init();

    match sdl_ctx {
        Ok(_) => println!("Success"),
        Err(e) => eprintln!("{}", e),
    }
}

SDL_Initにあたるものはsdl2::init()となっています。本来は初期化成功の場合は0、失敗の場合は負のエラーコードを返してきますが、Rustのsdl2はResult<Sdl, String>を返してきます。上記のように少ないコードなら何の問題もなくmatch式を使えばよいのですが、戻り値がResultというものは結構、いやむしろほとんどなので、毎回match式にするとごちゃごちゃしたコードになってしまいます。したがって、以下のような形に変更します。

extern crate sdl2;
use sdl2;

fn main() -> Result<(), String> {
    let sdl_ctx = sdl2::init()?;

    Ok(())
}

さて本来必須であるSDL_Quitでありますが、RustではSdlDropトレイトが実装されているので、終了の際には自動で行ってくれます。安心安全。

step2 ウィンドウの作成

次にstep2のウィンドウ作成をやります。コードは以下のようなものになりました。

use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use std::time::Duration;
use std::thread;

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 window = video_subsys.window(TITLE, WINDOW_WIDTH, WINDOW_HEIGHT)
        .position_centered()
        .build()
        .map_err(|e| e.to_string())?;

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

    let mut event_pump = sdl_ctx.event_pump()?;

    'running: loop {
        canvas.set_draw_color(Color::RGB(0, 255, 0));
        canvas.clear();
        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(())
}
CやNimではSDL_Init(SDL_INIT_VIDEO)といった感じで同時に行っていた処理ですが、Rustでは分けて行うようです。そしてSDL_Rendererに相当するものがCanvasになります。このあたりはソースコードを結構読みました。また上記のコードではウィンドウのxボタンでウィンドウを消すことができるようにしていますが、そのためにはevent_pumpとループが必要でした。例えば自動で消えるようにするだけならば、この部分は必要なく、thread::sleep(Duration::new(5, 0)といった感じでうまく行きます。しかし、今後どうせ使うことになりますので、この時点で書いておくのもありかと思います。

まとめ

C、Nimとは違い、違いをソースコードから探すことが結構多く、まだまだ私も完全に理解しているとはとても言い難いですが、大きな処理の流れは同じなので、何となくうまくいっている感じです。そう考えると、CでまずSDLに入門したことが「当たり」だったように思います。もっとも分かりやすかったですし、ドキュメントのほうも読みやすかったです。もしSDLをやってみようと思った方がいらっしゃいましたら、まずCからやってみることをおすすめします。そしてその後Rustでもやってみてください。同時進行が個人的にはおすすめです。