Last active
March 14, 2024 16:57
-
-
Save hamaluik/048d24c5d3ce154316f3 to your computer and use it in GitHub Desktop.
Minkowski Difference Collision Detection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ; | |
import openfl.display.Sprite; | |
/** | |
* ... | |
* @author Kenton Hamaluik | |
*/ | |
class AABB | |
{ | |
public var center:Vector = new Vector(); | |
public var extents:Vector = new Vector(); | |
public var min(get, never):Vector; | |
public function get_min() | |
{ | |
return new Vector(center.x - extents.x, center.y - extents.y); | |
} | |
public var max(get, never):Vector; | |
public function get_max() | |
{ | |
return new Vector(center.x + extents.x, center.y + extents.y); | |
} | |
public var size(get, never):Vector; | |
public function get_size() | |
{ | |
return new Vector(extents.x * 2, extents.y * 2); | |
} | |
public function new(center:Vector, extents:Vector) | |
{ | |
this.center = center; | |
this.extents = extents; | |
} | |
public function draw(container:Sprite, colour:Int = 0xffffff) | |
{ | |
container.graphics.beginFill(colour, 0.5); | |
container.graphics.drawRect(min.x, min.y, size.x, size.y); | |
container.graphics.endFill(); | |
} | |
public function minkowskiDifference(other:AABB):AABB | |
{ | |
var topLeft:Vector = min - other.max; | |
var fullSize:Vector = size + other.size; | |
return new AABB(topLeft + (fullSize / 2), fullSize / 2); | |
} | |
public function closestPointOnBoundsToPoint(point:Vector):Vector | |
{ | |
// test x first | |
var minDist:Float = Math.abs(point.x - min.x); | |
var boundsPoint:Vector = new Vector(min.x, point.y); | |
if (Math.abs(max.x - point.x) < minDist) | |
{ | |
minDist = Math.abs(max.x - point.x); | |
boundsPoint = new Vector(max.x, point.y); | |
} | |
if (Math.abs(max.y - point.y) < minDist) | |
{ | |
minDist = Math.abs(max.y - point.y); | |
boundsPoint = new Vector(point.x, max.y); | |
} | |
if (Math.abs(min.y - point.y) < minDist) | |
{ | |
minDist = Math.abs(min.y - point.y); | |
boundsPoint = new Vector(point.x, min.y); | |
} | |
return boundsPoint; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ; | |
import flash.display.Sprite; | |
import flash.events.Event; | |
import flash.Lib; | |
import openfl.display.FPS; | |
/** | |
* ... | |
* @author Kenton Hamaluik | |
*/ | |
class Main extends Sprite | |
{ | |
var inited:Bool; | |
var drawContainer:Sprite = new Sprite(); | |
var boxA:AABB; | |
var boxB:AABB; | |
/* ENTRY POINT */ | |
function resize(e) | |
{ | |
if (!inited) init(); | |
else | |
{ | |
// resize or orientation change | |
boxA = new AABB(new Vector(0, 0), new Vector(10, 10)); | |
boxB = new AABB(new Vector(stage.stageWidth / 2, stage.stageHeight / 2), new Vector(stage.stageWidth / 5, stage.stageHeight / 5)); | |
} | |
} | |
function init() | |
{ | |
if (inited) return; | |
inited = true; | |
this.addChild(drawContainer); | |
boxA = new AABB(new Vector(0, 0), new Vector(10, 10)); | |
boxB = new AABB(new Vector(stage.stageWidth / 2, stage.stageHeight / 2), new Vector(stage.stageWidth / 5, stage.stageHeight / 5)); | |
stage.addChild(new FPS(10, 10, 0xffffff)); | |
addEventListener(Event.ENTER_FRAME, onFrame); | |
} | |
function onFrame(event:Event):Void | |
{ | |
var mousePos:Vector = new Vector(stage.mouseX, stage.mouseY); | |
// move boxA around | |
boxA.center = mousePos; | |
var boxAColour:Int = 0xff0000; | |
// get the minkowski difference between the two | |
var md:AABB = boxB.minkowskiDifference(boxA); | |
var penetrationVector:Vector = null; | |
if (md.min.x <= 0 && | |
md.max.x >= 0 && | |
md.min.y <= 0 && | |
md.max.y >= 0) | |
{ | |
// collision is occurring! | |
boxAColour = 0x00ff00; | |
// find the penetration depth | |
penetrationVector = md.closestPointOnBoundsToPoint(Vector.zero); | |
boxA.center += penetrationVector; | |
} | |
// redraw | |
drawContainer.graphics.clear(); | |
boxB.draw(drawContainer); | |
boxA.draw(drawContainer, boxAColour); | |
md.draw(drawContainer, 0x0000ff); | |
if (penetrationVector != null) | |
penetrationVector.draw(drawContainer, new Vector(Lib.current.stage.stageWidth / 2, Lib.current.stage.stageHeight / 2), 0xff0000); | |
} | |
/* SETUP */ | |
public function new() | |
{ | |
super(); | |
addEventListener(Event.ADDED_TO_STAGE, added); | |
} | |
function added(e) | |
{ | |
removeEventListener(Event.ADDED_TO_STAGE, added); | |
stage.addEventListener(Event.RESIZE, resize); | |
#if ios | |
haxe.Timer.delay(init, 100); // iOS 6 | |
#else | |
init(); | |
#end | |
} | |
public static function main() | |
{ | |
// static entry point | |
Lib.current.stage.align = flash.display.StageAlign.TOP_LEFT; | |
Lib.current.stage.scaleMode = flash.display.StageScaleMode.NO_SCALE; | |
Lib.current.addChild(new Main()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ; | |
import openfl.display.Sprite; | |
/** | |
* ... | |
* @author Kenton Hamaluik | |
*/ | |
private class VectorRaw | |
{ | |
public var x:Float = 0; | |
public var y:Float = 0; | |
public function new(x:Float = 0, y:Float = 0) | |
{ | |
this.x = x; | |
this.y = y; | |
} | |
} | |
@:forward(x, y) | |
abstract Vector(VectorRaw) from VectorRaw to VectorRaw | |
{ | |
public function new(x:Float = 0, y:Float = 0) | |
{ | |
this = new VectorRaw(x, y); | |
} | |
public static var zero(get, never):Vector; | |
private static inline function get_zero():Vector | |
{ | |
return new Vector(0, 0); | |
} | |
public var length(get, never):Float; | |
private inline function get_length():Float | |
{ | |
return Math.sqrt((this.x * this.x) + (this.y * (this.y))); | |
} | |
@:op(A * B) | |
@:commutative | |
public static inline function multiplyScalar(a:Vector, s:Float) | |
{ | |
return new Vector(a.x * s, a.y * s); | |
} | |
@:op(A / B) | |
public static inline function divideByScalar(a:Vector, s:Float) | |
{ | |
return new Vector(a.x / s, a.y / s); | |
} | |
@:op(A + B) | |
public static inline function addVectors(a:Vector, b:Vector) | |
{ | |
return new Vector(a.x + b.x, a.y + b.y); | |
} | |
@:op(A - B) | |
public static inline function subtractVectors(a:Vector, b:Vector) | |
{ | |
return new Vector(a.x - b.x, a.y - b.y); | |
} | |
public function toString():String | |
{ | |
return "[" + this.x + ", " + this.y + "]"; | |
} | |
public function draw(container:Sprite, origin:Vector, colour:Int = 0xffffff) | |
{ | |
container.graphics.moveTo(origin.x, origin.y); | |
container.graphics.lineStyle(1, colour, 1); | |
container.graphics.lineTo(origin.x + this.x, origin.y + this.y); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment