ggezで画像を繰り返し描画する(バッチ処理する)

# 今回読み込む画像
![world.png](https://qiita-image-store.s3.amazonaws.com/0/196544/56abeee0-a1cd-333a-ab7e-37f706cf03cc.png)
[ぴぽや](https://pipoya.net/sozai/terms-of-use-of-free-materials/)様からお借りしました。

# 本編
```rust
extern crate ggez;

use ggez::event;
use ggez::graphics;
use ggez::{Context, GameResult};
use ggez::conf;
use std::env;
use std::path;

struct MainState {
    tiles: graphics::spritebatch::SpriteBatch,
}

impl MainState {
    fn new(ctx: &mut Context) -> GameResult<MainState> {
        let tile = graphics::Image::new(ctx, "/world.png")?;
        let tiles = graphics::spritebatch::SpriteBatch::new(tile);

        let s = MainState {
            tiles,
        };
        Ok(s)
    }
}

impl event::EventHandler for MainState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx);

        for x in 0..10 {
            for y in 0..10 {
                let x = x as f32;
                let y = y as f32;
                let p = graphics::DrawParam {
                    src: graphics::Rect::new(3. / 8., 4. / 23., 1. / 8., 1. / 23.), // 分割ロード
                    scale: graphics::Point2::new(2., 2.), // 見やすくするためサイズ二倍
                    dest: graphics::Point2::new(x * 32., y * 32.), // 敷き詰める
                    .. Default::default() // 他はデフォルト
                };
                self.tiles.add(p);
            }
        }

        graphics::draw_ex(
            ctx,
            &self.tiles,
            graphics::DrawParam {
                dest: graphics::Point2::new(10., 10.), // まとまりの描画位置
                .. Default::default()
            },
        ).expect("cannot draw tile");

        graphics::present(ctx);
        Ok(())
    }
}

pub fn main() {
    let c = conf::Conf::new();
    let ctx = &mut Context::load_from_conf("super_simple", "ggez", c).unwrap();

    if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        ctx.filesystem.mount(&path, true);
    }

    let state = &mut MainState::new(ctx).unwrap();
    if let Err(e) = event::run(ctx, state) {
        println!("Error encountered: {}", e);
    } else {
        println!("Game exited cleanly.");
    }
}
```

# 実行結果

<img width="912" alt="スクリーンショット 2019-01-03 17.30.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/196544/1404f5fc-70af-5621-cfd8-812fde5f7240.png">

# 簡単な解説
今回は冒頭の画像の地面項目、左から三番目のタイルを分割ロードし、画面に敷き詰めている。
分割ロードについては[別記事](https://qiita.com/Sylba2050/items/bfa701d56f7347de49bc)にまとめている。

``` rust
let tile = graphics::Image::new(ctx, "/world.png")?;
let tiles = graphics::spritebatch::SpriteBatch::new(tile);
```
この部分で画像の読み込み、それをバッチ処理することを設定している。

```rust
for x in 0..10 {
    for y in 0..10 {
        let x = x as f32;
        let y = y as f32;
        let p = graphics::DrawParam {
            src: graphics::Rect::new(3. / 8., 4. / 23., 1. / 8., 1. / 23.), // 分割ロード
            scale: graphics::Point2::new(2., 2.), // 見やすくするためサイズ二倍
            dest: graphics::Point2::new(x * 32., y * 32.), // 敷き詰める
            .. Default::default() // 他はデフォルト
        };
        self.tiles.add(p);
    }
}
```
どのように繰り返すかの設定。今回は分割した画像を二倍に拡大したものを縦に10個、横に10個、描画している。二倍に拡大すると画像サイズが32 * 32 ピクセルなのでそれを考慮している。

```rust
graphics::draw_ex(
    ctx,
    &self.tiles,
    graphics::DrawParam {
        dest: graphics::Point2::new(10., 10.), // まとまりの描画位置
        .. Default::default()
    },
).expect("cannot draw tile");
```
画像の「まとまり」をどのように描画するかの設定。

もちろん一つずつ描画しても問題はないのだろうが、公式でこちらの手法が推奨されている。最適化も効くとのことなので速度面でも改善がみられるものと思われる(未検証)。