DXライブラリとC言語でゲーム制作。今回はアクションゲームならほぼ必須なジャンプの実装。
ジャンプの基本
アクションゲームなら2D・3D問わずほぼあるジャンプ。高校の物理で習う「落下」や「放物運動」を思い出す人がいるかもしれないがそれらで使われる公式を厳密に再現しなくてもジャンプの実装は可能。
- ジャンプボタンが押されたらY軸方向の速度(踏切の瞬間の速度)をセットしてジャンプ状態にする
- ジャンプ状態の場合は毎フレームY軸方向の速度を減少させる
- 2で求めたY軸方向の速度を移動ベクトルに加えてプレイヤーの座標を更新
- 床に着地(衝突)した場合Y軸方向の速度を0にしてジャンプ状態を解除
Y軸方向に初速を与えてあとは速度を一定値ずつ減少させるというシンプルな構造。√や2乗の計算はいらないので処理も早い。
余談だがファミコン時代のゲーム(スーパーマリオブラザーズなど)はハードのスペックの関係で重い計算処理はできないので上の方法を使っているとどこかのプログラミング記事で見かけた記憶がある。
ジャンプの実装
それではジャンプの実装。今回はまだ床との当たり判定を実装していないのでY座標が負になったら床に着地(衝突)したと判定し、ジャンプ状態を解除およびY座標を強制的に0にしている。
あとジャンプで上昇中および下降中(落下)でのそれぞれのアニメーションの再生も合わせて実装。
Game.cpp
キーコンフィグの記事での設定をそのまま使っている場合、ジャンプボタンとメニューに戻るボタンが同じになっているためこのままだとジャンプができない。なのでまずメニューに戻るボタンを変更する。
//更新 void Game_Update() { //中略 if (Input_GetKeyboardDown(KEY_INPUT_ESCAPE)) { //ESCAPEボタンが押されていたら SceneMgr_ChangeScene(eScene_Menu);//シーンをメニューに変更 } }
Player.cpp
ジャンプ処理に必要な変数およびアニメーション再生に必要な変数を追加および変更。
typedef struct { //中略 //以下の変数を追加 float JumpPower; //Y軸方向の速度 bool JumpFlag; //ジャンプしているかのフラグ }player_t; //以下の変数を追加・変更 const static float PLAYER_JUMP_POWER = 1.0f; //ジャンプ力 const static float PLAYER_GRAVITY = 0.07f; //プレイヤーの重力 const static int AL_LLAYER_ANIMATION_NUM = 4; //プレイヤーのアニメーション総数 const static int PLAYER_IDLE = 0; const static int PLAYER_WALK = 1; const static int PLAYER_JUMP = 2; const static int PLAYER_FALL = 3;
Player_Initialize()でジャンプ関連の変数を初期化
void Player_Initialize() { //中略 //ジャンプ力の初期化 player.JumpPower = 0.0f; //ジャンプフラグはOFF player.JumpFlag = false; //中略 }
Player_Update()にてジャンプ処理を追加
//更新 void Player_Update() { //中略 //プレイヤーがジャンプ中でないかつジャンプボタンが押されていればジャンプ処理 if (player.Action != PLAYER_JUMP && Input_GetGamepadDown(config.jump)) { //ジャンプフラグを立てる player.JumpFlag = true; //Y軸方向の速度をセット player.JumpPower = PLAYER_JUMP_POWER; } //ジャンプフラグが経っていればプレイヤーの行動をジャンプにする(落下状態は除く) if (player.JumpFlag) { if(player.Action != PLAYER_FALL) player.Action = PLAYER_JUMP; } //Y座標の計算 if (player.Action == PLAYER_JUMP || player.Action == PLAYER_FALL) { // Y軸方向の速度を重力分減算する player.JumpPower -= PLAYER_GRAVITY; // 移動ベクトルのY成分をY軸方向の速度にする MoveVec.y = player.JumpPower; //移動ベクトルのY成分が負なら落下状態にする if (MoveVec.y < 0) player.Action = PLAYER_FALL; } //中略 //Y座標が0より小さい場合(マップとの当たり判定未実装のため仮処理) if (NowPos.y < 0) { //Y座標を0にする NowPos.y = 0.0f; //プレイヤーの行動を待機にする player.Action = PLAYER_IDLE; //ジャンプフラグをオフにする player.JumpFlag = false; } //中略 }
アニメーション再生の関数も合わせて更新。
//プレイヤーのアニメーション処理 void Player_PlayAnimation() { //中略 //1F前のプレイヤーの行動と現在のプレイヤーの行動が違っていれば再生するアニメーションを変更 if (player.PrevAction != player.Action) { //今までアタッチしていたアニメーションのデタッチ MV1DetachAnim(player.ModelHandle, player.AttachIndex); if (player.Action == PLAYER_IDLE) { //中略 } else if (player.Action == PLAYER_WALK) { //中略 } else if (player.Action == PLAYER_JUMP) { //Jump という名前のアニメーションの番号を取得する player.AnimIndex = MV1GetAnimIndex(player.ModelHandle, "Armature|Jump"); //取得したアニメーション番号のアニメーションをアタッチする player.AttachIndex = MV1AttachAnim(player.ModelHandle, player.AnimIndex, -1, FALSE); //アタッチしたアニメーションの総再生時間を取得する player.TotalTime = MV1GetAttachAnimTotalTime(player.ModelHandle, player.AttachIndex); } else if (player.Action == PLAYER_FALL) { //Fall という名前のアニメーションの番号を取得する player.AnimIndex = MV1GetAnimIndex(player.ModelHandle, "Armature|Fall"); //取得したアニメーション番号のアニメーションをアタッチする player.AttachIndex = MV1AttachAnim(player.ModelHandle, player.AnimIndex, -1, FALSE); //アタッチしたアニメーションの総再生時間を取得する player.TotalTime = MV1GetAttachAnimTotalTime(player.ModelHandle, player.AttachIndex); } //中略 }
改良の余地
ジャンプの実装をしたわけだが、今回の方法だけだと以下のような問題がある。
- ジャンプの高さが常に一定(ボタンの押し具合でジャンプの高さが変わらない)
- 落下速度に限度がないため、高所から落下すると床との当たり判定が行われない可能性がある
- カメラも合わせて移動するので周りが見にくい
なので次回はこれらを改善しようと思う。
コメント