ステージやプレイヤーキャラクターを描画するときは、現在のカメラの位置を反映した場所に描画しなければなりません。そのためには、ステージを描画する関数やキャラクターを描画する関数に、現在のカメラの位置の情報を渡す必要があります。このとき、単にカメラの位置を引数に渡して伝播するのではなく、グラフィックスコンテキストとしてさまざまな情報をまとめて伝播する設計がよく使われています。今回も自力でそれを定義しておきましょう。
グラフィックスコンテキスト
グラフィックコンテキストには以下のように、描画位置とdx
, dy
と、アニメーションで使う用の現在のフレーム数、それからデバッグ描画をするかどうかというフィールドをもたせました。
#[derive(Clone, Copy, Default)]
pub struct Graphics {
pub frame_count: u32,
pub debug: bool,
pub dx: i32,
pub dy: i32,
}
それから、画像を描画するメソッドをこのグラフィックスコンテキストに生やします。このとき、グラフィックコンテキストの描画位置を反映させるようにします。
impl Graphics {
pub fn draw(&self, image: &Image, x: i32, y: i32, flags: u32) {
image.draw(x + self.dx, y + self.dy, flags);
}
...
}
このグラフィックスコンテキストを作るときは、プレイヤーキャラクターの位置に応じた値で初期化します。
let player_center = self.player.center();
let dx = wasm4::SCREEN_SIZE as i32 / 2 - player_center.x.floor() as i32;
let dy = wasm4::SCREEN_SIZE as i32 / 2 - player_center.y.floor() as i32;
let graphics = Graphics {
frame_count: self.frame_count,
debug: self.debug,
dx,
dy,
};
これで、graphics.draw
メソッドで画像を描画すれば、プレイヤーキャラクターがどう動き回っても、プレイヤーキャラクターが画面の中心に来るようになります。
カメラのブレの演出
これを応用すると、ダメージを受けたときなどに画面をブレさせる演出もできそうです。ゲーム全体を表す構造体 GameScene
に、画面の振動の量を表すフィールドvibration
を付け加えます。
#[derive(Clone)]
pub struct GameScene {
...
vibration: i32,
}
それから、この vibration
を 振り幅にして、振動自体は f32::cos
で算出することにしました。f32::cos(self.frame_count as f32))
だとちょっと振動するスピードが早すぎた感じがしたので、0.5
をかけて振動のスピードを遅くしています。このあたりは好みの問題なので好きに調整するといいと思います。
let graphics = Graphics {
frame_count: self.frame_count,
debug: self.debug,
dx: dx + (self.vibration as f32 * f32::cos(self.frame_count as f32 * 0.5)) as i32,
dy,
};
また、振り幅をだんだん小さくすることで、やがてもとの状態に戻るようにします。ここでは毎フレーム1
づつ減らすようにしましたが、半分づつにしていくような方法もあるかなと思います。このあたりもお好みです。
self.vibration = i32::max(0, self.vibration - 1);
今回のゲームでは、ダメージブロックに触れるとプレイヤーキャラクターが吹っ飛びますが、そのときにこの画面が振動する演出を使っています。
次回予告
Rustはライフタイムや借用が難しいといわれますが、次回は私も少しライフタイムで戸惑った話です。