Intersection detection (using Matter.js)

Intersection detection using the built-in physics engine. This approach requires the physics engine so it uses the full runtime... just if that is any concern for you.

The following file includes the function hypeDocument.queryIntersections.

Download:
Please download in next post

Example:
drag_intersection_matter.html

Another example was released in this thread:

6 Likes

This is a slight tweak and more favorable version of the code using elements (Node, NodeList) in hypeDocument.queryIntersections rather than a selector string. This allows to pass in arbitrary lists rather than only a selector. Selectors can still be used but now reside in your game logic.

/**
 * Query intersections between elements using Matter.js
 *
 * @param {Node} sourceElm The source element used in each check
 * @param {Nodelist} targetElms The target elements to query against
 * @return {Nodelist} Returns a list of targetElms that intersect with sourceElm
 */
hypeDocument.queryIntersections = function(sourceElm, targetElms) {
	var sourceBody = hypeDocument.getElementProperty(sourceElm, 'physics-body');
	if (sourceBody) {
		var targetBodies = [];
		targetElms.forEach(function(elm){
			var elmBody = hypeDocument.getElementProperty(elm, 'physics-body');
			if (elmBody) {
				targetBodies.push(elmBody);
			} else {
				console.log('target '+elm.id+' must have static Physics enabled');
			}
		});
		var collisions = Matter.Query.collides(sourceBody, targetBodies);
		if (collisions.length) {
			return collisions.map(function(collision){
				var isB = sourceElm.id == collision.bodyB.elementId;
				return document.getElementById((isB? collision.bodyA : collision.bodyB).elementId);
			});
		}
	} else {
		console.log('sourceElm must have static Physics enabled');
	}
	return [];
}

Corresponding file:
drag_intersection_matter_elements.hype.zip (28,6 KB)

About using physics and intersection detection at once

So, there is an elephant in the room. What about using this approach when one is already using the physics engine for something else and the static elements needed for intersection detection are interacting with the game in unwanted ways. There is a simple solution called a collisionFilter in Matter.js and it can be used to remove the elements from the physics simulation Hype is running while still having them active for intersection detection and other forms of queries (like point, ray etc.).

The following function removes all elements from the regular scope of the Hype physics interaction:

hypeDocument.disablePhysicsCollisions = function(targetElms) {
	targetElms.forEach(function(elm){
		var elmBody = hypeDocument.getElementProperty(elm, 'physics-body');
		if (elmBody) elmBody.collisionFilter = { 'group': -1}
	});
}

If you need a version of the code that allows to re-enable them you would need to store a temporary copy of the original collisionFilter:

hypeDocument.disablePhysicsCollisions = function(targetElms) {
	targetElms.forEach(function(elm){
		var elmBody = hypeDocument.getElementProperty(elm, 'physics-body');
		if (elmBody) {
			if (!elmBody._collisionFilter) elmBody._collisionFilter = Object.assign({}, elmBody.collisionFilter);
			elmBody.collisionFilter = { 'group': -1}
		}
	});
}

hypeDocument.enablePhysicsCollisions = function(targetElms) {
	targetElms.forEach(function(elm){
		var elmBody = hypeDocument.getElementProperty(elm, 'physics-body');
		if (elmBody && elmBody._collisionFilter) {
			elmBody.collisionFilter = elmBody._collisionFilter;
			delete elmBody._collisionFilter;
		}
	});
}

One interesting by product of intersection detection is that it works solely in the Matter-Engine. Hence, if you disable the visibility (display:none) it still works for intersection detection. If in your game logic the visibility status is to be considered just filter the elements returned from the intersection check.

This article was posted to Medium:

3 Likes

Helper functions

Here are further helper functions to expose the Matter.Query possibilities. I didn't bother to optimize them as a function library and some code chunks could be streamlined if put together that way but as standalone functions you can mix and match needed stuff.

Query using a point

Meaning any target element hit by the point will be returned.

/**
 * Query intersections of a points and elements using Matter.js
 *
 * @param {Nodelist} targetElms The target elements to query against
 * @param {Vector} point The point containing the properties x and y to test against
 * @return {Nodelist} Returns a list of targetElms that intersect with sourceElm
 */
hypeDocument.queryIntersectionsByPoint = function(targetElms, point) {
	var targetBodies = [];
	if (targetElms && !targetElms.length) targetElms = [targetElms];
	targetElms.forEach(function(elm){
		var elmBody = hypeDocument.getElementProperty(elm, 'physics-body');
		if (elmBody) {
			targetBodies.push(elmBody);
		} else {
			console.log('target '+elm.id+' must have static Physics enabled');
		}
	});
	var collisions = Matter.Query.point(targetBodies, point);
	if (collisions.length) return collisions.map(function(body){
		return document.getElementById(body.elementId);
	});
	return [];
}

Query using a ray

Meaning any target element touching the ray will be returned. You can also define the width of the ray.

/**
 * Query intersections of a ray and elements using Matter.js
 *
 * @param {Nodelist} targetElms The target elements to query against
 * @param {Vector} startPoint The startPoint contains the properties x and y of the ray start point
 * @param {Vector} endPoint The endPoint contains the properties x and y of the ray end point
 * @param {Number} rayWidth The width of the ray (optional)
 * @return {Nodelist} Returns a list of targetElms that intersect with sourceElm
 */
hypeDocument.queryIntersectionsByRay = function(targetElms, startPoint, endPoint, rayWidth) {
	var targetBodies = []; rayWidth = rayWidth || 1;
	if (targetElms && !targetElms.length) targetElms = [targetElms];
	targetElms.forEach(function(elm){
		var elmBody = hypeDocument.getElementProperty(elm, 'physics-body');
		if (elmBody) {
			targetBodies.push(elmBody);
		} else {
			console.log('target '+elm.id+' must have static Physics enabled');
		}
	});
	var collisions = Matter.Query.ray(targetBodies, startPoint, endPoint, rayWidth);
	if (collisions.length) return collisions.map(function(collision){
		return document.getElementById(collision.body.elementId);
	});
	return [];
}

Have fun…


Update: Added new cover art to the first post

2 Likes