"""Simple collision check. This module provides simple collision checking appropriate for shmups. It provides a routine to check whether two moving circles collided during the past frame. Basic Usage: from bulletml.collision import collides for bullet in bullets: if collides(player, bullet): ... # Kill the player. """ from __future__ import division def overlaps(a, b): """Return true if two circles are overlapping. Usually, you'll want to use the 'collides' method instead, but this one can be useful for just checking to see if the player has entered an area or hit a stationary oject. """ dx = a.x - b.x dy = a.y - b.y radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5) return dx * dx + dy * dy <= radius * radius def collides(a, b): """Return true if the two moving circles collide. a and b should have the following attributes: x, y - required, current position px, py - not required, defaults to x, y, previous frame position radius - not required, defaults to 0.5 """ # Current locations. xa = a.x xb = b.x ya = a.y yb = b.y # Treat b as a point, we only need one radius. radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5) # Previous frame locations. pxa = getattr(a, 'px', xa) pya = getattr(a, 'py', ya) pxb = getattr(b, 'px', xb) pyb = getattr(b, 'py', yb) # Translate b's final position to be relative to a's start. # And now, circle/line collision. dir_x = pxa + (xb - xa) - pxb dir_y = pya + (yb - ya) - pyb if abs(dir_x) < 0.0001 and abs(dir_y) < 0.0001: # b did not move relative to a, so do point/circle. dx = pxb - pxa dy = pyb - pya return dx * dx + dy * dy < radius * radius diff_x = pxa - pxb diff_y = pya - pyb # dot(diff, dir) / dot(dir, dir) t = (diff_x * dir_x + diff_y * dir_y) / (dir_x * dir_x + dir_y * dir_y) if t < 0: t = 0 elif t > 1: t = 1 dist_x = pxa - (pxb + dir_x * t) dist_y = pya - (pyb + dir_y * t) # dist_sq < radius_sq return dist_x * dist_x + dist_y * dist_y <= radius * radius