summaryrefslogtreecommitdiff
path: root/jaws/src/collision_detection.js
diff options
context:
space:
mode:
Diffstat (limited to 'jaws/src/collision_detection.js')
-rwxr-xr-xjaws/src/collision_detection.js226
1 files changed, 226 insertions, 0 deletions
diff --git a/jaws/src/collision_detection.js b/jaws/src/collision_detection.js
new file mode 100755
index 0000000..2f4588c
--- /dev/null
+++ b/jaws/src/collision_detection.js
@@ -0,0 +1,226 @@
+if(typeof require !== "undefined") { var jaws = require("./core.js"); }
+
+/**
+ * @fileOverview Collision Detection
+ *
+ * Collision detection helpers.
+ *
+ * @example
+ * // collision helper exampels:
+ * collideOneWithOne(player, boss) // -> false
+ * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
+ * collideManyWithMany(bullets, enemies) // -> [ [bullet1, enemy1], [bullet2, enemy2] ]
+ * collide(player, boss) // -> false
+ * collide(player,
+ * bullets,
+ * function(player, bullet) {}) // Callback: arguments[0] -> player
+ * // arguments[1] -> bullets[i]
+ *
+ */
+var jaws = (function(jaws) {
+
+ /**
+ * Collides two objects by reading x, y and either method rect() or property radius.
+ * @public
+ * @param {object} object1 An object with a 'radius' or 'rect' property
+ * @param {object} object2 An object with a 'radius' or 'rect' property
+ * @returns {boolean} If the two objects are colliding or not
+ */
+ jaws.collideOneWithOne = function(object1, object2) {
+ if (object1.radius && object2.radius && object1 !== object2 && jaws.collideCircles(object1, object2))
+ return true;
+
+ if (object1.rect && object2.rect && object1 !== object2 && jaws.collideRects(object1.rect(), object2.rect()))
+ return true;
+
+ return false;
+ };
+
+ /**
+ * Compares an object against a list, returning those from list that collide with object, and
+ * calling 'callback' per collision (if set) with object and item from list.
+ * (Note: Will never collide objects with themselves.)
+ * @public
+ * @param {object} object An object with a 'radius' or 'rect' property
+ * @param {array|object} list A collection of objects with a 'length' property
+ * @param {function} callback The function to be called per collison detected
+ * @returns {array} A collection of items colliding with object from list
+ * @example
+ * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
+ * collideOneWithMany(player, bullets, function(player, bullet) {
+ * //player and bullet (bullets[i])
+ * });
+ */
+ jaws.collideOneWithMany = function(object, list, callback) {
+ var a = [];
+ if (callback) {
+ for (var i = 0; i < list.length; i++) {
+ if (jaws.collideOneWithOne(object, list[i])) {
+ callback(object, list[i]);
+ a.push(list[i])
+ }
+ }
+ return a;
+ }
+ else {
+ return list.filter(function(item) {
+ return jaws.collideOneWithOne(object, item);
+ });
+ }
+ };
+
+ /**
+ * Compares two lists, returning those items from each that collide with each other, and
+ * calling 'callback' per collision (if set) with item from list1 and item from list2.
+ * (Note: Will never collide objects with themselves.)
+ * @public
+ * @param {array|object} list1 A collection of objects with a 'forEach' property
+ * @param {array|object} list2 A collection of objects with a 'forEach' property
+ * @param {function} callback The function to be called per collison detected
+ * @returns {array} A collection of items colliding with list1 from list2
+ * @example
+ * jaws.collideManyWithMany(bullets, enemies) // --> [[bullet, enemy], [bullet, enemy]]
+ */
+ jaws.collideManyWithMany = function(list1, list2, callback) {
+ var a = [];
+
+ if (list1 === list2) {
+ combinations(list1, 2).forEach(function(pair) {
+ if (jaws.collideOneWithOne(pair[0], pair[1])) {
+ if (callback) {
+ callback(pair[0], pair[1]);
+ }
+ else {
+ a.push([pair[0], pair[1]]);
+ }
+ }
+ });
+ }
+ else {
+ list1.forEach(function(item1) {
+ list2.forEach(function(item2) {
+ if (jaws.collideOneWithOne(item1, item2)) {
+ if (callback) {
+ callback(item1, item2);
+ }
+ else {
+ a.push([item1, item2]);
+ }
+ }
+ });
+ });
+ }
+
+ return a;
+ };
+
+ /**
+ * Returns if two circle-objects collide with each other
+ * @public
+ * @param {object} object1 An object with a 'radius' property
+ * @param {object} object2 An object with a 'radius' property
+ * @returns {boolean} If two circle-objects collide or not
+ */
+ jaws.collideCircles = function(object1, object2) {
+ return (jaws.distanceBetween(object1, object2) < object1.radius + object2.radius);
+ };
+
+ /**
+ * Returns if two Rects collide with each other or not
+ * @public
+ * @param {object} rect1 An object with 'x', 'y', 'right' and 'bottom' properties
+ * @param {object} rect2 An object with 'x', 'y', 'right' and 'bottom' properties
+ * @returns {boolean} If two Rects collide with each other or not
+ */
+ jaws.collideRects = function(rect1, rect2) {
+ return ((rect1.x >= rect2.x && rect1.x <= rect2.right) || (rect2.x >= rect1.x && rect2.x <= rect1.right)) &&
+ ((rect1.y >= rect2.y && rect1.y <= rect2.bottom) || (rect2.y >= rect1.y && rect2.y <= rect1.bottom));
+ };
+
+ /**
+ * Returns the distance between two objects
+ * @public
+ * @param {object} object1 An object with 'x' and 'y' properties
+ * @param {object} object2 An object with 'x' and 'y' properties
+ * @returns {number} The distance between two objects
+ */
+ jaws.distanceBetween = function(object1, object2) {
+ return Math.sqrt(Math.pow(object1.x - object2.x, 2) + Math.pow(object1.y - object2.y, 2));
+ };
+
+ /**
+ * Creates combinations of items from a list of a specific size
+ * @private
+ * @param {array|object} list An object with a 'length' property
+ * @param {number} n The size of the array to return
+ * @returns {Array} An array of items having a specific size number of its own entries
+ */
+ function combinations(list, n) {
+ var f = function(i) {
+ if (list.isSpriteList !== undefined) {
+ return list.at(i);
+ } else { // s is an Array
+ return list[i];
+ }
+ };
+ var r = [];
+ var m = new Array(n);
+ for (var i = 0; i < n; i++)
+ m[i] = i;
+ for (var i = n - 1, sn = list.length; 0 <= i; sn = list.length) {
+ r.push(m.map(f));
+ while (0 <= i && m[i] === sn - 1) {
+ i--;
+ sn--;
+ }
+ if (0 <= i) {
+ m[i] += 1;
+ for (var j = i + 1; j < n; j++)
+ m[j] = m[j - 1] + 1;
+ i = n - 1;
+ }
+ }
+ return r;
+ }
+
+ /**
+ * If an object has items or not
+ * @private
+ * @param {array|object} array An object with a 'length' property
+ * @returns {boolean} If the object has items (length > 0)
+ */
+ function hasItems(array) {
+ return (array && array.length > 0);
+ }
+
+ /**
+ * Compares two objects or lists, returning if they collide, and
+ * calling 'callback' per collision (if set) between objects or lists.
+ * @param {array|object} x An object with either 'rect' or 'forEach' property
+ * @param {array|object} x2 An object with either 'rect' or 'forEach' property
+ * @param {function} callback
+ * @returns {boolean}
+ * @examples
+ * jaws.collide(player, enemy, function(player, enemy) { ... } )
+ * jaws.collide(player, enemies, function(player, enemy) { ... } )
+ * jaws.collide(bullets, enemies, function(bullet, enemy) { ... } )
+ */
+ jaws.collide = function(x, x2, callback) {
+ if ((x.rect || x.radius) && x2.forEach)
+ return (jaws.collideOneWithMany(x, x2, callback).length > 0);
+ if (x.forEach && x2.forEach)
+ return (jaws.collideManyWithMany(x, x2, callback).length > 0);
+ if (x.forEach && (x2.rect || x2.radius))
+ return (jaws.collideOneWithMany(x2, x, callback).length > 0);
+ if ((x.rect && x2.rect) || (x.radius && x2.radius)) {
+ var result = jaws.collideOneWithOne(x, x2);
+ if (callback && result)
+ callback(x, x2);
+ else
+ return result;
+ }
+ };
+
+ return jaws;
+})(jaws || {});
+