さて、ホッケーの動きとバーの動きが出来たことだし、次はこの2つの衝突判定を考えていこうか
この衝突判定って、「ウィンドウから出てしまわないようにする」時の考え方と同じになるのかな?
そうだね!だけど、前回までと大きく違う点は「判定対象が両方とも大きさを持っている」ってことだね
今回は両方とも動くものだし、イメージするのがなかなか難しそう...
そうでしょ
だから、まずはホッケーとバーの衝突範囲の関係を図にしてみよう
図を見た感じ、両方動くものの場合は片方を固定して考えるのが良さそうだね
そういうことだね!
今回の場合、幅と高さが同じ「ホッケー」を固定して考えると分かりやすいかな
ホッケーの上下左右について、それぞれどの範囲でバーが触れているか条件式を作って、その全てが当てはまる場合に「衝突した!」って考えていけば良いよ
ほうほう
例えば、ホッケーの左側のみに注目して条件を考えるとどうなると思う??
バーのx座標は「self.bar_x」、バーの幅は「self.bar_width」として考えてみて!
「ホッケーの中心のx座標 - ホッケーの半径」がホッケーの左端で、「バーの左上のx座標 + バーの幅」がバーの右端になって、バーがホッケーのx座標より小さくなると接触していることにならないから...
「self.x - self.r <= self.bar_x + self.bar_width」がホッケーの左側についての条件だね!
正解!あとは、同じような考え方で残りの条件式を考えて、すべての条件を「and」で繋げてあげればバッチリだよ!
うへー大変だ汗
今回は練習だから、事前に用意しておくよ!
もし、「自分で条件式を考えてみたい!」って場合は下のプログラムを見る前に自分でチャレンジしてみてね
# 関数「update」の最後に追加
# 4つの条件式で当たり判定
if (
self.x + self.r >= self.bar_x and # ホッケーの右側
self.x - self.r <= self.bar_x + self.bar_width and # ホッケーの左側
self.y + self.r >= self.bar_y and # ホッケーの下側
self.y - self.r <= self.bar_y + self.bar_height # ホッケーの上側
):
self.vx = abs(self.vx) # 絶対値「abs()」を使うことで、必ず数値をプラスに出来る
バーのx座標「self.bar_x」と、バーの幅「self.bar_width」を追加しておかないとだね!
# 関数「__init__」内を変更
self.bar_height = 20 # バーの高さ
self.bar_width = 5 # バーの幅
self.bar_x = 10 # バーのx座標
self.bar_y = (pyxel.height - self.bar_height) // 2 # バーのy座標
# 関数「draw」内を変更
pyxel.rect(self.bar_x, self.bar_y, self.bar_width, self.bar_height, 11)
よし!ちゃんと変更完了!!
バーにホッケーを当てたらちゃんと跳ね返ったよ!!
良い感じだね!Pyxelでは衝突判定の考え方がちょっと大変だから、慣れるまでは何度もトライアンドエラーで頑張ろう!
この時点での全体のプログラム
import pyxel
class App:
def __init__(self):
pyxel.init(160, 120, title="ホッケーゲーム")
# ホッケーの座標、速度、半径
self.x = pyxel.width // 2
self.y = pyxel.height // 2
self.vx = 1
self.vy = 1
self.r = 4
# プレイヤー(左側)のバー
self.bar_height = 20 # バーの高さ
self.bar_width = 5 # バーの幅
self.bar_x = 10 # プレイヤーのx座標
self.bar_y = (pyxel.height - self.bar_height) // 2 # プレイヤーのy座標
pyxel.run(self.update, self.draw)
def update(self):
self.x += self.vx
self.y += self.vy
# プレイヤーバーの操作(上下キー)
if pyxel.btn(pyxel.KEY_UP):
self.bar_y -= 2
if pyxel.btn(pyxel.KEY_DOWN):
self.bar_y += 2
# 画面外に出ないように制限
self.bar_y = max(0, min(pyxel.height - self.bar_height, self.bar_y))
# ウィンドウの端に当たったら反射(跳ね返る)
if self.x <= self.r or self.x >= pyxel.width - self.r:
self.vx *= -1
if self.y <= self.r or self.y >= pyxel.height - self.r:
self.vy *= -1
# 4つの条件式で当たり判定
if (
self.x + self.r >= self.bar_x and # ホッケーの右側
self.x - self.r <= self.bar_x + self.bar_width and # ホッケーの左側
self.y + self.r >= self.bar_y and # ホッケーの下側
self.y - self.r <= self.bar_y + self.bar_height # ホッケーの上側
):
self.vx = abs(self.vx) # 絶対値「abs()」を使うことで、必ず数値をプラスに出来る
def draw(self):
pyxel.cls(0)
pyxel.circ(self.x, self.y, self.r, 8)
pyxel.rect(self.bar_x, self.bar_y, self.bar_width, self.bar_height, 11)
App()
