step7 マウス操作
いよいよ、とりあえず最後のステップ7です。今回はマウスで操作するものを作ります。といってもキーボード操作の時と大きく違うことはありません。共通しているのは座標をどう扱うかというだけです。コードは以下のようになりました。
use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::mouse::MouseState;
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;
const SPEED: i32 = 300;
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 mut dest = Rect::new(
((WINDOW_WIDTH - surface.width()) / 2) as i32,
((WINDOW_HEIGHT - surface.height()) / 2) as i32,
surface.width() / 4,
surface.height() / 4
);
let mut x_pos = ((WINDOW_WIDTH - dest.width()) / 2) as i32;
let mut y_pos = ((WINDOW_HEIGHT - dest.height()) / 2) as i32;
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 {
let mouse_state = MouseState::new(&event_pump);
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,
_ => {}
}
}
let mut x_vel;
let mut y_vel;
let target_x = (mouse_state.x() - dest.w / 2) as f64;
let target_y = (mouse_state.y() - dest.h / 2) as f64;
let delta_x = target_x - (x_pos as f64);
let delta_y = target_y - (y_pos as f64);
let distance = (delta_x * delta_x + delta_y * delta_y).sqrt();
if distance < 5.0 {
x_vel = 0;
y_vel = 0;
} else {
x_vel = (delta_x * (SPEED as f64) / distance) as i32;
y_vel = (delta_y * (SPEED as f64) / distance) as i32;
}
if mouse_state.left() {
x_vel = -(x_vel);
y_vel = -(y_vel);
}
x_pos += x_vel / 60;
y_pos += y_vel / 60;
if x_pos <= 0 { x_pos = 0; }
if y_pos <= 0 { y_pos = 0; }
if x_pos >= ((WINDOW_WIDTH - dest.width()) as i32) {
x_pos = (WINDOW_WIDTH - dest.width()) as i32;
}
if y_pos >= ((WINDOW_HEIGHT - dest.height()) as i32) {
y_pos = (WINDOW_HEIGHT - dest.height()) as i32;
}
dest.set_y(y_pos as i32);
dest.set_x(x_pos as i32);
thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
Ok(())
}
このプログラムでは、画像がマウスのカーソルを追いかけてきます。しかし、マウスを左クリックすると逃げます。なんとなく昔やったファミコンのゲームを思い出しました。画像が敵で、マウスのカーソルが自分のキャラ。攻撃すると敵が一時的に逃げて、また追いかけてくる。そういった動作に感じました。これらのプログラムをうまく組み合わせていくことで、ゲームというものが出来上がっているのだろうな、と感じました。我々のような昭和世代たちは、ゲームはダメなものとして育てられてきましたが、プログラミングを知った今だから思うことは「ゲームプログラミングはプログラミングの技術を結集したもの」なんじゃないか、ということです。今回はグラフィックなところだけですが、ゲームには効果音、音楽といった音のプログラムも含まれます。以前WAVEファイルの書き込みを自作してやってみましたが、本を少しかじった程度では理解できないものでした。そこには物理の知識が必要に思えました。そのあたりの知識が身につけば、画像だけでなく、SDL_Mixerを使って効果音などもつけることが可能になります。
まとめ
ステップ1から7まで、きちゃないコードを晒してきましたが、何か役立つものがあれば幸いです。間違い、より良い方法、もしくは「こんなこともできますよ!」といったことがございましたら、気軽にお声掛けください。勉強させていただきます。