Skip to content

Instantly share code, notes, and snippets.

@KeyMaster-
Created April 4, 2015 22:16
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save KeyMaster-/4aa297d2842b03ece36b to your computer and use it in GitHub Desktop.
Save KeyMaster-/4aa297d2842b03ece36b to your computer and use it in GitHub Desktop.
Luxe rectangles physics engine (WIP, and very basic)
package ;
import luxe.Component;
import luxe.Rectangle;
import luxe.Vector;
class PhysBody {
public var rect:Rectangle;
public var vel:Vector;
public var acc:Vector;
public var velCap:Vector;
public var touching:DirectionStates;
public function new(?_rect:Rectangle) {
rect = (_rect == null) ? new Rectangle() : _rect;
vel = new Vector();
acc = new Vector();
velCap = new Vector();
touching = { left:false, right:false, top:false, bottom:false };
}
public function clearTouching():Void {
touching.left = false;
touching.right = false;
touching.top = false;
touching.bottom = false;
}
}
typedef DirectionStates = {
var left:Bool;
var right:Bool;
var top:Bool;
var bottom:Bool;
}
package ;
import luxe.Rectangle;
import luxe.Vector;
class RectExt {
public static function move(_this:Rectangle, v:Vector) {
_this.x += v.x;
_this.y += v.y;
}
public static function intersection( _this:Rectangle, _other:Rectangle ):Rectangle {
if (!_this.overlaps(_other)) return null;
var left = Math.max(_this.x, _other.x);
var top = Math.max(_this.y, _other.y);
var right = Math.min(_this.x + _this.w, _other.x + _other.w);
var bottom = Math.min(_this.y + _this.h, _other.y + _other.h);
return new Rectangle(left, top, right - left, bottom - top);
}
public static inline function right(_this:Rectangle):Float {
return _this.x + _this.w;
}
public static inline function bottom(_this:Rectangle):Float {
return _this.y + _this.h;
}
public static inline function leftOf(_this:Rectangle, _other:Rectangle):Bool {
return right(_this) <= _other.x;
}
public static inline function rightOf(_this:Rectangle, _other:Rectangle):Bool {
return _this.x >= right(_other);
}
public static inline function above(_this:Rectangle, _other:Rectangle):Bool {
return bottom(_this) <= _other.y;
}
public static inline function below(_this:Rectangle, _other:Rectangle):Bool {
return _this.y >= bottom(_other);
}
}
package ;
import luxe.Color;
import luxe.Physics.PhysicsEngine;
import luxe.Rectangle;
import luxe.utils.Maths;
import luxe.Vector;
using RectExt;
//This should really be called the Mosaaic engine
//Anagram for "MOve Stuff Around And It Collides" Engine
//Alternatively just "Mosaic", for "MOve Stuff And It Collides"
class RectPhysics extends PhysicsEngine {
public var dynamicBodies:Array<PhysBody>;
public var staticBodies:Array<Rectangle>;
public var staticColor:Color = new Color(0.5, 0.5, 1);
public var dynamicColor:Color = new Color(0.5, 1, 0.5);
public var collisionColor:Color = new Color(0.8, 0.0, 0.0);
public var touchingThreshold:Float = 0.5;
override public function init() {
dynamicBodies = new Array<PhysBody>();
staticBodies = new Array<Rectangle>();
}
override public function render() {
if (!draw) return;
for (rect in staticBodies) {
Luxe.draw.rectangle( {
immediate:true,
rect:rect,
color:staticColor
});
}
for (body in dynamicBodies) {
Luxe.draw.rectangle( {
immediate:true,
rect:body.rect,
color:dynamicColor
});
if (body.touching.left) {
Luxe.draw.line( {
p0:new Vector(body.rect.x, body.rect.y),
p1:new Vector(body.rect.x, body.rect.bottom()),
color:collisionColor,
immediate:true
});
}
if (body.touching.right) {
Luxe.draw.line( {
p0:new Vector(body.rect.right(), body.rect.y),
p1:new Vector(body.rect.right(), body.rect.bottom()),
color:collisionColor,
immediate:true
});
}
if (body.touching.top) {
Luxe.draw.line( {
p0:new Vector(body.rect.x, body.rect.y),
p1:new Vector(body.rect.right(), body.rect.y),
color:collisionColor,
immediate:true
});
}
if (body.touching.bottom) {
Luxe.draw.line( {
p0:new Vector(body.rect.x, body.rect.bottom()),
p1:new Vector(body.rect.right(), body.rect.bottom()),
color:collisionColor,
immediate:true
});
}
}
}
override public function update() {
if (paused) return;
var oldBodyRect = new Rectangle();
var vel = new Vector();
var acc = new Vector();
var intersection:Rectangle;
for (body in dynamicBodies) {
oldBodyRect.copy_from(body.rect);
acc.copy_from(body.acc);
acc.multiplyScalar(Luxe.physics.step_delta * Luxe.timescale);
body.vel.add(acc);
body.vel.x = Maths.sign(body.vel.x) * Math.min(Math.abs(body.vel.x), body.velCap.x);
body.vel.y = Maths.sign(body.vel.y) * Math.min(Math.abs(body.vel.y), body.velCap.y);
vel.copy_from(body.vel);
vel.multiplyScalar(Luxe.physics.step_delta * Luxe.timescale);
body.rect.move(vel);
body.clearTouching();
var intersection:Rectangle;
for (rect in staticBodies) {
intersection = oldBodyRect.intersection(rect);
if (intersection != null) {
if (intersection.h <= intersection.w) {
oldBodyRect.y += (oldBodyRect.y < rect.y) ? -intersection.h : intersection.h;
}
else {
oldBodyRect.x += (oldBodyRect.x < rect.x) ? -intersection.w : intersection.w;
}
}
if (body.rect.overlaps(rect)) {
if (oldBodyRect.leftOf(rect)) {
body.rect.x = rect.x - body.rect.w;
}
else if (oldBodyRect.rightOf(rect)) {
body.rect.x = rect.right();
}
else if (oldBodyRect.above(rect)) {
body.rect.y = rect.y - body.rect.h;
}
else if (oldBodyRect.below(rect)) {
body.rect.y = rect.bottom();
}
} // if dynamic overlaps static
//the body is regarded as touching when it is less than one pixel away from the edge of the object
//(i.e. difference between the edge of the body and the edge of the obstacle is less than one)
//Use range check because the body may be far beyond the obstacle, so the difference will be negative, and less than 1 would be true
if (body.rect.bottom() > rect.y && body.rect.y < rect.bottom()) {
if (Maths.within_range(rect.x - body.rect.right(), 0, touchingThreshold)) body.touching.right = true;
if (Maths.within_range(body.rect.x - rect.right(), 0, touchingThreshold)) body.touching.left = true;
}
if (body.rect.right() > rect.x && body.rect.x < rect.right()) {
if (Maths.within_range(body.rect.y - rect.bottom(), 0, touchingThreshold)) body.touching.top = true;
if (Maths.within_range(rect.y - body.rect.bottom(), 0, touchingThreshold)) body.touching.bottom = true;
}
} // for each static body
} // for each dynamic body
Luxe.events.fire('RectPhysics.update.complete');
} // update
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment