Rust Bevy を使ってみたメモ テトリスを作ってみる⑤
移動を実装したので見た目がだいぶテトリスっぽくなってきましたねぇ!
今回はそんなに難しいことはしていないので早速コードを紹介していきたいと思います
・テトリミノを移動させる
の一本立てでやっていきます!
bevyのバージョン:0.13.0
rundのバージョン:0.8.5
参考:Rustゲームエンジンbevyでテトリスを作る | makibishi throw
List of all items in this crate (docs.rs)
目次
左移動
まず、左移動を実装していきます
Rust Bevy を使ってみたメモ キーボード入力を受け付ける – 趣味ブログ (shumi-blog.com) の記事でも書いたようにキーボード入力を受けつけるのはそこまで難しくないです
if key_input.pressed(KeyCode::ArrowLeft) {//キー入力を受け付ける
for prev_position in &prev_positions {//テトリミノの各ポジションに対して移動可能か調べる
if prev_position.0 + pos.x == 0 ||//今回は左移動なので左端にあると移動できない
is_exsist.0[(prev_position.0 + pos.x - 1) as usize][(prev_position.1 + pos.y) as usize] {
flag = true;//フラグを立てる
}
println!("{:?}, {:?}", prev_position.0 + pos.x - 1, prev_position.1 + pos.y);
}
if flag {
println!("衝突!");
}else {
println!("左移動");
pos.x -= 1;//フラグが立ってなかったら左移動
}
}
上のコードで左移動が実装できます
簡単ですね
is_exsist.0[(prev_position.0 + pos.x – 1) as usize][(prev_position.1 + pos.y) as usize]
ココのコードは移動先にすでにブロックがあると移動できないことを表しています
右移動
右移動はさっきの左移動とほぼ同じコードになります
if key_input.pressed(KeyCode::ArrowRight) {
for prev_position in &prev_positions {
if prev_position.0 + pos.x == 9 ||//右移動なので右端にあると移動できない
is_exsist.0[(prev_position.0 + pos.x + 1) as usize][(prev_position.1 + pos.y) as usize] {
flag = true;
}
println!("{:?}, {:?}", prev_position.0 + pos.x + 1, prev_position.1 + pos.y);
}
if flag {
println!("衝突!");
}else {
println!("右移動");
pos.x += 1;//フラグが立ってなかったら右移動
}
}
左移動のコードとほぼ同じです
異なる点は、コメントがついている部分くらいですかね
下移動
下移動もコードはほとんど変わりません
if key_input.pressed(KeyCode::ArrowDown) {
for prev_position in &prev_positions {
if prev_position.1 + pos.y == 0 ||//下移動なので下端にあると移動できない
is_exsist.0[(prev_position.0 + pos.x) as usize][(prev_position.1 + pos.y - 1) as usize] {
flag = true;
}
println!("{:?}, {:?}", prev_position.0 + pos.x + 1, prev_position.1 + pos.y);
}
if flag {
println!("衝突!");
}else {
println!("下移動");
pos.y -= 1;//フラグが立ってなかったら下移動
}
}
左移動とほぼ同じコードですが、prev_positionとposの部分がけっこう変わってます
これは、移動の向きが変わったからですね
瞬間移動
最後に瞬間移動を実装していきます
瞬間移動はあれです。上ボタンを押したときに落ちれるところまでテトリミノが移動するやつです
あれの正式名称しらない…
コードは下移動とほぼ変わりません。移動できなくなるまでテトリミノを下に移動させてるだけです
if key_input.pressed(KeyCode::ArrowUp) {
for _ in 0..20 {//0~20まで(21回)繰り返す
for prev_position in &prev_positions {
if prev_position.1 + pos.y == 0 ||
is_exsist.0[(prev_position.0 + pos.x) as usize][(prev_position.1 + pos.y - 1) as usize] {
flag = true;
}
println!("{:?}, {:?}", prev_position.0 + pos.x + 1, prev_position.1 + pos.y);
}
if flag {
println!("衝突!");
break;//地面に衝突したらループ脱出
}else {
pos.y -= 1;//フラグが立ってなかったら下移動
}
}
}
for文を使って下移動を繰り返しています
地面に衝突したらループを脱出して、地面に衝突するまで下移動を繰り返している感じです
ループ回数が21回なのはテトリスフィールドが10×20だからですね
移動をまとめる
以上のコードをまとめて書くと以下のようなコードになります
//入力のクールタイム(これがないと早すぎる)Appへの登録も忘れずに!!
#[derive(Resource)]
struct InputCoolTime(Timer);
//入力を受けとってテトリミノを移動と回転 Appへの登録も忘れずに!!
fn tetrimino_movememt (
time: Res<Time>,
mut game_timer: ResMut<InputCoolTime>,
mut tetrimino_query: Query<(Entity, &mut Position, &Children), (With<Tetrimino>, With<Active>)>,
tetrimino_flagment_query: Query<&Transform, With<TetriminoFlagment>>,
mut spawn_tetrimino_event_writer: EventWriter<SpawnTetriminoEvent>,
mut is_exsist: ResMut<IsExsist>,
key_input: Res<ButtonInput<KeyCode>>
) {
let mut prev_positions: Vec<(i32, i32)> = vec![];
let mut flag: bool = false;
if game_timer.0.tick(time.delta()).just_finished() {
return;
}
if game_timer.0.tick(time.delta()).just_finished() {
tetrimino_query
.iter_mut()
.for_each(|(entity, mut pos, children)| {
for &child in children.iter() {
let child_x: i32 = tetrimino_flagment_query.get(child).unwrap().translation[0] as i32;
let child_y: i32 = tetrimino_flagment_query.get(child).unwrap().translation[1] as i32;
prev_positions.push((child_x, child_y));
}
if key_input.pressed(KeyCode::ArrowLeft) {//左移動
//省略!
}
}else if key_input.pressed(KeyCode::ArrowRight) {//右移動
//省略!
}else if key_input.pressed(KeyCode::ArrowDown) {//下移動
//省略!
}else if key_input.pressed(KeyCode::ArrowUp) {//瞬間移動
//省略!
}
});
}
}
まとめるとこんな感じのコードを書くといいんじゃないかなと思います
けどまとめるかまとめないかは個人の好みじゃないかな??
左移動だけするファンクションとかを作ってもいいと思います
コード中には書いてあるのですが、InputCoolTime というタイマーを新しく作成しました。これがないと移動がデフォルトで瞬間移動みたいになります(なりました)
とりあえず適当に0.1秒でリピートする設定にしておきました
あと、落下のタイミングと重なると予期せぬエラーが発生しそうなので落下のタイミングで処理をしないようにしています。つまりこのタイミングに合わせてキーを押すと移動が発生しません!!
これに関してはもうちょっと対処法を考えておかないとなって思ってます
タイマーのスタート時間を少しずらせばいけそうな気がするんだけど…
まとめ
今回は移動キー入力による移動を実装しました
おそらくそんな疑問点とかも出ないんじゃないかなって思います
目次
・
・
・
雑記
今回はキーボード入力の受け付けだけだったので比較的簡単に実装できました!!
エラーが少ないと気持ちがとても楽ですねぇ
次回は回転を実装すると思うのですが、とっても難しそう…と思っています…
あとは回転と列の消去が実装できればテトリスっぽくなるんじゃないかな??
あともう少しですね…
テトリスを実装し終わったら、ブログの大改造でもしようかな?
個人的にこのブログめちゃくちゃ見ずらいんですよね
っと先のことを考えても仕方がないので、とりあえずテトリスの実装に全力で取り組もうと思います