ジャンプアクションの基本を作る

これらをさらに発展させて、シンプルなサイドビューのジャンプアクション(通称プラットフォーマー)を組んでみたいと思います。
いよいよゲームっぽくなってきました。

プラットフォーマーのキャラクターに必要な処理

プラットフォーマーでキャラクターに必要な処理は、次のようなものが考えられます。

  1. 左右に歩く
  2. 壁に当たって止まる
  3. ジャンプする
  4. 重力に引っ張られてキャラクターが下に落ちる
  5. 地面に当たると止まる
  6. ジャンプ中に天井に当たると落下する

左右の移動と壁に当たって止まるについては、壁で止めるのコードがほぼそのまま使えそうです。
問題は、ジャンプや落下といった上下の移動。何もしなくても下に落ち、地面にあたったら止まらなければなりません。

残念なことに、PICO-8の世界には地面も重力もありません。
地面は地面として、重力は重力として、自分自身が神となって、これらの物理法則を作る必要があります。
ちょっとしたコードでこの世界の神となって、重力と物理法則を画面内に作り上げてみましょう。

サンプルCART

サンプルcart

サンプルのCARTです。
この画像(gravity_and_jumping.p8.png)をダウンロードして、PICO-8で開いてください。

スプライトシートに、歩くアニメーションとジャンプのポーズ、床(壁)のスプライトが設定されています。

コード解説

当たり判定(コード画面のタブ1番)

get_map_flag()h_collision()は、「壁で止める」とほぼ同じものです。

それに加えて、着地を判定するlanding()と、頭をぶつけたことを判定するheading()が追加されています。
内容は、h_collision()と同じ仕組みで、見る座標が上か下かの違いだけです。

いずれも、キャラクターの四隅から少し内側を判定箇所にしています。

初期設定

function _init()
	x=0-- X座標
	y=8-- y座標
	dx=0-- 横移動量
	dy=0-- 縦移動量
	counter=1-- アニメーション用カウンター
	spr_num={4}-- 初期スプライト(ジャンプポーズ)
	gravity=0.5-- 重力加速
	max_gravity=6-- 最大落下速度
	jumping=true-- ジャンプ中かどうかのフラグ
	max_height=6-- ジャンプの最大高
end

歩くアニメーション」にはなかったジャンプ関連の変数が追加されています。

gravityは重力の大きさです。毎フレームY方向の移動距離(dy)に加算されていき、何もしなければ、キャラクターは下に落ち続けます。

max_gravityは最大の落下速度です。無限に落下速度が大きくならないよう上限を設定します。

jumpingはジャンプ中かどうかのフラグです。キャラクターの状態を判定します。

max_heightはジャンプの最大の高さです。ジャンプするとまずこの高さまで上がります。

キャラクターの動き

function _update()
	-- キー入力処理
	dx=0
	if btn(0) then
		dx-=2
		f=true
	end
	if btn(1) then
		dx+=2
		f=false
	end
	-- ジャンプ中でなければジャンプさせる
	if btn(🅾️) and not jumping then
		dy-=max_height
		jumping=true
	end
	
	-- 縦移動距離
	dy=min(dy+gravity,max_gravity)
	
	-- 壁との衝突判定
	if h_collision(x+dx,y) then
		dx=0
	end
	
	-- 頭をぶつけたかどうか
	if jumping
	and heading(x+dx,y+dy) then
		dy=1
		y=flr((y+dy+1)/8)*8-- 天井にめり込まないよう座標を調整
	end
	
	-- 着地しているかどうか
	if landing(x+dx,y+dy) then
		y=flr((y+dy+7)/8)*8-8-- 地面にめり込まないよう座標を調整
		dy=0
		jumping=false
	end
	
	-- 落下中もジャンプ扱いにする
	if dy>1 then
		jumping=true
	end
	
	-- 移動
	x+=dx
	y+=dy
	x=mid(0,x,120)-- 画面の外に出ないようにする
	
	-- アニメーションの設定
	if not jumping then
		spr_num={1,2,3,2}-- 歩く絵
	else
		spr_num={4}-- ジャンプの絵
	end 
	if dx!=0 then
		counter+=0.5-- アニメーションカウンター
	end
end

ジャンプスタート

ジャンプボタン(キーボードのZ)を押すと、dymax_heightの負の値を入れて、上方向に移動するよう設定します。
このとき、ジャンプ中かどうかの判定を入れているのは、空中ジャンプをさせないためです。
(ためしに、and not jumpingを削除して実行してみてください。ボタンを押すだけ無限にジャンプしていきます)

天井

ジャンプ中にheading(x+dx,y+dy)で、移動先で頭をぶつけたかどうかを判定します。
頭をぶつけた場合、縦の加速度を下向き(dy=1)にします。

地面

キャラクターは常に落ち続けている(dy=min(dy+gravity,max_gravity))ので、毎フレーム、landing(x+dx,y+dy)で着地しているかどうかを判定します。
着地判定された場合、dyを0にして落下を止め、ジャンプフラグをfalseにします。

天井も地面も、当たり判定が発生した時に、y座標が8の倍数になるように丸める調整をします。
これをしないと、dyの値が大きい場合に、天井や地面にキャラクターがめり込んでしまいます。
今回のコードは簡易な判定であるため、max_heightやmax_gravityがあまりに大きな数字になると、地面や天井をすり抜けてしまう可能性があります。

その他の見所

min(A,B)は、二つの数字のいずれか小さい方の値を返します。
dy=min(dy+gravity,max_gravity)は、dy+gravityがmax_gravityよりも大きくなった場合にmax_gravityを返して、それ以上落下速度が速くならないようになっています。

mid(A,B,C)は、3つの数字のうち真ん中の値を返します。
x=mid(0,x,120)とすると、xは常に0〜120の間の数字となり、キャラクターが画面の左右から外に出ることがなくなります。

いずれも、if文をたくさん書かなくても数値に制限をかけるのに便利な書き方ですので、ぜひ使ってみてください。

サンプルcartの動き

いかがでしょうか?
ジャンプ力や重力の数字、マップの構成を変えて、どんな動きになるかいろいろ試してみてください!