From 0f067785de711c772c2c44c7f1e1cf8b44b2704f Mon Sep 17 00:00:00 2001 From: Joe Wreschnig Date: Sat, 20 Mar 2010 17:44:17 -0700 Subject: [PATCH] Basic static/moving circle collisions. (Fixes issue #5) --- bulletml-runner | 6 ++-- bulletml/__init__.py | 7 +++- bulletml/collision.py | 81 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 bulletml/collision.py diff --git a/bulletml-runner b/bulletml-runner index faaf9e0..25fa50b 100755 --- a/bulletml-runner +++ b/bulletml-runner @@ -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 diff --git a/bulletml/__init__.py b/bulletml/__init__.py index 8ab4ec2..2a825cd 100644 --- a/bulletml/__init__.py +++ b/bulletml/__init__.py @@ -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 index 0000000..f185745 --- /dev/null +++ b/bulletml/collision.py @@ -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 -- 2.20.1