Basic static/moving circle collisions. (Fixes issue #5)
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Sun, 21 Mar 2010 00:44:17 +0000 (17:44 -0700)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Sun, 21 Mar 2010 00:44:17 +0000 (17:44 -0700)
bulletml-runner
bulletml/__init__.py
bulletml/collision.py [new file with mode: 0644]

index faaf9e0..25fa50b 100755 (executable)
@@ -8,6 +8,7 @@ import pygame
 
 import bulletml
 import bulletml.bulletyaml
+from bulletml.collision import collides
 
 try:
     import yaml
@@ -82,10 +83,10 @@ def main(argv):
             target.y /= 2
 
             if not paused or go:
-
+                lactive = list(active)
                 start = time.time()
                 count = len(active)
-                for obj in list(active):
+                for obj in lactive:
                     new = obj.step()
                     total += len(new)
                     active.update(new)
@@ -93,6 +94,7 @@ def main(argv):
                         or not (-50 < obj.x < 350)
                         or not (-50 < obj.y < 350)):
                         active.remove(obj)
+                    collides(obj, lactive[0])
                 elapsed = time.time() - start
 
                 frames += 1
index 8ab4ec2..2a825cd 100644 (file)
@@ -9,6 +9,9 @@ renderer-agnostic.
 In addition to the standard BulletML XML format, this module supports
 an equivalent YAML format. See bulletml.bulletyaml for more details.
 
+Finally, two simple collision routines are provided, bulletml.overlaps
+for stationary circles and bulletml.collides for moving circles.
+
 More information is available at the BulletML homepage,
 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html, or the
 python-bullet homepage, http://code.google.com/p/python-bulletml/.
@@ -33,9 +36,11 @@ attributes that can be used to influence it.
 
 from bulletml.parser import BulletML
 from bulletml.impl import Bullet
+from bulletml.collision import overlaps, collides
 
 VERSION = (0, 1)
 VERSION_STRING = ".".join(map(str, VERSION))
 
-__all__ = ["VERSION", "VERSION_STRING", "Bullet", "BulletML"]
+__all__ = ["VERSION", "VERSION_STRING", "Bullet", "BulletML",
+           "overlaps", "collides"]
 
diff --git a/bulletml/collision.py b/bulletml/collision.py
new file mode 100644 (file)
index 0000000..f185745
--- /dev/null
@@ -0,0 +1,81 @@
+"""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