【PICO-8入門(6)】当たり判定とパーティクルでやられた感を出してみよう

この記事はPICO-8を使ってゲームを作りに挑戦する初心者向けの記事です。連載になっているので初回から見たい方はこちらからどうぞ。

▷ レトロ風ゲームエンジンPICO-8でゲーム作ってみよう

 

前回作ったプログラムではキャラクターが障害物に当たっても何も起こらなかったです。何も起こらないのは当たっているかどうかチェックする処理を書いていないからです。

障害物との当たり判定をつけよう

実は当たり判定の基本の話は別の記事にまとめてあるのでまずそっちを一度読んでください。今回はこの記事内にある「円と円の当たり判定」を使います。

で、当たり判定をつける前に当たったときに当たったことが分かるように鳴らす効果音を先に用意しましょう。

エディタ画面で右上にあるアイコンの中の◁みたいなのを選びます。するとサウンドエディタが開くので下の画像のような感じで適当にマウスでなぞってみてください。

効果音

スペースキーを押すと再生できます。

サウンドの作り方についてはまた別の記事を書く予定なのでとりあえず今はこれだけでOKです。保存してプログラムに戻ってください。

で、当たり判定を付けたプログラムはこんな感じになります。

enemy_class = function(r, i)
  local obj = {}
  obj.init = function()
    obj.x = r + rnd(128 - r*2)
    obj.y = -(r * i * 2 + r)
    obj.r = r
  end
  obj.update = function()
    obj.y += 1
    if obj.y > 127 + obj.r then
      obj.y = -obj.r
      obj.x = obj.r + rnd(128 - obj.r*2)
    end
    if obj.y > -obj.r then
      if hitcheck(obj.x, obj.y, obj.r, player.x+4, player.y+4, player.r) then
        sfx(0)
      end
    end
  end
  obj.draw = function()
    circ(obj.x, obj.y, obj.r, 7)
  end
  return obj
end
function hitcheck(x1, y1, r1, x2, y2, r2)
  if (x1 - x2) ^ 2 + (y1 - y2) ^ 2 < (r1 + r2) ^ 2 then
    return true
  end
  return false
end

あとplayerテーブルのinit()に「player.r = 3」を追加してください。

これで実行すると障害物に当たったときにさっき作った効果音が鳴ります。

さっくり説明

まずfunction hitcheck()が当たり判定用の関数です。この当たり判定の説明は上で紹介している当たり判定記事を読んでください。

でこのhitcheck()をenemy_class内で使っています。hitcheck()は別にどこで実行してもいいんですが今回の判定は障害物とプレイヤーキャラだけなのでenemy_classに書くのが一番書くことが少なくて済むのでここにしました(他の場所だとfor文が必要になってくる)。

hitcheck()の前にあるif文は画面内に障害物が見える範囲だけに絞るために付けています。理由としては前の記事でも書いたようにPICO-8で使える数字の最大値が32767.99のためです。今回の判定では2乗(^2と書いているのが2乗という意味です)を使うので画面外にある場合に32767を超える可能性があります(ゲーム開始時に超えてるものがあります)。なのでこの条件を付けています。

sfx(0)は効果音を鳴らす命令です。引数は作ったサウンドの番号です。めちゃくちゃ簡単ですね。

そういえばプレイヤーキャラに画面外に出ないようにする処理を書いていなかったので追加しておいてください(‘ω’)ノ

あと当たり判定で使うplayer.rが画像サイズの半分の4ではなく3にしているのは4で作ると当たる範囲が広すぎるためです。試しに大きさを変えて遊んでみてください(circ()で表示してみるといいよ)。

あ、あとスプライトの座標は画像の左上の角を指しています。なので円の当たり判定で中心を取りたい場合は座標に画像の半分の幅・高さを足してください。

キャラクターを非表示にする

障害物に当たったら演出を加えたいところですが、まずキャラクターを非表示にしましょう。もいらないんでね。

非表示にするのは単純にdraw()しなければ表示されることが無くなるんですが、そのためにフラグというものを作ります。フラグというのはtrue/falseで状態を管理するもので、今回はプレイヤーキャラクターの生死を管理するのに使います。

まず、player.init()内に「player.isdead = fasle」というのを作ります。ゲーム開始時はまだ死んでないのでfalseです。

で、当たり判定の所に「player.isdead = true」と追加します。これで死んだことが分かります。

次にplayer.draw()内のspr()の前に「if player.isdead then return end」を追加します。これでキャラクターが表示されなくなります。

やられた感を出す演出を作る

最近のゲームでは「ブシャーッ!」とか「プチュッ!」みたいな感じを出すために木っ端みじんに飛ばしたり血しぶきを飛び散らしてやられちゃった感を演出してるものが多いです。

その飛び散らしてるものをパーティクルと呼ぶらしいです。

というわけでプレイヤーが障害物に当たったときにパーティクルを飛び散らして「ミスった感」を盛り上げましょう。

出来上がりイメージはこんな感じです↓

パーティクル

パーティクルクラスを作る

particle_class = function(x, y)
  local obj = {}
  obj.x = x
  obj.y = y
  obj.r = rnd(4)+1
  obj.angle = rnd()
  obj.speed = rnd(3)+2
  obj.update = function()
    obj.speed *= 0.9
    obj.x += obj.speed * cos(obj.angle)
    obj.y += obj.speed * sin(obj.angle)
    obj.r *= 0.9
  end
  obj.draw = function()
    if obj.r < 0.3 then return end
    circfill(obj.x, obj.y, obj.r, 8)
  end
  return obj
end
particles = {}

パーティクルクラスはこんな感じにしてみました。今回はinit()はつくってません。

angleは飛んでいく角度、speedは飛んでいく速さです。どちらもランダムにしています。rnd()は引数無しだと0~1未満の範囲(小数点以下の数字)になるようなのです。

update()ではパーティクルの移動を行っています。またspeedとrは毎回ちょっとずつ小さくなるようにしています。

draw()のif文ではrがo.3より小さくなったら表示しないで終わりにします。これはcirc()が半径0でも1ドット表示するのでこうしました。

最後にパーティクル用のテーブル「particles = {}」を作っていますがここではまだパーティクルは作りません。

ミスったらパーティクルを作る

プレイヤーキャラが障害物に当たったら実際にパーティクルを作ります。

if hitcheck(obj.x, obj.y, obj.r, player.x+4, player.y+4, player.r) then
  sfx(0)
  player.isdead = true
  for i = 1, 20, 1 do
    particles[i] = particle_class(player.x+4, player.y+4)
  end
end

enemy_class内の当たり判定のif文内でパーティクルを作成するようにします。

ここでは20個のパーティクルをプレイヤーキャラクターの位置に作っています。

パーティクルの更新処理

作ったパーティクルは_update()と_draw()で更新して描画します。

function _update()
  fcount += 1
  if fcount > 32767 then 
    fcount = 0 
  end
  player.update()
  for enm in all(enemies) do
    enm.update()
  end
  if #particles then
    for prt in all(particles) do
      prt.update()
    end
  end
end
function _draw()
  cls()
  player.draw()
  for enm in all(enemies) do
    enm.draw()
  end
  if #particles then
    for prt in all(particles) do
      prt.draw()
    end
  end
end

ここで大事なのはゲーム開始時はparticlesテーブルは空っぽなことです。空っぽの状態(まだクラスが無い)ではクラス内のupdate()やdraw()は実行できないのでif文で中身があるか判定しています。

テーブルに「#」をつけるとテーブルの要素の数が分かるのでそれで判定します。判定の時「0」はfalseとして処理されます。そして「0以外」はtrueになります。なので比較式が無しでOKです。

今回for文がまたちょっと違う書き方になっています。↑の例で言うとparticlesの要素を一つ取り出してprtという名前でローカル変数を作っています。それをpartilclesの要素の数だけ順番にやってくれます。要素から取り出す場合はこの方が書く量が少なくて便利です。

完成までもう少しって感じですね

当たり判定と演出がついてもうかなりゲームな感じになってきましたね。

完成までもうあと一息って感じですね(*‘∀‘)

今回もあちこちいじって試して遊んでください。

ではまた次回(‘ω’)ノ

次回 ▷ ゲームの結果と画面遷移

 

BOOTHでサンプルプログラム無料ダウンロードできます

BOOTHでこのPICO-8入門講座で紹介したサンプルプログラムをダウンロードできます。

今回の記事の最後のプログラムが「sample6.p8」という名前で入っています。

同カテゴリー記事

記事の感想・コメント

※コメントはまだありません※

コメントを残す

メールアドレスが公開されることはありません。

最近作ったゲーム