Skip to content

Instantly share code, notes, and snippets.

@KeyMaster-
Created April 4, 2015 22:16
Show Gist options
  • Save KeyMaster-/4aa297d2842b03ece36b to your computer and use it in GitHub Desktop.
Save KeyMaster-/4aa297d2842b03ece36b to your computer and use it in GitHub Desktop.

Revisions

  1. KeyMaster- created this gist Apr 4, 2015.
    38 changes: 38 additions & 0 deletions PhysBody.hx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    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;
    }
    43 changes: 43 additions & 0 deletions RectExt.hx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    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);
    }
    }
    149 changes: 149 additions & 0 deletions RectPhysics.hx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,149 @@
    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
    }