ロボ団群馬のブログ
11.32019
Pyxelで衝突判定(あたり判定)を作る方法|矩形・円・タイル・実践パターン
Pyxelで衝突判定(あたり判定)を作る方法|矩形・円・タイル・実践パターン
Pythonでレトロゲームを作れるPyxel(ピクセル)。
今回は「衝突判定(あたり判定)」を、最小実装 → 応用の順でやさしく解説します。
プレイヤーと敵、アイテムの取得、壁との当たり、タイルマップとの衝突など、ゲーム作りの必須テクニックをまとめて学べます。
まずは覚えておきたい:3つの基本判定
1) 点が矩形に入っているか
def point_in_rect(px, py, x, y, w, h):
return (x <= px < x + w) and (y <= py < y + h)
2) 矩形と矩形(AABB)の衝突
一番よく使う基本。AABB(Axis-Aligned Bounding Box)=回転していない四角どうしの判定です。
def rect_rect(ax, ay, aw, ah, bx, by, bw, bh):
return (ax < bx + bw and ax + aw > bx and
ay < by + bh and ay + ah > by)
3) 円と円の衝突
def circle_circle(x1, y1, r1, x2, y2, r2):
dx = x1 - x2
dy = y1 - y2
return dx * dx + dy * dy <= (r1 + r2) * (r1 + r2)
最小の動くサンプル:プレイヤー四角 vs 壁四角
矢印キーで動くプレイヤー(8×8)と、固定の壁(32×8)で、AABB判定を使います。
コツ:「x方向 → 判定・修正」「y方向 → 判定・修正」の順に分けて解決(分離軸の基本)。
import pyxel
W, H = 160, 120
def rect_rect(ax, ay, aw, ah, bx, by, bw, bh):
return (ax < bx + bw and ax + aw > bx and
ay < by + bh and ay + ah > by)
class App:
def __init__(self):
pyxel.init(W, H, title="AABB Collision")
self.x, self.y = 20, 20
self.w, self.h = 8, 8
# 壁
self.wx, self.wy, self.ww, self.wh = 60, 60, 32, 8
pyxel.run(self.update, self.draw)
def update(self):
vx = (pyxel.btn(pyxel.KEY_RIGHT) - pyxel.btn(pyxel.KEY_LEFT)) * 2
vy = (pyxel.btn(pyxel.KEY_DOWN) - pyxel.btn(pyxel.KEY_UP)) * 2
# --- X方向を先に移動・判定 ---
nx = self.x + vx
if rect_rect(nx, self.y, self.w, self.h, self.wx, self.wy, self.ww, self.wh):
# 右に進む場合:壁の左側で止める/左に進む場合:壁の右側で止める
if vx > 0:
nx = self.wx - self.w
elif vx < 0:
nx = self.wx + self.ww
self.x = max(0, min(W - self.w, nx))
# --- Y方向を次に移動・判定 ---
ny = self.y + vy
if rect_rect(self.x, ny, self.w, self.h, self.wx, self.wy, self.ww, self.wh):
if vy > 0:
ny = self.wy - self.h
elif vy < 0:
ny = self.wy + self.wh
self.y = max(0, min(H - self.h, ny))
def draw(self):
pyxel.cls(0)
pyxel.rect(self.x, self.y, self.w, self.h, 11) # プレイヤー
pyxel.rect(self.wx, self.wy, self.ww, self.wh, 8) # 壁
App()
これで、壁にめり込まずにカチッと止まる感覚が作れます。
当たりで引っかかる場合は、移動→判定→補正の順序を再確認してください。
アイテム取得:プレイヤー矩形 vs アイテム矩形
当たったら「取得済み」にして非表示にする典型パターン。
# プレイヤー:x,y,w,h / アイテム:ix,iy,iw,ih / gotフラグ
if not got and rect_rect(x, y, w, h, ix, iy, iw, ih):
got = True # スコア加算や効果音再生もここで
タイルマップと衝突(8×8タイル)
PyxelのTILEMAPは8×8のタイルで構成されます。
基本は「プレイヤーの足元や先端の座標 → タイル座標に変換 → そのタイルが壁かどうかを見る」という流れです。
1) タイル座標へ変換
TILE = 8
def to_tile(v):
return int(v // TILE)
2) タイルが「壁」か調べる(簡易:タイルマップのタイル種で判定)
Pyxel Editorで、壁として使うタイル(画像上の位置)を決めておき、集合で管理します。
# 例:壁に使うタイルの「画像上のU,V(8の倍数)」を登録
SOLID_TILES = {(0, 0), (8, 0), (16, 0)} # 必要に応じて追加
def is_solid_tile(tx, ty):
# マップ範囲外は壁扱い(落下防止など)
if tx < 0 or ty < 0:
return True
try:
u, v = pyxel.tilemap(0).pget(tx, ty) # タイルの画像座標を取得
except Exception:
return True
return (u, v) in SOLID_TILES
3) 進行方向の先端だけチェックして止める
移動予定位置に対して、接触しそうな角のタイルだけを調べるのが軽量です。
def move_and_collide(x, y, w, h, vx, vy):
# 先にX
nx = x + vx
if vx > 0:
# 右上・右下の角
if is_solid_tile(to_tile(nx + w - 1), to_tile(y)) or \
is_solid_tile(to_tile(nx + w - 1), to_tile(y + h - 1)):
nx = (to_tile(nx + w - 1)) * TILE - w # 壁の手前で止める
elif vx < 0:
# 左上・左下
if is_solid_tile(to_tile(nx), to_tile(y)) or \
is_solid_tile(to_tile(nx), to_tile(y + h - 1)):
nx = (to_tile(nx) + 1) * TILE
# 次にY
ny = y + vy
if vy > 0:
# 右下・左下
if is_solid_tile(to_tile(nx), to_tile(ny + h - 1)) or \
is_solid_tile(to_tile(nx + w - 1), to_tile(ny + h - 1)):
ny = (to_tile(ny + h - 1)) * TILE - h
elif vy < 0:
# 右上・左上
if is_solid_tile(to_tile(nx), to_tile(ny)) or \
is_solid_tile(to_tile(nx + w - 1), to_tile(ny)):
ny = (to_tile(ny) + 1) * TILE
return nx, ny
ポイント:
「X→判定→補正」「Y→判定→補正」の順を守ると、角でガタつきにくくなります。
円形の当たり(近接判定・攻撃範囲など)
当たりが丸い(近接距離で判定したい)ときは円判定が簡単です。
if circle_circle(player_x, player_y, 6, enemy_x, enemy_y, 6):
# 当たり!
hp -= 1
画像スプライトの当たりサイズを調整する(当たり枠)
見た目が16×16でも、当たりだけ12×12などにして「当たり負け」を減らすのが実用的です。
# 見た目は16x16、当たりは12x12(上下左右に2pxの余白)
HIT_W, HIT_H = 12, 12
hit_x = x + 2
hit_y = y + 2
# 以後の判定は hit_x, hit_y, HIT_W, HIT_H で行う
よくあるハマりポイント(FAQ)
- 角でガタつく/引っかかる:「X→補正→Y→補正」の順に分ける。1フレームの移動量が大きすぎる場合は小さくする。
- タイルとすり抜ける:チェックする角の数が足りない、またはタイル座標の丸め(floor)が合っていない可能性。
// 8(8で割る)で確実に。 - タイル種類の見分け:
pyxel.tilemap(0).pget(tx, ty)が返す(画像内の座標)が「壁扱いの集合」に含まれているかで判定するのが簡単。 - 処理が重い:接触可能性のある「近傍だけ判定」する(四分木やグリッド分割が理想、まずは“距離が近い相手だけ”の簡易フィルタでも効果大)。
まとめ:まずはAABB→タイル→応用の順で固めよう
- 最初に覚える:矩形×矩形(AABB)
- 背景と当たる:8×8タイルで角だけ判定(X→Yの順で補正)
- 演出を上げる:円判定や当たり枠の微調整
衝突判定が安定すると、ゲームの気持ち良さが一気に上がります。
次は「ダッシュ/坂/斜面」や「当たり判定ごとの状態遷移(無敵時間・ノックバック)」に挑戦してみましょう!
コメント
この記事へのトラックバックはありません。










この記事へのコメントはありません。