Basic static/moving circle collisions. (Fixes issue #5)
[python-bulletml.git] / bulletml / collision.py
1 """Simple collision check.
2
3 This module provides simple collision checking appropriate for
4 shmups. It provides a routine to check whether two moving circles
5 collided during the past frame.
6
7 Basic Usage:
8
9 from bulletml.collision import collides
10
11 for bullet in bullets:
12 if collides(player, bullet): ... # Kill the player.
13 """
14
15 from __future__ import division
16
17 def overlaps(a, b):
18 """Return true if two circles are overlapping.
19
20 Usually, you'll want to use the 'collides' method instead, but
21 this one can be useful for just checking to see if the player has
22 entered an area or hit a stationary oject.
23 """
24
25 dx = a.x - b.x
26 dy = a.y - b.y
27 radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
28
29 return dx * dx + dy * dy <= radius * radius
30
31 def collides(a, b):
32 """Return true if the two moving circles collide.
33
34 a and b should have the following attributes:
35
36 x, y - required, current position
37 px, py - not required, defaults to x, y, previous frame position
38 radius - not required, defaults to 0.5
39
40 """
41 # Current locations.
42 xa = a.x
43 xb = b.x
44 ya = a.y
45 yb = b.y
46
47 # Treat b as a point, we only need one radius.
48 radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
49
50 # Previous frame locations.
51 pxa = getattr(a, 'px', xa)
52 pya = getattr(a, 'py', ya)
53 pxb = getattr(b, 'px', xb)
54 pyb = getattr(b, 'py', yb)
55
56 # Translate b's final position to be relative to a's start.
57 # And now, circle/line collision.
58 dir_x = pxa + (xb - xa) - pxb
59 dir_y = pya + (yb - ya) - pyb
60
61 if abs(dir_x) < 0.0001 and abs(dir_y) < 0.0001:
62 # b did not move relative to a, so do point/circle.
63 dx = pxb - pxa
64 dy = pyb - pya
65 return dx * dx + dy * dy < radius * radius
66
67 diff_x = pxa - pxb
68 diff_y = pya - pyb
69
70 # dot(diff, dir) / dot(dir, dir)
71 t = (diff_x * dir_x + diff_y * dir_y) / (dir_x * dir_x + dir_y * dir_y)
72 if t < 0:
73 t = 0
74 elif t > 1:
75 t = 1
76
77 dist_x = pxa - (pxb + dir_x * t)
78 dist_y = pya - (pyb + dir_y * t)
79
80 # dist_sq < radius_sq
81 return dist_x * dist_x + dist_y * dist_y <= radius * radius