ボタン二回押しで走らせるプログラム with OpenSiv3D

はじめに

f:id:movementi:20180520123613p:plain
馬を走らせます

星のカービィシリーズやマインクラフトなどの多数の有名作品にも搭載されている「ボタン2回押しで走るシステム」(2回目は押しっぱなし)。

今回はそのシステムをOpenSiv3Dで作ってみたので紹介したいと思います。

Siv3Dのバージョン

OpenSiv3D 0.2.5を使います。

コード全文

f:id:movementi:20180520123348g:plain
こんな感じに動きます

コピペするだけで動きます。短い時間ですが徐々に加速減速する仕様です。ボタンを押してすぐ最高速になったり離してすぐ停止したりしないわけですね。

見ての通りコメントを山程書き込んでいるのでブログで解説することが殆どないのですが少しだけ解説します。

走る仕様

今回の馬が走る仕様は

  • 「基本は歩行モード」
  • 「方向キーが離されたら待機モードに移行」
  • 「待機モードで一定時間経過すると歩行モードに移行」
  • 「待機モード中に方向キーを押すと走りモードに移行し走る」
  • 「走りモード中に方向キーが押されていなかったら待機モードに移行」

としました。ボタンを離してから一定時間以内にまたボタンを押すのがミソですね。カービィシリーズはこの仕様です。

enum class Run :uint8 {
    walking, // 歩き
    standby, // 走り待機
    running // 走り
};

馬の状態はenum classで管理します。

待機モードの時間判定はSiv3DのStopwatch型を使い時間を測定しています。

Player::running関数でボタンの状況に応じて馬の状態を変更しているのでコメントと一緒に御覧ください。

f:id:movementi:20180520123234p:plainf:id:movementi:20180520123616p:plainf:id:movementi:20180520123613p:plain
3つの状態一覧

馬の向き

if (wide() > 0.0) m_direction = Vec2::Right(); //馬の向き
if (wide() < 0.0) m_direction = Vec2::Left(); //馬の向き

馬の向きはVec2型で管理します。こうすることで右を向いているなら1、左なら-1の取得が用意になります。

const  Optional<bool> isRight()const { // いつか上下移動を追加するときのためにOptional<bool>
        if (m_direction == Vec2::Right()) { return true; }; // 右向きならtrue
        if (m_direction == Vec2::Left()) { return false; };// 左向きならtrue
        return none; // このプログラムがnoneを返すことはない将来の拡張用
    }

右か左かそれ以外かを向いているかboolで知りたいときのためにOptional<bool>型を返す関数を用意しました。 今回はほぼTexture::mirrored()用です。 なおこのプログラムでは左右以外を向くことは基本ありません。

移動速度

m_speed = Vec2(m_direction).setLength(System::DeltaTime() * m_runSpeed*m_runTransition.easeInOut());
//System::DeltaTime()は直前フレームからの経過時間を返す。フレームレートが落ちるほどSystem::DeltaTime()は大きくなる
//コレによりフレームレートに関係なく時間あたりの移動量を一定にできる。

移動速度を取得する部分です。

setLength()はベクトルの大きさを設定する関数です。

setLength() System::DeltaTime()は先日のSiv3D実装回in広島で Ryo Suzuki (@Reputeless) | Twitterさんに教えていただいたことです。ありがとうございます。

トランジション

トランジションに関しては以下の記事を参考にしてください。 movement.hatenablog.jp トランジションにより動きはじめが滑らかになります。

描画

const void draw()const {
        m_texture.mirrored(isRight().value())
            .rotated(Radians(Random(-10, 10)*m_runTransition.easeInOut())) // 角度をランダムに変更
            .drawAt(m_pos + RandomVec2(-5.0, 5.0)*m_runTransition.easeInOut()); // 位置をランダムに変更
        //走っていない時は角度も位置も変わらない(Transitionで滑らかに切り替え)
    }

Random(-10, 10)は-10から10までのランダムな数字を返します。イージングしたトランジションの数字をかけ、Radians()に入れてラジアン角に変換して使います。

RandomVec2(-5.0, 5.0)も同じく-5.0から5.0までのランダムな数字を返します。イージングしたトランジションの数字をかけ、プレイヤーの座標と足し合わせます。

さいごに

今回はソースコードの方にたくさんコメントを書いてみましたがいかがだったでしょうか。 150行くらいでサクッと実装出来て楽しかったです。次回もよろしくおねがいします。