reworked physics system
This commit is contained in:
		
							parent
							
								
									c77a947cc0
								
							
						
					
					
						commit
						19028a9a58
					
				| 
						 | 
					@ -35,7 +35,7 @@ export default class BoidDemo extends Scene {
 | 
				
			||||||
        for(let i = 0; i < 150; i++){
 | 
					        for(let i = 0; i < 150; i++){
 | 
				
			||||||
            let boid = this.add.graphic(Boid, layer, new Vec2(this.worldSize.x*Math.random(), this.worldSize.y*Math.random()));
 | 
					            let boid = this.add.graphic(Boid, layer, new Vec2(this.worldSize.x*Math.random(), this.worldSize.y*Math.random()));
 | 
				
			||||||
            boid.fb = new FlockBehavior(this, boid, this.boids, 75, 50);
 | 
					            boid.fb = new FlockBehavior(this, boid, this.boids, 75, 50);
 | 
				
			||||||
            boid.setSize(5, 5);
 | 
					            boid.size.set(5, 5);
 | 
				
			||||||
            this.boids.push(boid);
 | 
					            this.boids.push(boid);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,44 +1,121 @@
 | 
				
			||||||
import AABB from "../AABB";
 | 
					import GameEvent from "../../Events/GameEvent";
 | 
				
			||||||
 | 
					import Map from "../Map";
 | 
				
			||||||
 | 
					import AABB from "../Shapes/AABB";
 | 
				
			||||||
 | 
					import Shape from "../Shapes/Shape";
 | 
				
			||||||
import Vec2 from "../Vec2";
 | 
					import Vec2 from "../Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Unique {
 | 
					export interface Unique {
 | 
				
			||||||
    getId: () => number;
 | 
					    /** The unique id of this object. */
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Positioned {
 | 
					export interface Positioned {
 | 
				
			||||||
    /**
 | 
					    /** The center of this object. */
 | 
				
			||||||
     * Returns the center of this object
 | 
					    position: Vec2;
 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getPosition: () => Vec2;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Region {
 | 
					export interface Region {
 | 
				
			||||||
    /**
 | 
					    /** The size of this object. */
 | 
				
			||||||
     * Returns the size of this object
 | 
					    size: Vec2;
 | 
				
			||||||
     */
 | 
					
 | 
				
			||||||
    getSize: () => Vec2;
 | 
					    /** The scale of this object. */
 | 
				
			||||||
 | 
					    scale: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The bounding box of this object. */
 | 
				
			||||||
 | 
					    boundary: AABB;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isRegion(arg: any): boolean {
 | 
				
			||||||
 | 
					    return arg && arg.size && arg.scale && arg.boundary;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
     * Returns the scale of this object
 | 
					 * Describes an object that can opt into physics.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
    getScale: () => Vec2;
 | 
					export interface Physical {
 | 
				
			||||||
 | 
					    /** A flag for whether or not this object has initialized game physics. */
 | 
				
			||||||
 | 
					    hasPhysics: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Represents whether the object is moving or not. */
 | 
				
			||||||
 | 
					    moving: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Represents whether the object is on the ground or not. */
 | 
				
			||||||
 | 
					    onGround: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Reprsents whether the object is on the wall or not. */
 | 
				
			||||||
 | 
					    onWall: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Reprsents whether the object is on the ceiling or not. */
 | 
				
			||||||
 | 
					    onCeiling: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Represnts whether this object has active physics or not. */
 | 
				
			||||||
 | 
					    active: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The shape of the collider for this physics object. */
 | 
				
			||||||
 | 
					    collisionShape: Shape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Represents whether this object can move or not. */
 | 
				
			||||||
 | 
					    isStatic: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Represents whether this object is collidable (solid) or not. */
 | 
				
			||||||
 | 
					    isCollidable: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Represnts whether this object is a trigger or not. */
 | 
				
			||||||
 | 
					    isTrigger: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The physics group of this object. Used for triggers and for selective collisions. */
 | 
				
			||||||
 | 
					    group: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Associates different groups with trigger events. */
 | 
				
			||||||
 | 
					    triggers: Map<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** A vector that allows velocity to be passed to the physics engine */
 | 
				
			||||||
 | 
					    _velocity: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The rectangle swept by the movement of this object, if dynamic */
 | 
				
			||||||
 | 
					    sweptRect: AABB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*---------- FUNCTIONS ----------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the bounding box of this object
 | 
					     * Tells the physics engine to handle a move by this object.
 | 
				
			||||||
 | 
					     * @param velocity The velocity with which to move the object.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getBoundary: () => AABB;
 | 
					    move: (velocity: Vec2) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The move actually done by the physics engine after collision checks are done.
 | 
				
			||||||
 | 
					     * @param velocity The velocity with which the object will move.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    finishMove: () => void;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Adds physics to this object
 | 
				
			||||||
 | 
					     * @param collisionShape The shape of this collider for this object
 | 
				
			||||||
 | 
					     * @param isCollidable Whether this object will be able to collide with other objects
 | 
				
			||||||
 | 
					     * @param isStatic Whether this object will be static or not
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    addPhysics: (collisionShape?: Shape, isCollidable?: boolean, isStatic?: boolean) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Adds a trigger to this object for a specific group
 | 
				
			||||||
 | 
					     * @param group The name of the group that activates the trigger
 | 
				
			||||||
 | 
					     * @param eventType The name of the event to send when this trigger is activated
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    addTrigger: (group: string, eventType: string) => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Updateable {
 | 
					export interface Updateable {
 | 
				
			||||||
    /**
 | 
					    /** Updates this object. */
 | 
				
			||||||
     * Updates this object
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    update: (deltaT: number) => void;
 | 
					    update: (deltaT: number) => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Renderable {
 | 
					export interface Renderable {
 | 
				
			||||||
    /**
 | 
					    /** Renders this object. */
 | 
				
			||||||
     * Renders this object
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    render: (ctx: CanvasRenderingContext2D) => void;
 | 
					    render: (ctx: CanvasRenderingContext2D) => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface Debug_Renderable {
 | 
				
			||||||
 | 
					    /** Renders the debugging infor for this object. */
 | 
				
			||||||
 | 
					    debug_render: (ctx: CanvasRenderingContext2D) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import Vec2 from "./Vec2";
 | 
					import Vec2 from "./Vec2";
 | 
				
			||||||
import Collection from "./Collection";
 | 
					import Collection from "./Collection";
 | 
				
			||||||
import AABB from "./AABB"
 | 
					import AABB from "./Shapes/AABB"
 | 
				
			||||||
import { Region, Unique } from "./Interfaces/Descriptors";
 | 
					import { Region, Unique } from "./Interfaces/Descriptors";
 | 
				
			||||||
import Map from "./Map";
 | 
					import Map from "./Map";
 | 
				
			||||||
import Stats from "../Debug/Stats";
 | 
					import Stats from "../Debug/Stats";
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    insert(item: T): void {
 | 
					    insert(item: T): void {
 | 
				
			||||||
        // If the item is inside of the bounds of this quadtree
 | 
					        // If the item is inside of the bounds of this quadtree
 | 
				
			||||||
        if(this.boundary.overlaps(item.getBoundary())){
 | 
					        if(this.boundary.overlaps(item.boundary)){
 | 
				
			||||||
            if(this.divided){
 | 
					            if(this.divided){
 | 
				
			||||||
                // Defer to the children
 | 
					                // Defer to the children
 | 
				
			||||||
                this.deferInsert(item);
 | 
					                this.deferInsert(item);
 | 
				
			||||||
| 
						 | 
					@ -124,9 +124,9 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Otherwise, return a set of the items
 | 
					            // Otherwise, return a set of the items
 | 
				
			||||||
            for(let item of this.items){
 | 
					            for(let item of this.items){
 | 
				
			||||||
                let id = item.getId().toString();
 | 
					                let id = item.id.toString();
 | 
				
			||||||
                // If the item hasn't been found yet and it contains the point
 | 
					                // If the item hasn't been found yet and it contains the point
 | 
				
			||||||
                if(!uniqueMap.has(id) && item.getBoundary().containsPoint(point)){
 | 
					                if(!uniqueMap.has(id) && item.boundary.containsPoint(point)){
 | 
				
			||||||
                    // Add it to our found points
 | 
					                    // Add it to our found points
 | 
				
			||||||
                    uniqueMap.add(id, item);
 | 
					                    uniqueMap.add(id, item);
 | 
				
			||||||
                    results.push(item);
 | 
					                    results.push(item);
 | 
				
			||||||
| 
						 | 
					@ -182,10 +182,10 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
 | 
				
			||||||
                // }
 | 
					                // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Maybe this is better? Just use a boolean array with no string nonsense?
 | 
					                // Maybe this is better? Just use a boolean array with no string nonsense?
 | 
				
			||||||
                if(item.getId() >= uniqueMap.length || !uniqueMap[item.getId()]){
 | 
					                if(item.id >= uniqueMap.length || !uniqueMap[item.id]){
 | 
				
			||||||
                    if(item.getBoundary().overlaps(boundary)){
 | 
					                    if(item.boundary.overlaps(boundary)){
 | 
				
			||||||
                        results.push(item);
 | 
					                        results.push(item);
 | 
				
			||||||
                        uniqueMap[item.getId()] = true;
 | 
					                        uniqueMap[item.id] = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +0,0 @@
 | 
				
			||||||
import AABB from "./AABB";
 | 
					 | 
				
			||||||
import Vec2 from "./Vec2";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default abstract class Shape {
 | 
					 | 
				
			||||||
    abstract setCenter(center: Vec2): void;
 | 
					 | 
				
			||||||
    abstract getCenter(): Vec2;
 | 
					 | 
				
			||||||
    abstract getBoundingRect(): AABB;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,12 @@
 | 
				
			||||||
import Shape from "./Shape";
 | 
					import Shape from "./Shape";
 | 
				
			||||||
import Vec2 from "./Vec2";
 | 
					import Vec2 from "../Vec2";
 | 
				
			||||||
import MathUtils from "../Utils/MathUtils";
 | 
					import MathUtils from "../../Utils/MathUtils";
 | 
				
			||||||
 | 
					import Circle from "./Circle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class AABB extends Shape {
 | 
					export default class AABB extends Shape {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected center: Vec2;
 | 
					    center: Vec2;
 | 
				
			||||||
    protected halfSize: Vec2;
 | 
					    halfSize: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(center?: Vec2, halfSize?: Vec2){
 | 
					    constructor(center?: Vec2, halfSize?: Vec2){
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
| 
						 | 
					@ -45,16 +46,13 @@ export default class AABB extends Shape {
 | 
				
			||||||
        return this.x + this.hw;
 | 
					        return this.x + this.hw;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getCenter(): Vec2 {
 | 
					 | 
				
			||||||
        return this.center;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setCenter(center: Vec2): void {
 | 
					 | 
				
			||||||
        this.center = center;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getBoundingRect(): AABB {
 | 
					    getBoundingRect(): AABB {
 | 
				
			||||||
        return this;
 | 
					        return this.clone();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getBoundingCircle(): Circle {
 | 
				
			||||||
 | 
					        let r = Math.max(this.hw, this.hh)
 | 
				
			||||||
 | 
					        return new Circle(this.center.clone(), r);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getHalfSize(): Vec2 {
 | 
					    getHalfSize(): Vec2 {
 | 
				
			||||||
| 
						 | 
					@ -126,10 +124,10 @@ export default class AABB extends Shape {
 | 
				
			||||||
        let signX = MathUtils.sign(scaleX);
 | 
					        let signX = MathUtils.sign(scaleX);
 | 
				
			||||||
        let signY = MathUtils.sign(scaleY);
 | 
					        let signY = MathUtils.sign(scaleY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let tnearx = scaleX*(this.center.x - signX*(this.halfSize.x + _paddingX) - point.x);
 | 
					        let tnearx = scaleX*(this.x - signX*(this.hw + _paddingX) - point.x);
 | 
				
			||||||
        let tneary = scaleX*(this.center.y - signY*(this.halfSize.y + _paddingY) - point.y);
 | 
					        let tneary = scaleX*(this.y - signY*(this.hh + _paddingY) - point.y);
 | 
				
			||||||
        let tfarx = scaleY*(this.center.x + signX*(this.halfSize.x + _paddingX) - point.x);
 | 
					        let tfarx = scaleY*(this.x + signX*(this.hw + _paddingX) - point.x);
 | 
				
			||||||
        let tfary = scaleY*(this.center.y + signY*(this.halfSize.y + _paddingY) - point.y);
 | 
					        let tfary = scaleY*(this.y + signY*(this.hh + _paddingY) - point.y);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if(tnearx > tfary || tneary > tfarx){
 | 
					        if(tnearx > tfary || tneary > tfarx){
 | 
				
			||||||
            // We aren't colliding - we clear one axis before intersecting another
 | 
					            // We aren't colliding - we clear one axis before intersecting another
 | 
				
			||||||
| 
						 | 
					@ -164,11 +162,18 @@ export default class AABB extends Shape {
 | 
				
			||||||
        return hit;
 | 
					        return hit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    overlaps(other: Shape): boolean {
 | 
				
			||||||
 | 
					        if(other instanceof AABB){
 | 
				
			||||||
 | 
					            return this.overlapsAABB(other);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw "Overlap not defined between these shapes."
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * A simple boolean check of whether this AABB overlaps another
 | 
					     * A simple boolean check of whether this AABB overlaps another
 | 
				
			||||||
     * @param other 
 | 
					     * @param other 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    overlaps(other: AABB): boolean {
 | 
					    overlapsAABB(other: AABB): boolean {
 | 
				
			||||||
        let dx = other.x - this.x;
 | 
					        let dx = other.x - this.x;
 | 
				
			||||||
        let px = this.hw + other.hw - Math.abs(dx);
 | 
					        let px = this.hw + other.hw - Math.abs(dx);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
| 
						 | 
					@ -200,6 +205,35 @@ export default class AABB extends Shape {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return dx*dy;
 | 
					        return dx*dy;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Moves and resizes this rect from its current position to the position specified
 | 
				
			||||||
 | 
					     * @param velocity The movement of the rect from its position
 | 
				
			||||||
 | 
					     * @param fromPosition A position specified to be the starting point of sweeping
 | 
				
			||||||
 | 
					     * @param halfSize The halfSize of the sweeping rect 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    sweep(velocity: Vec2, fromPosition?: Vec2, halfSize?: Vec2): void {
 | 
				
			||||||
 | 
					        if(!fromPosition){
 | 
				
			||||||
 | 
					            fromPosition = this.center;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!halfSize){
 | 
				
			||||||
 | 
					            halfSize = this.halfSize;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let centerX = fromPosition.x + velocity.x/2;
 | 
				
			||||||
 | 
					        let centerY = fromPosition.y + velocity.y/2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let minX = Math.min(fromPosition.x - halfSize.x, fromPosition.x + velocity.x - halfSize.x);
 | 
				
			||||||
 | 
					        let minY = Math.min(fromPosition.y - halfSize.y, fromPosition.y + velocity.y - halfSize.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.center.set(centerX, centerY);
 | 
				
			||||||
 | 
					        this.halfSize.set(centerX - minX, centerY - minY);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    clone(): AABB {
 | 
				
			||||||
 | 
					        return new AABB(this.center.clone(), this.halfSize.clone());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Hit {
 | 
					export class Hit {
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/DataTypes/Shapes/Circle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/DataTypes/Shapes/Circle.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,42 @@
 | 
				
			||||||
 | 
					import Vec2 from "../Vec2";
 | 
				
			||||||
 | 
					import AABB from "./AABB";
 | 
				
			||||||
 | 
					import Shape from "./Shape";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Circle extends Shape {
 | 
				
			||||||
 | 
						private _center: Vec2;
 | 
				
			||||||
 | 
						private radius: number;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						constructor(center: Vec2, radius: number) {
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
					        this._center = center ? center : new Vec2(0, 0);
 | 
				
			||||||
 | 
					        this.radius = radius ? radius : 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get center(): Vec2 {
 | 
				
			||||||
 | 
							return this._center;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set center(center: Vec2) {
 | 
				
			||||||
 | 
							this._center = center;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get halfSize(): Vec2 {
 | 
				
			||||||
 | 
							return new Vec2(this.radius, this.radius);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getBoundingRect(): AABB {
 | 
				
			||||||
 | 
							return new AABB(this._center.clone(), new Vec2(this.radius, this.radius));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getBoundingCircle(): Circle {
 | 
				
			||||||
 | 
							return this.clone();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						overlaps(other: Shape): boolean {
 | 
				
			||||||
 | 
							throw new Error("Method not implemented.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clone(): Circle {
 | 
				
			||||||
 | 
							return new Circle(this._center.clone(), this.radius);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										111
									
								
								src/DataTypes/Shapes/Shape.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/DataTypes/Shapes/Shape.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					import Vec2 from "../Vec2";
 | 
				
			||||||
 | 
					import AABB from "./AABB";
 | 
				
			||||||
 | 
					import Circle from "./Circle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class Shape {
 | 
				
			||||||
 | 
					    abstract get center(): Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract set center(center: Vec2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract get halfSize(): Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Gets a bounding rectangle for this shape */
 | 
				
			||||||
 | 
					    abstract getBoundingRect(): AABB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Gets a bounding circle for this shape */
 | 
				
			||||||
 | 
					    abstract getBoundingCircle(): Circle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns a copy of this Shape */
 | 
				
			||||||
 | 
					    abstract clone(): Shape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Checks if this shape overlaps another */
 | 
				
			||||||
 | 
					    abstract overlaps(other: Shape): boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static getTimeOfCollision(A: Shape, velA: Vec2, B: Shape, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
 | 
				
			||||||
 | 
							if(A instanceof AABB && B instanceof AABB){
 | 
				
			||||||
 | 
								return Shape.getTimeOfCollision_AABB_AABB(A, velA, B, velB);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static getTimeOfCollision_AABB_AABB(A: AABB, velA: Vec2, B: Shape, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
 | 
				
			||||||
 | 
					        let posSmaller = A.center;
 | 
				
			||||||
 | 
					        let posLarger = B.center;
 | 
				
			||||||
 | 
					        let sizeSmaller = A.halfSize;
 | 
				
			||||||
 | 
					        let sizeLarger = B.halfSize;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        let firstContact = new Vec2(0, 0);
 | 
				
			||||||
 | 
					        let lastContact = new Vec2(0, 0);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        let collidingX = false;
 | 
				
			||||||
 | 
					        let collidingY = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        // Sort by position
 | 
				
			||||||
 | 
					        if(posLarger.x < posSmaller.x){
 | 
				
			||||||
 | 
					            // Swap, because smaller is further right than larger
 | 
				
			||||||
 | 
					            let temp: Vec2;
 | 
				
			||||||
 | 
					            temp = sizeSmaller;
 | 
				
			||||||
 | 
					            sizeSmaller = sizeLarger;
 | 
				
			||||||
 | 
					            sizeLarger = temp;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            temp = posSmaller;
 | 
				
			||||||
 | 
					            posSmaller = posLarger;
 | 
				
			||||||
 | 
					            posLarger = temp;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            temp = velA;
 | 
				
			||||||
 | 
					            velA = velB;
 | 
				
			||||||
 | 
					            velB = temp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        // A is left, B is right
 | 
				
			||||||
 | 
					        firstContact.x = Infinity;
 | 
				
			||||||
 | 
					        lastContact.x = Infinity;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        if (posLarger.x - sizeLarger.x >= posSmaller.x + sizeSmaller.x){
 | 
				
			||||||
 | 
					            // If we aren't currently colliding
 | 
				
			||||||
 | 
					            let relVel = velA.x - velB.x;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(relVel > 0){
 | 
				
			||||||
 | 
					                // If they are moving towards each other
 | 
				
			||||||
 | 
					                firstContact.x = ((posLarger.x - sizeLarger.x) - (posSmaller.x + sizeSmaller.x))/(relVel);
 | 
				
			||||||
 | 
					                lastContact.x = ((posLarger.x + sizeLarger.x) - (posSmaller.x - sizeSmaller.x))/(relVel);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            collidingX = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        if(posLarger.y < posSmaller.y){
 | 
				
			||||||
 | 
					            // Swap, because smaller is further up than larger
 | 
				
			||||||
 | 
					            let temp: Vec2;
 | 
				
			||||||
 | 
					            temp = sizeSmaller;
 | 
				
			||||||
 | 
					            sizeSmaller = sizeLarger;
 | 
				
			||||||
 | 
					            sizeLarger = temp;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            temp = posSmaller;
 | 
				
			||||||
 | 
					            posSmaller = posLarger;
 | 
				
			||||||
 | 
					            posLarger = temp;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            temp = velA;
 | 
				
			||||||
 | 
					            velA = velB;
 | 
				
			||||||
 | 
					            velB = temp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        // A is top, B is bottom
 | 
				
			||||||
 | 
					        firstContact.y = Infinity;
 | 
				
			||||||
 | 
					        lastContact.y = Infinity;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        if (posLarger.y - sizeLarger.y >= posSmaller.y + sizeSmaller.y){
 | 
				
			||||||
 | 
					            // If we aren't currently colliding
 | 
				
			||||||
 | 
					            let relVel = velA.y - velB.y;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(relVel > 0){
 | 
				
			||||||
 | 
					                // If they are moving towards each other
 | 
				
			||||||
 | 
					                firstContact.y = ((posLarger.y - sizeLarger.y) - (posSmaller.y + sizeSmaller.y))/(relVel);
 | 
				
			||||||
 | 
					                lastContact.y = ((posLarger.y + sizeLarger.y) - (posSmaller.y - sizeSmaller.y))/(relVel);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            collidingY = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        return [firstContact, lastContact, collidingX, collidingY];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,11 +4,11 @@ import { Updateable } from "../Interfaces/Descriptors";
 | 
				
			||||||
import StateMachine from "./StateMachine";
 | 
					import StateMachine from "./StateMachine";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default abstract class State implements Updateable {
 | 
					export default abstract class State implements Updateable {
 | 
				
			||||||
    protected parentStateMachine: StateMachine;
 | 
					    protected parent: StateMachine;
 | 
				
			||||||
    protected emitter: Emitter;
 | 
					    protected emitter: Emitter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(parent: StateMachine) {
 | 
					    constructor(parent: StateMachine) {
 | 
				
			||||||
        this.parentStateMachine = parent;
 | 
					        this.parent = parent;
 | 
				
			||||||
        this.emitter = new Emitter();
 | 
					        this.emitter = new Emitter();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ export default abstract class State implements Updateable {
 | 
				
			||||||
     * @param stateName The name of the state to transition to
 | 
					     * @param stateName The name of the state to transition to
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected finished(stateName: string): void {
 | 
					    protected finished(stateName: string): void {
 | 
				
			||||||
        this.parentStateMachine.changeState(stateName);
 | 
					        this.parent.changeState(stateName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,8 @@ export default class Vec2 {
 | 
				
			||||||
		return new Vec2(0, 0);
 | 
							return new Vec2(0, 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static readonly ZERO_STATIC = new Vec2(0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static get INF() {
 | 
						static get INF() {
 | 
				
			||||||
		return new Vec2(Infinity, Infinity);
 | 
							return new Vec2(Infinity, Infinity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
import Scene from "./Scene/Scene";
 | 
					import Scene from "./Scene/Scene";
 | 
				
			||||||
import OrthogonalTilemap from "./Nodes/Tilemaps/OrthogonalTilemap";
 | 
					 | 
				
			||||||
import Player from "./Player";
 | 
					 | 
				
			||||||
import Rect from "./Nodes/Graphics/Rect";
 | 
					import Rect from "./Nodes/Graphics/Rect";
 | 
				
			||||||
import Color from "./Utils/Color";
 | 
					import Color from "./Utils/Color";
 | 
				
			||||||
import Vec2 from "./DataTypes/Vec2";
 | 
					import Vec2 from "./DataTypes/Vec2";
 | 
				
			||||||
| 
						 | 
					@ -10,6 +8,7 @@ import Layer from "./Scene/Layer";
 | 
				
			||||||
import SecondScene from "./SecondScene";
 | 
					import SecondScene from "./SecondScene";
 | 
				
			||||||
import { GameEventType } from "./Events/GameEventType";
 | 
					import { GameEventType } from "./Events/GameEventType";
 | 
				
			||||||
import SceneGraphQuadTree from "./SceneGraph/SceneGraphQuadTree";
 | 
					import SceneGraphQuadTree from "./SceneGraph/SceneGraphQuadTree";
 | 
				
			||||||
 | 
					import PlayerController from "./_DemoClasses/Player/PlayerStates/Platformer/PlayerController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class MainScene extends Scene {
 | 
					export default class MainScene extends Scene {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +26,7 @@ export default class MainScene extends Scene {
 | 
				
			||||||
        bar.setColor(new Color(0, 200, 200));
 | 
					        bar.setColor(new Color(0, 200, 200));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.load.onLoadProgress = (percentProgress: number) => {
 | 
					        this.load.onLoadProgress = (percentProgress: number) => {
 | 
				
			||||||
            bar.setSize(295 * percentProgress, bar.getSize().y);
 | 
					            bar.size.x = 295 * percentProgress;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.load.onLoadComplete = () => {
 | 
					        this.load.onLoadComplete = () => {
 | 
				
			||||||
| 
						 | 
					@ -58,40 +57,38 @@ export default class MainScene extends Scene {
 | 
				
			||||||
        let mainLayer = this.addLayer();
 | 
					        let mainLayer = this.addLayer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add a player
 | 
					        // Add a player
 | 
				
			||||||
        let player = this.add.physics(Player, mainLayer, "platformer");
 | 
					 | 
				
			||||||
        let playerSprite = this.add.sprite("player", mainLayer)
 | 
					        let playerSprite = this.add.sprite("player", mainLayer)
 | 
				
			||||||
        player.setSprite(playerSprite);
 | 
					        playerSprite.position.set(0, 0);
 | 
				
			||||||
        playerSprite.position = player.position.clone();
 | 
					        playerSprite.size.set(64, 64);
 | 
				
			||||||
        playerSprite.setSize(new Vec2(64, 64));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.viewport.follow(player);
 | 
					        this.viewport.follow(playerSprite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Initialize UI
 | 
					        // Initialize UI
 | 
				
			||||||
        let uiLayer = this.addLayer();
 | 
					        let uiLayer = this.addLayer();
 | 
				
			||||||
        uiLayer.setParallax(0, 0);
 | 
					        uiLayer.setParallax(0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let recordButton = this.add.uiElement(Button, uiLayer);
 | 
					        let recordButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        recordButton.setSize(100, 50);
 | 
					        recordButton.size.set(100, 50);
 | 
				
			||||||
        recordButton.setText("Record");
 | 
					        recordButton.setText("Record");
 | 
				
			||||||
        recordButton.setPosition(400, 30);
 | 
					        recordButton.position.set(400, 30);
 | 
				
			||||||
        recordButton.onClickEventId = GameEventType.START_RECORDING;
 | 
					        recordButton.onClickEventId = GameEventType.START_RECORDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let stopButton = this.add.uiElement(Button, uiLayer);
 | 
					        let stopButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        stopButton.setSize(100, 50);
 | 
					        stopButton.size.set(100, 50);
 | 
				
			||||||
        stopButton.setText("Stop");
 | 
					        stopButton.setText("Stop");
 | 
				
			||||||
        stopButton.setPosition(550, 30);
 | 
					        stopButton.position.set(550, 30);
 | 
				
			||||||
        stopButton.onClickEventId = GameEventType.STOP_RECORDING;
 | 
					        stopButton.onClickEventId = GameEventType.STOP_RECORDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let playButton = this.add.uiElement(Button, uiLayer);
 | 
					        let playButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        playButton.setSize(100, 50);
 | 
					        playButton.size.set(100, 50);
 | 
				
			||||||
        playButton.setText("Play");
 | 
					        playButton.setText("Play");
 | 
				
			||||||
        playButton.setPosition(700, 30);
 | 
					        playButton.position.set(700, 30);
 | 
				
			||||||
        playButton.onClickEventId = GameEventType.PLAY_RECORDING;
 | 
					        playButton.onClickEventId = GameEventType.PLAY_RECORDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let cycleFramerateButton = this.add.uiElement(Button, uiLayer);
 | 
					        let cycleFramerateButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        cycleFramerateButton.setSize(150, 50);
 | 
					        cycleFramerateButton.size.set(150, 50);
 | 
				
			||||||
        cycleFramerateButton.setText("Cycle FPS");
 | 
					        cycleFramerateButton.setText("Cycle FPS");
 | 
				
			||||||
        cycleFramerateButton.setPosition(5, 400);
 | 
					        cycleFramerateButton.position.set(5, 400);
 | 
				
			||||||
        let i = 0;
 | 
					        let i = 0;
 | 
				
			||||||
        let fps = [15, 30, 60];
 | 
					        let fps = [15, 30, 60];
 | 
				
			||||||
        cycleFramerateButton.onClick = () => {
 | 
					        cycleFramerateButton.onClick = () => {
 | 
				
			||||||
| 
						 | 
					@ -105,32 +102,32 @@ export default class MainScene extends Scene {
 | 
				
			||||||
        pauseLayer.disable();
 | 
					        pauseLayer.disable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let pauseButton = this.add.uiElement(Button, uiLayer);
 | 
					        let pauseButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        pauseButton.setSize(100, 50);
 | 
					        pauseButton.size.set(100, 50);
 | 
				
			||||||
        pauseButton.setText("Pause");
 | 
					        pauseButton.setText("Pause");
 | 
				
			||||||
        pauseButton.setPosition(700, 400);
 | 
					        pauseButton.position.set(700, 400);
 | 
				
			||||||
        pauseButton.onClick = () => {
 | 
					        pauseButton.onClick = () => {
 | 
				
			||||||
            this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(true));
 | 
					            this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(true));
 | 
				
			||||||
            pauseLayer.enable();
 | 
					            pauseLayer.enable();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let modalBackground = this.add.uiElement(UIElement, pauseLayer);
 | 
					        let modalBackground = this.add.uiElement(UIElement, pauseLayer);
 | 
				
			||||||
        modalBackground.setSize(400, 200);
 | 
					        modalBackground.size.set(400, 200);
 | 
				
			||||||
        modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
 | 
					        modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
 | 
				
			||||||
        modalBackground.setPosition(200, 100);
 | 
					        modalBackground.position.set(200, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let resumeButton = this.add.uiElement(Button, pauseLayer);
 | 
					        let resumeButton = this.add.uiElement(Button, pauseLayer);
 | 
				
			||||||
        resumeButton.setSize(100, 50);
 | 
					        resumeButton.size.set(100, 50);
 | 
				
			||||||
        resumeButton.setText("Resume");
 | 
					        resumeButton.setText("Resume");
 | 
				
			||||||
        resumeButton.setPosition(360, 150);
 | 
					        resumeButton.position.set(360, 150);
 | 
				
			||||||
        resumeButton.onClick = () => {
 | 
					        resumeButton.onClick = () => {
 | 
				
			||||||
            this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(false));
 | 
					            this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(false));
 | 
				
			||||||
            pauseLayer.disable();
 | 
					            pauseLayer.disable();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let switchButton = this.add.uiElement(Button, pauseLayer);
 | 
					        let switchButton = this.add.uiElement(Button, pauseLayer);
 | 
				
			||||||
        switchButton.setSize(140, 50);
 | 
					        switchButton.size.set(140, 50);
 | 
				
			||||||
        switchButton.setText("Change Scene");
 | 
					        switchButton.setText("Change Scene");
 | 
				
			||||||
        switchButton.setPosition(340, 190);
 | 
					        switchButton.position.set(340, 190);
 | 
				
			||||||
        switchButton.onClick = () => {
 | 
					        switchButton.onClick = () => {
 | 
				
			||||||
            this.emitter.fireEvent(GameEventType.STOP_SOUND, {key: "level_music"});
 | 
					            this.emitter.fireEvent(GameEventType.STOP_SOUND, {key: "level_music"});
 | 
				
			||||||
            this.sceneManager.changeScene(SecondScene);
 | 
					            this.sceneManager.changeScene(SecondScene);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import GameNode from "./GameNode";
 | 
					import GameNode from "./GameNode";
 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
import { Region } from "../DataTypes/Interfaces/Descriptors";
 | 
					import { Region } from "../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The representation of an object in the game world that can be drawn to the screen
 | 
					 * The representation of an object in the game world that can be drawn to the screen
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import AABB from "../DataTypes/AABB";
 | 
				
			||||||
export default abstract class CanvasNode extends GameNode implements Region {
 | 
					export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
	private _size: Vec2;
 | 
						private _size: Vec2;
 | 
				
			||||||
	private _scale: Vec2;
 | 
						private _scale: Vec2;
 | 
				
			||||||
	private boundary: AABB;
 | 
						private _boundary: AABB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(){
 | 
						constructor(){
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
		this._size.setOnChange(this.sizeChanged);
 | 
							this._size.setOnChange(this.sizeChanged);
 | 
				
			||||||
		this._scale = new Vec2(1, 1);
 | 
							this._scale = new Vec2(1, 1);
 | 
				
			||||||
		this._scale.setOnChange(this.scaleChanged);
 | 
							this._scale.setOnChange(this.scaleChanged);
 | 
				
			||||||
		this.boundary = new AABB();
 | 
							this._boundary = new AABB();
 | 
				
			||||||
		this.updateBoundary();
 | 
							this.updateBoundary();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,34 +42,11 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
		this.scaleChanged();
 | 
							this.scaleChanged();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getSize(): Vec2 {
 | 
					 | 
				
			||||||
		return this.size.clone();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setSize(vecOrX: Vec2 | number, y: number = null): void {
 | 
					 | 
				
			||||||
		if(vecOrX instanceof Vec2){
 | 
					 | 
				
			||||||
			this.size.set(vecOrX.x, vecOrX.y);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			this.size.set(vecOrX, y);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
     * Returns the scale of the sprite
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getScale(): Vec2 {
 | 
					 | 
				
			||||||
        return this.scale.clone();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Sets the scale of the sprite to the value provided
 | 
					 | 
				
			||||||
     * @param scale 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    setScale(scale: Vec2): void {
 | 
					 | 
				
			||||||
		this.scale = scale;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected positionChanged = (): void => {
 | 
						protected positionChanged = (): void => {
 | 
				
			||||||
 | 
							if(this.hasPhysics){
 | 
				
			||||||
 | 
								this.collisionShape.center = this.position;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		this.updateBoundary();
 | 
							this.updateBoundary();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,12 +59,12 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private updateBoundary(): void {
 | 
						private updateBoundary(): void {
 | 
				
			||||||
		this.boundary.setCenter(this.position.clone());
 | 
							this._boundary.center.set(this.position.x, this.position.y);
 | 
				
			||||||
		this.boundary.setHalfSize(this.size.clone().mult(this.scale).scale(1/2));
 | 
							this._boundary.halfSize.set(this.size.x*this.scale.x/2, this.size.y*this.scale.y/2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getBoundary(): AABB {
 | 
						get boundary(): AABB {
 | 
				
			||||||
		return this.boundary;
 | 
							return this._boundary;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -96,7 +73,7 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
	 * @param y 
 | 
						 * @param y 
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	contains(x: number, y: number): boolean {
 | 
						contains(x: number, y: number): boolean {
 | 
				
			||||||
		return this.boundary.containsPoint(new Vec2(x, y));
 | 
							return this._boundary.containsPoint(new Vec2(x, y));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	abstract render(ctx: CanvasRenderingContext2D): void;
 | 
						abstract render(ctx: CanvasRenderingContext2D): void;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,19 +4,44 @@ import Receiver from "../Events/Receiver";
 | 
				
			||||||
import Emitter from "../Events/Emitter";
 | 
					import Emitter from "../Events/Emitter";
 | 
				
			||||||
import Scene from "../Scene/Scene";
 | 
					import Scene from "../Scene/Scene";
 | 
				
			||||||
import Layer from "../Scene/Layer";
 | 
					import Layer from "../Scene/Layer";
 | 
				
			||||||
import { Positioned, Unique, Updateable } from "../DataTypes/Interfaces/Descriptors"
 | 
					import { Physical, Positioned, isRegion, Unique, Updateable, Region } from "../DataTypes/Interfaces/Descriptors"
 | 
				
			||||||
 | 
					import Shape from "../DataTypes/Shapes/Shape";
 | 
				
			||||||
 | 
					import GameEvent from "../Events/GameEvent";
 | 
				
			||||||
 | 
					import Map from "../DataTypes/Map";
 | 
				
			||||||
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The representation of an object in the game world
 | 
					 * The representation of an object in the game world
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default abstract class GameNode implements Positioned, Unique, Updateable {
 | 
					export default abstract class GameNode implements Positioned, Unique, Updateable, Physical {
 | 
				
			||||||
	protected input: InputReceiver;
 | 
						/*---------- POSITIONED ----------*/
 | 
				
			||||||
	private _position: Vec2;
 | 
						private _position: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*---------- UNIQUE ----------*/
 | 
				
			||||||
 | 
						private _id: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*---------- PHYSICAL ----------*/
 | 
				
			||||||
 | 
						hasPhysics: boolean;
 | 
				
			||||||
 | 
						moving: boolean;
 | 
				
			||||||
 | 
						onGround: boolean;
 | 
				
			||||||
 | 
						onWall: boolean;
 | 
				
			||||||
 | 
						onCeiling: boolean;
 | 
				
			||||||
 | 
						active: boolean;
 | 
				
			||||||
 | 
						collisionShape: Shape;
 | 
				
			||||||
 | 
						isStatic: boolean;
 | 
				
			||||||
 | 
						isCollidable: boolean;
 | 
				
			||||||
 | 
						isTrigger: boolean;
 | 
				
			||||||
 | 
						group: string;
 | 
				
			||||||
 | 
						triggers: Map<string>;
 | 
				
			||||||
 | 
						_velocity: Vec2;
 | 
				
			||||||
 | 
						sweptRect: AABB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected input: InputReceiver;
 | 
				
			||||||
	protected receiver: Receiver;
 | 
						protected receiver: Receiver;
 | 
				
			||||||
	protected emitter: Emitter;
 | 
						protected emitter: Emitter;
 | 
				
			||||||
	protected scene: Scene;
 | 
						protected scene: Scene;
 | 
				
			||||||
	protected layer: Layer;
 | 
						protected layer: Layer;
 | 
				
			||||||
	private id: number;
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(){
 | 
						constructor(){
 | 
				
			||||||
		this.input = InputReceiver.getInstance();
 | 
							this.input = InputReceiver.getInstance();
 | 
				
			||||||
| 
						 | 
					@ -26,22 +51,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
		this.emitter = new Emitter();
 | 
							this.emitter = new Emitter();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setScene(scene: Scene): void {
 | 
						/*---------- POSITIONED ----------*/
 | 
				
			||||||
		this.scene = scene;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	getScene(): Scene {
 | 
					 | 
				
			||||||
		return this.scene;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setLayer(layer: Layer): void {
 | 
					 | 
				
			||||||
		this.layer = layer;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	getLayer(): Layer {
 | 
					 | 
				
			||||||
		return this.layer;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	get position(): Vec2 {
 | 
						get position(): Vec2 {
 | 
				
			||||||
		return this._position;
 | 
							return this._position;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -52,30 +62,116 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
		this.positionChanged();
 | 
							this.positionChanged();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getPosition(): Vec2 {
 | 
						/*---------- UNIQUE ----------*/
 | 
				
			||||||
		return this._position.clone();
 | 
						get id(): number {
 | 
				
			||||||
 | 
							return this._id;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setPosition(vecOrX: Vec2 | number, y: number = null): void {
 | 
						set id(id: number) {
 | 
				
			||||||
		if(vecOrX instanceof Vec2){
 | 
							// id can only be set once
 | 
				
			||||||
			this.position.set(vecOrX.x, vecOrX.y);
 | 
							if(this._id === undefined){
 | 
				
			||||||
 | 
								this._id = id;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this.position.set(vecOrX, y);
 | 
								throw "Attempted to assign id to object that already has id."
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setId(id: number): void {
 | 
						/*---------- PHYSICAL ----------*/
 | 
				
			||||||
		this.id = id;
 | 
						/**
 | 
				
			||||||
 | 
					     * @param velocity The velocity with which to move the object.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
						move = (velocity: Vec2): void => {
 | 
				
			||||||
 | 
							this.moving = true;
 | 
				
			||||||
 | 
							this._velocity = velocity;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @param velocity The velocity with which the object will move.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
						finishMove = (): void => {
 | 
				
			||||||
 | 
							this.moving = false;
 | 
				
			||||||
 | 
							this.position.add(this._velocity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getId(): number {
 | 
						/**
 | 
				
			||||||
		return this.id;
 | 
						 * @param collisionShape The collider for this object. If this has a region (implements Region),
 | 
				
			||||||
 | 
						 * it will be used when no collision shape is specified (or if collision shape is null).
 | 
				
			||||||
 | 
						 * @param isCollidable Whether this is collidable or not. True by default.
 | 
				
			||||||
 | 
						 * @param isStatic Whether this is static or not. False by default
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						addPhysics = (collisionShape?: Shape, isCollidable: boolean = true, isStatic: boolean = false): void => {
 | 
				
			||||||
 | 
							this.hasPhysics = true;
 | 
				
			||||||
 | 
							this.moving = false;
 | 
				
			||||||
 | 
							this.onGround = false;
 | 
				
			||||||
 | 
							this.onWall = false;
 | 
				
			||||||
 | 
							this.onCeiling= false;
 | 
				
			||||||
 | 
							this.active = true;
 | 
				
			||||||
 | 
							this.isCollidable = isCollidable;
 | 
				
			||||||
 | 
							this.isStatic = isStatic;
 | 
				
			||||||
 | 
							this.isTrigger = false;
 | 
				
			||||||
 | 
							this.group = "";
 | 
				
			||||||
 | 
							this.triggers = new Map();
 | 
				
			||||||
 | 
							this._velocity = Vec2.ZERO;
 | 
				
			||||||
 | 
							this.sweptRect = new AABB();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(collisionShape){
 | 
				
			||||||
 | 
								this.collisionShape = collisionShape;
 | 
				
			||||||
 | 
							} else if (isRegion(this)) {
 | 
				
			||||||
 | 
								// If the gamenode has a region and no other is specified, use that
 | 
				
			||||||
 | 
								this.collisionShape = (<any>this).boundary.clone();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								throw "No collision shape specified for physics object."
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.sweptRect = this.collisionShape.getBoundingRect();
 | 
				
			||||||
 | 
							this.scene.getPhysicsManager().registerObject(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param group The name of the group that will activate the trigger
 | 
				
			||||||
 | 
						 * @param eventType The type of this event to send when this trigger is activated
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					    addTrigger = (group: string, eventType: string): void => {
 | 
				
			||||||
 | 
							this.isTrigger = true;
 | 
				
			||||||
 | 
							this.triggers.add(group, eventType);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*---------- GAME NODE ----------*/
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Sets the scene for this object.
 | 
				
			||||||
 | 
						 * @param scene The scene this object belongs to.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						setScene(scene: Scene): void {
 | 
				
			||||||
 | 
							this.scene = scene;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Gets the scene this object is in. */
 | 
				
			||||||
 | 
						getScene(): Scene {
 | 
				
			||||||
 | 
							return this.scene;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Sets the layer of this object.
 | 
				
			||||||
 | 
						 * @param layer The layer this object will be on.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						setLayer(layer: Layer): void {
 | 
				
			||||||
 | 
							this.layer = layer;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Returns the layer this object is on. */
 | 
				
			||||||
 | 
						getLayer(): Layer {
 | 
				
			||||||
 | 
							return this.layer;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Called if the position vector is modified or replaced
 | 
						 * Called if the position vector is modified or replaced
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected positionChanged = (): void => {};
 | 
						// TODO - For some reason this isn't recognized in the child class
 | 
				
			||||||
 | 
						protected positionChanged = (): void => {
 | 
				
			||||||
 | 
							if(this.hasPhysics){
 | 
				
			||||||
 | 
								this.collisionShape.center = this.position;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO - This doesn't seem ideal. Is there a better way to do this?
 | 
						// TODO - This doesn't seem ideal. Is there a better way to do this?
 | 
				
			||||||
	getViewportOriginWithParallax(): Vec2 {
 | 
						getViewportOriginWithParallax(): Vec2 {
 | 
				
			||||||
| 
						 | 
					@ -86,6 +182,5 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
		return this.scene.getViewport().getZoomLevel();
 | 
							return this.scene.getViewport().getZoomLevel();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
	abstract update(deltaT: number): void;
 | 
						abstract update(deltaT: number): void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ export default class Point extends Graphic {
 | 
				
			||||||
    constructor(position: Vec2){
 | 
					    constructor(position: Vec2){
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.position = position;
 | 
					        this.position = position;
 | 
				
			||||||
        this.setSize(5, 5);
 | 
					        this.size.set(5, 5);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update(deltaT: number): void {}
 | 
					    update(deltaT: number): void {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,7 @@ export default class Sprite extends CanvasNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ctx.lineWidth = 4;
 | 
					        ctx.lineWidth = 4;
 | 
				
			||||||
        ctx.strokeStyle = "#00FF00"
 | 
					        ctx.strokeStyle = "#00FF00"
 | 
				
			||||||
        let b = this.getBoundary();
 | 
					        let b = this.boundary;
 | 
				
			||||||
        ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2*zoom, b.hh*2*zoom);
 | 
					        ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2*zoom, b.hh*2*zoom);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,6 @@ export default abstract class Tilemap extends GameNode {
 | 
				
			||||||
    protected tileSize: Vec2;
 | 
					    protected tileSize: Vec2;
 | 
				
			||||||
    protected scale: Vec2;
 | 
					    protected scale: Vec2;
 | 
				
			||||||
    public data: Array<number>;
 | 
					    public data: Array<number>;
 | 
				
			||||||
	public collidable: boolean;
 | 
					 | 
				
			||||||
	public visible: boolean;
 | 
						public visible: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: Make this no longer be specific to Tiled
 | 
					    // TODO: Make this no longer be specific to Tiled
 | 
				
			||||||
| 
						 | 
					@ -48,14 +47,15 @@ export default abstract class Tilemap extends GameNode {
 | 
				
			||||||
        this.scale = scale;
 | 
					        this.scale = scale;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    isCollidable(): boolean {
 | 
					 | 
				
			||||||
        return this.collidable;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isVisible(): boolean {
 | 
					    isVisible(): boolean {
 | 
				
			||||||
        return this.visible;
 | 
					        return this.visible;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Adds this tilemaps to the physics system */
 | 
				
			||||||
 | 
					    addPhysics = (): void => {
 | 
				
			||||||
 | 
					        this.scene.getPhysicsManager().registerTilemap(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract getTileAt(worldCoords: Vec2): number;
 | 
					    abstract getTileAt(worldCoords: Vec2): number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,11 +18,11 @@ export default class OrthogonalTilemap extends Tilemap {
 | 
				
			||||||
        this.tileSize.set(tilemapData.tilewidth, tilemapData.tileheight);
 | 
					        this.tileSize.set(tilemapData.tilewidth, tilemapData.tileheight);
 | 
				
			||||||
        this.data = layer.data;
 | 
					        this.data = layer.data;
 | 
				
			||||||
        this.visible = layer.visible;
 | 
					        this.visible = layer.visible;
 | 
				
			||||||
        this.collidable = false;
 | 
					        this.isCollidable = false;
 | 
				
			||||||
        if(layer.properties){
 | 
					        if(layer.properties){
 | 
				
			||||||
            for(let item of layer.properties){
 | 
					            for(let item of layer.properties){
 | 
				
			||||||
                if(item.name === "Collidable"){
 | 
					                if(item.name === "Collidable"){
 | 
				
			||||||
                    this.collidable = item.value;
 | 
					                    this.isCollidable = item.value;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,7 @@ export default class OrthogonalTilemap extends Tilemap {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO - Currently, all tiles in a collidable layer are collidable
 | 
					        // TODO - Currently, all tiles in a collidable layer are collidable
 | 
				
			||||||
        return this.data[index] !== 0 && this.collidable;
 | 
					        return this.data[index] !== 0 && this.isCollidable;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,7 +201,7 @@ export default class UIElement extends CanvasNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.lineWidth = 4;
 | 
							ctx.lineWidth = 4;
 | 
				
			||||||
        ctx.strokeStyle = "#00FF00"
 | 
					        ctx.strokeStyle = "#00FF00"
 | 
				
			||||||
        let b = this.getBoundary();
 | 
					        let b = this.boundary;
 | 
				
			||||||
        ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2, b.hh*2);
 | 
					        ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2, b.hh*2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										312
									
								
								src/Physics/BasicPhysicsManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								src/Physics/BasicPhysicsManager.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,312 @@
 | 
				
			||||||
 | 
					import { Physical } from "../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
 | 
					import Tilemap from "../Nodes/Tilemap";
 | 
				
			||||||
 | 
					import PhysicsManager from "./PhysicsManager";
 | 
				
			||||||
 | 
					import BroadPhase from "./BroadPhaseAlgorithms/BroadPhase";
 | 
				
			||||||
 | 
					import SweepAndPrune from "./BroadPhaseAlgorithms/SweepAndPrune";
 | 
				
			||||||
 | 
					import Shape from "../DataTypes/Shapes/Shape";
 | 
				
			||||||
 | 
					import MathUtils from "../Utils/MathUtils";
 | 
				
			||||||
 | 
					import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
 | 
				
			||||||
 | 
					import Debug from "../Debug/Debug";
 | 
				
			||||||
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class BasicPhysicsManager extends PhysicsManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** The array of static nodes */
 | 
				
			||||||
 | 
						protected staticNodes: Array<Physical>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** The array of dynamic nodes */
 | 
				
			||||||
 | 
						protected dynamicNodes: Array<Physical>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** The array of tilemaps */
 | 
				
			||||||
 | 
						protected tilemaps: Array<Tilemap>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** The broad phase collision detection algorithm used by this physics system */
 | 
				
			||||||
 | 
						protected broadPhase: BroadPhase;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
							this.staticNodes = new Array();
 | 
				
			||||||
 | 
							this.dynamicNodes = new Array();
 | 
				
			||||||
 | 
							this.tilemaps = new Array();
 | 
				
			||||||
 | 
							this.broadPhase = new SweepAndPrune();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Add a new physics object to be updated with the physics system
 | 
				
			||||||
 | 
						 * @param node The node to be added to the physics system
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						registerObject(node: GameNode): void {
 | 
				
			||||||
 | 
							if(node.isStatic){
 | 
				
			||||||
 | 
								// Static and not collidable
 | 
				
			||||||
 | 
								this.staticNodes.push(node);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Dynamic and not collidable
 | 
				
			||||||
 | 
								this.dynamicNodes.push(node);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.broadPhase.addNode(node);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Add a new tilemap to be updated with the physics system
 | 
				
			||||||
 | 
						 * @param tilemap The tilemap to be added to the physics system
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						registerTilemap(tilemap: Tilemap): void {
 | 
				
			||||||
 | 
							this.tilemaps.push(tilemap);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Resolves a collision between two nodes, adjusting their velocities accordingly.
 | 
				
			||||||
 | 
						 * @param node1 
 | 
				
			||||||
 | 
						 * @param node2 
 | 
				
			||||||
 | 
						 * @param firstContact 
 | 
				
			||||||
 | 
						 * @param lastContact 
 | 
				
			||||||
 | 
						 * @param collidingX 
 | 
				
			||||||
 | 
						 * @param collidingY 
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						resolveCollision(node1: Physical, node2: Physical, firstContact: Vec2, lastContact: Vec2, collidingX: boolean, collidingY: boolean): void {
 | 
				
			||||||
 | 
							// Handle collision
 | 
				
			||||||
 | 
							if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
				
			||||||
 | 
								// We are colliding. Check for any triggers
 | 
				
			||||||
 | 
								let group1 = node1.group;
 | 
				
			||||||
 | 
								let group2 = node2.group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// TODO - This is problematic if a collision happens, but it is later learned that another collision happens before it
 | 
				
			||||||
 | 
								if(node1.triggers.has(group2)){
 | 
				
			||||||
 | 
									// Node1 should send an event
 | 
				
			||||||
 | 
									let eventType = node1.triggers.get(group2);
 | 
				
			||||||
 | 
									this.emitter.fireEvent(eventType, {node: node1, other: node2});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(node2.triggers.has(group1)){
 | 
				
			||||||
 | 
									// Node2 should send an event
 | 
				
			||||||
 | 
									let eventType = node2.triggers.get(group1);
 | 
				
			||||||
 | 
									this.emitter.fireEvent(eventType, {node: node2, other: node1});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(collidingX && collidingY){
 | 
				
			||||||
 | 
									// If we're already intersecting, freak out I guess? Probably should handle this in some way for if nodes get spawned inside of tiles
 | 
				
			||||||
 | 
								} else if(node1.isCollidable && node2.isCollidable) {
 | 
				
			||||||
 | 
									// We aren't already colliding, and both nodes can collide, so this is a new collision.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Get the amount to scale x and y based on their initial collision times
 | 
				
			||||||
 | 
									let xScale = MathUtils.clamp(firstContact.x, 0, 1);
 | 
				
			||||||
 | 
									let yScale = MathUtils.clamp(firstContact.y, 0, 1);
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									// Handle special case of stickiness on perfect corner to corner collisions
 | 
				
			||||||
 | 
									if(xScale === yScale){
 | 
				
			||||||
 | 
										xScale = 1;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Handle being stopped moving in the y-direction
 | 
				
			||||||
 | 
									if(yScale !== 1){
 | 
				
			||||||
 | 
										// Figure out which node is on top
 | 
				
			||||||
 | 
										let node1onTop = node1.collisionShape.center.y < node2.collisionShape.center.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// If either is moving, set their onFloor and onCeiling appropriately
 | 
				
			||||||
 | 
										if(!node1.isStatic && node1.moving){
 | 
				
			||||||
 | 
											node1.onGround = node1onTop;
 | 
				
			||||||
 | 
											node1.onCeiling = !node1onTop;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										if(!node2.isStatic && node2.moving){
 | 
				
			||||||
 | 
											node1.onGround = !node1onTop;
 | 
				
			||||||
 | 
											node1.onCeiling = node1onTop;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Handle being stopped moving in the x-direction
 | 
				
			||||||
 | 
									if(xScale !== 1){
 | 
				
			||||||
 | 
										// If either node is non-static and moving, set its onWall to true
 | 
				
			||||||
 | 
										if(!node1.isStatic && node1.moving){
 | 
				
			||||||
 | 
											node1.onWall = true;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
										if(!node2.isStatic && node2.moving){
 | 
				
			||||||
 | 
											node2.onWall = true;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Scale velocity for either node if it is moving
 | 
				
			||||||
 | 
									if(!node1.isStatic && node1.moving){
 | 
				
			||||||
 | 
										node1._velocity.scale(xScale, yScale);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									if(!node2.isStatic && node2.moving){
 | 
				
			||||||
 | 
										node2._velocity.scale(xScale, yScale);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						collideWithTilemap(node: Physical, tilemap: Tilemap, velocity: Vec2): void {
 | 
				
			||||||
 | 
							if(tilemap instanceof OrthogonalTilemap){
 | 
				
			||||||
 | 
					            this.collideWithOrthogonalTilemap(node, tilemap, velocity);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						collideWithOrthogonalTilemap(node: Physical, tilemap: OrthogonalTilemap, velocity: Vec2): void {
 | 
				
			||||||
 | 
					        // Get the starting position, ending position, and size of the node
 | 
				
			||||||
 | 
					        let startPos = node.collisionShape.center;
 | 
				
			||||||
 | 
					        let endPos = startPos.clone().add(velocity);
 | 
				
			||||||
 | 
					        let size = node.collisionShape.halfSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the min and max x and y coordinates of the moving node
 | 
				
			||||||
 | 
					        let min = new Vec2(Math.min(startPos.x - size.x, endPos.x - size.x), Math.min(startPos.y - size.y, endPos.y - size.y));
 | 
				
			||||||
 | 
					        let max = new Vec2(Math.max(startPos.x + size.x, endPos.x + size.x), Math.max(startPos.y + size.y, endPos.y + size.y));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Convert the min/max x/y to the min and max row/col in the tilemap array
 | 
				
			||||||
 | 
					        let minIndex = tilemap.getColRowAt(min);
 | 
				
			||||||
 | 
					        let maxIndex = tilemap.getColRowAt(max);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create an empty set of tilemap collisions (We'll handle all of them at the end)
 | 
				
			||||||
 | 
					        let tilemapCollisions = new Array<TileCollisionData>();
 | 
				
			||||||
 | 
					        let tileSize = tilemap.getTileSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Debug.log("tilemapCollision", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Loop over all possible tiles (which isn't many in the scope of the velocity per frame)
 | 
				
			||||||
 | 
					        for(let col = minIndex.x; col <= maxIndex.x; col++){
 | 
				
			||||||
 | 
					            for(let row = minIndex.y; row <= maxIndex.y; row++){
 | 
				
			||||||
 | 
					                if(tilemap.isTileCollidable(col, row)){
 | 
				
			||||||
 | 
					                    Debug.log("tilemapCollision", "Colliding with Tile");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Get the position of this tile
 | 
				
			||||||
 | 
					                    let tilePos = new Vec2(col * tileSize.x + tileSize.x/2, row * tileSize.y + tileSize.y/2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Create a new collider for this tile
 | 
				
			||||||
 | 
					                    let collider = new AABB(tilePos, tileSize.scaled(1/2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Calculate collision area between the node and the tile
 | 
				
			||||||
 | 
					                    let dx = Math.min(startPos.x, tilePos.x) - Math.max(startPos.x + size.x, tilePos.x + size.x);
 | 
				
			||||||
 | 
					                    let dy = Math.min(startPos.y, tilePos.y) - Math.max(startPos.y + size.y, tilePos.y + size.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // If we overlap, how much do we overlap by?
 | 
				
			||||||
 | 
					                    let overlap = 0;
 | 
				
			||||||
 | 
					                    if(dx * dy > 0){
 | 
				
			||||||
 | 
					                        overlap = dx * dy;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    tilemapCollisions.push(new TileCollisionData(collider, overlap));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Now that we have all collisions, sort by collision area highest to lowest
 | 
				
			||||||
 | 
							tilemapCollisions = tilemapCollisions.sort((a, b) => a.overlapArea - b.overlapArea);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					        let areas = "";
 | 
				
			||||||
 | 
					        tilemapCollisions.forEach(col => areas += col.overlapArea + ", ")
 | 
				
			||||||
 | 
					        Debug.log("cols", areas)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Resolve the collisions in order of collision area (i.e. "closest" tiles are collided with first, so we can slide along a surface of tiles)
 | 
				
			||||||
 | 
					        tilemapCollisions.forEach(collision => {
 | 
				
			||||||
 | 
					            let [firstContact, _, collidingX, collidingY] = Shape.getTimeOfCollision(node.collisionShape, velocity, collision.collider, Vec2.ZERO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Handle collision
 | 
				
			||||||
 | 
					            if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
				
			||||||
 | 
					                if(collidingX && collidingY){
 | 
				
			||||||
 | 
					                    // If we're already intersecting, freak out I guess? Probably should handle this in some way for if nodes get spawned inside of tiles
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Get the amount to scale x and y based on their initial collision times
 | 
				
			||||||
 | 
					                    let xScale = MathUtils.clamp(firstContact.x, 0, 1);
 | 
				
			||||||
 | 
					                    let yScale = MathUtils.clamp(firstContact.y, 0, 1);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Handle special case of stickiness on perfect corner to corner collisions
 | 
				
			||||||
 | 
					                    if(xScale === yScale){
 | 
				
			||||||
 | 
					                        xScale = 1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if(yScale !== 1){
 | 
				
			||||||
 | 
					                        // If the tile is below us
 | 
				
			||||||
 | 
					                        if(collision.collider.y > node.collisionShape.center.y){
 | 
				
			||||||
 | 
					                            node.onGround = true;
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            node.onCeiling = true;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if(xScale !== 1){
 | 
				
			||||||
 | 
					                        node.onWall = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Scale the velocity of the node
 | 
				
			||||||
 | 
					                    velocity.scale(xScale, yScale);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						update(deltaT: number): void {
 | 
				
			||||||
 | 
							/*---------- INITIALIZATION PHASE ----------*/
 | 
				
			||||||
 | 
							for(let node of this.dynamicNodes){
 | 
				
			||||||
 | 
								// Clear frame dependent boolean values for each node
 | 
				
			||||||
 | 
								node.onGround = false;
 | 
				
			||||||
 | 
								node.onCeiling = false;
 | 
				
			||||||
 | 
								node.onWall = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Update the swept shapes of each node
 | 
				
			||||||
 | 
								if(node.moving){
 | 
				
			||||||
 | 
									// Round Velocity
 | 
				
			||||||
 | 
									node._velocity.x = Math.round(node._velocity.x*1000)/1000;
 | 
				
			||||||
 | 
									node._velocity.y = Math.round(node._velocity.y*1000)/1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// If moving, reflect that in the swept shape
 | 
				
			||||||
 | 
									node.sweptRect.sweep(node._velocity, node.collisionShape.center, node.collisionShape.halfSize);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									node.sweptRect.sweep(Vec2.ZERO_STATIC, node.collisionShape.center, node.collisionShape.halfSize);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*---------- BROAD PHASE ----------*/
 | 
				
			||||||
 | 
							// Get a potentially colliding set
 | 
				
			||||||
 | 
							let potentialCollidingPairs = this.broadPhase.runAlgorithm();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO - Should I be getting all collisions first, sorting by the time they happen, the resolving them?
 | 
				
			||||||
 | 
							/*---------- NARROW PHASE ----------*/
 | 
				
			||||||
 | 
							for(let pair of potentialCollidingPairs){
 | 
				
			||||||
 | 
								let node1 = pair[0];
 | 
				
			||||||
 | 
								let node2 = pair[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Get Collision (which may or may not happen)
 | 
				
			||||||
 | 
								let [firstContact, lastContact, collidingX, collidingY] = Shape.getTimeOfCollision(node1.collisionShape, node1._velocity, node2.collisionShape, node2._velocity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.resolveCollision(node1, node2, firstContact, lastContact, collidingX, collidingY);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*---------- TILEMAP PHASE ----------*/
 | 
				
			||||||
 | 
							for(let node of this.dynamicNodes){
 | 
				
			||||||
 | 
								if(node.moving && node.isCollidable){
 | 
				
			||||||
 | 
									// If a node is moving and can collide, check it against every tilemap
 | 
				
			||||||
 | 
									for(let tilemap of this.tilemaps){
 | 
				
			||||||
 | 
										this.collideWithTilemap(node, tilemap, node._velocity);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*---------- ENDING PHASE ----------*/
 | 
				
			||||||
 | 
							for(let node of this.dynamicNodes){
 | 
				
			||||||
 | 
								if(node.moving){
 | 
				
			||||||
 | 
									node.finishMove();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						debug_render(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Collision data objects for tilemaps
 | 
				
			||||||
 | 
					class TileCollisionData {
 | 
				
			||||||
 | 
					    collider: AABB;
 | 
				
			||||||
 | 
					    overlapArea: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(collider: AABB, overlapArea: number){
 | 
				
			||||||
 | 
					        this.collider = collider;
 | 
				
			||||||
 | 
					        this.overlapArea = overlapArea;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/Physics/BroadPhaseAlgorithms/BroadPhase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Physics/BroadPhaseAlgorithms/BroadPhase.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					import { Physical } from "../../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
 | 
					import GameNode from "../../Nodes/GameNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class BroadPhase {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Runs the algorithm and returns an array of possible collision pairs.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						abstract runAlgorithm(): Array<Physical[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						abstract addNode(node: GameNode): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										69
									
								
								src/Physics/BroadPhaseAlgorithms/SweepAndPrune.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/Physics/BroadPhaseAlgorithms/SweepAndPrune.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					import { Physical } from "../../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
 | 
					import PhysicsUtils from "../../Utils/PhysicsUtils";
 | 
				
			||||||
 | 
					import SortingUtils from "../../Utils/SortingUtils";
 | 
				
			||||||
 | 
					import BroadPhase from "./BroadPhase";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class SweepAndPrune extends BroadPhase {
 | 
				
			||||||
 | 
						protected xList: Array<Physical>;
 | 
				
			||||||
 | 
						protected yList: Array<Physical>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
							this.xList = new Array();
 | 
				
			||||||
 | 
							this.yList = new Array();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addNode(node: Physical): void {
 | 
				
			||||||
 | 
							this.xList.push(node);
 | 
				
			||||||
 | 
							this.yList.push(node);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO - Can optimize further by doing a callback whenever a swap occurs
 | 
				
			||||||
 | 
						// TODO - And by using better pair management
 | 
				
			||||||
 | 
						runAlgorithm(): Array<Physical[]> {
 | 
				
			||||||
 | 
							// Sort the xList
 | 
				
			||||||
 | 
							SortingUtils.insertionSort(this.xList, (a, b) => (a.sweptRect.left - b.sweptRect.left) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let xCollisions = [];
 | 
				
			||||||
 | 
							for(let i = 0; i < this.xList.length; i++){
 | 
				
			||||||
 | 
								let node = this.xList[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let index = 1;
 | 
				
			||||||
 | 
								while(i + index < this.xList.length && node.sweptRect.right > this.xList[i + index].sweptRect.left){
 | 
				
			||||||
 | 
									// Colliding pair in x-axis
 | 
				
			||||||
 | 
									xCollisions.push([node, this.xList[i + index]]);
 | 
				
			||||||
 | 
									index++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Sort the y-list
 | 
				
			||||||
 | 
							SortingUtils.insertionSort(this.yList, (a, b) => (a.sweptRect.top - b.sweptRect.top) );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let yCollisions = [];
 | 
				
			||||||
 | 
							for(let i = 0; i < this.yList.length; i++){
 | 
				
			||||||
 | 
								let node = this.yList[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let index = 1;
 | 
				
			||||||
 | 
								while(i + index < this.yList.length && node.sweptRect.bottom > this.yList[i + index].sweptRect.top){
 | 
				
			||||||
 | 
									// Colliding pair in y-axis
 | 
				
			||||||
 | 
									yCollisions.push([node, this.yList[i + index]]);
 | 
				
			||||||
 | 
									index++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Check the pairs
 | 
				
			||||||
 | 
							let collisions = []
 | 
				
			||||||
 | 
							for(let xPair of xCollisions){
 | 
				
			||||||
 | 
								for(let yPair of yCollisions){
 | 
				
			||||||
 | 
									if((xPair[0] === yPair[0] && xPair[1] === yPair[1])
 | 
				
			||||||
 | 
									||(xPair[0] === yPair[1] && xPair[1] === yPair[0])){
 | 
				
			||||||
 | 
										// Colliding in both axes, add to set
 | 
				
			||||||
 | 
										collisions.push(xPair);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							return collisions;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,39 +0,0 @@
 | 
				
			||||||
import AABB from "../../DataTypes/AABB";
 | 
					 | 
				
			||||||
import { Positioned } from "../../DataTypes/Interfaces/Descriptors";
 | 
					 | 
				
			||||||
import Shape from "../../DataTypes/Shape";
 | 
					 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class Collider implements Positioned {
 | 
					 | 
				
			||||||
    protected shape: Shape;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(shape: Shape){
 | 
					 | 
				
			||||||
        this.shape = shape;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setPosition(position: Vec2): void {
 | 
					 | 
				
			||||||
        this.shape.setCenter(position);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getPosition(): Vec2 {
 | 
					 | 
				
			||||||
        return this.shape.getCenter();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getBoundingRect(): AABB {
 | 
					 | 
				
			||||||
        return this.shape.getBoundingRect();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Sets the collision shape for this collider.
 | 
					 | 
				
			||||||
     * @param shape 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    setCollisionShape(shape: Shape): void {
 | 
					 | 
				
			||||||
        this.shape = shape;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Returns the collision shape this collider has
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getCollisionShape(): Shape {
 | 
					 | 
				
			||||||
        return this.shape;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,97 +0,0 @@
 | 
				
			||||||
import Shape from "../../DataTypes/Shape";
 | 
					 | 
				
			||||||
import AABB from "../../DataTypes/AABB";
 | 
					 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					 | 
				
			||||||
import Collider from "./Collider";
 | 
					 | 
				
			||||||
import Debug from "../../Debug/Debug";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getTimeOfCollision(A: Collider, velA: Vec2, B: Collider, velB: Vec2): [Vec2, Vec2, boolean, boolean]  {
 | 
					 | 
				
			||||||
    let shapeA = A.getCollisionShape();
 | 
					 | 
				
			||||||
    let shapeB = B.getCollisionShape();
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if(shapeA instanceof AABB && shapeB instanceof AABB){
 | 
					 | 
				
			||||||
        return getTimeOfCollision_AABB_AABB(shapeA, velA, shapeB, velB);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO - Make this work with centered points to avoid this initial calculation
 | 
					 | 
				
			||||||
function getTimeOfCollision_AABB_AABB(A: AABB, velA: Vec2, B: AABB, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
 | 
					 | 
				
			||||||
    let posA = A.getCenter().clone();
 | 
					 | 
				
			||||||
    let posB = B.getCenter().clone();
 | 
					 | 
				
			||||||
    let sizeA = A.getHalfSize();
 | 
					 | 
				
			||||||
    let sizeB = B.getHalfSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let firstContact = new Vec2(0, 0);
 | 
					 | 
				
			||||||
    let lastContact = new Vec2(0, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let collidingX = false;
 | 
					 | 
				
			||||||
    let collidingY = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Sort by position
 | 
					 | 
				
			||||||
    if(posB.x < posA.x){
 | 
					 | 
				
			||||||
        // Swap, because B is to the left of A
 | 
					 | 
				
			||||||
        let temp: Vec2;
 | 
					 | 
				
			||||||
        temp = sizeA;
 | 
					 | 
				
			||||||
        sizeA = sizeB;
 | 
					 | 
				
			||||||
        sizeB = temp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        temp = posA;
 | 
					 | 
				
			||||||
        posA = posB;
 | 
					 | 
				
			||||||
        posB = temp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        temp = velA;
 | 
					 | 
				
			||||||
        velA = velB;
 | 
					 | 
				
			||||||
        velB = temp;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // A is left, B is right
 | 
					 | 
				
			||||||
    firstContact.x = Infinity;
 | 
					 | 
				
			||||||
    lastContact.x = Infinity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (posB.x - sizeB.x >= posA.x + sizeA.x){
 | 
					 | 
				
			||||||
        // If we aren't currently colliding
 | 
					 | 
				
			||||||
        let relVel = velA.x - velB.x;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if(relVel > 0){
 | 
					 | 
				
			||||||
            // If they are moving towards each other
 | 
					 | 
				
			||||||
            firstContact.x = ((posB.x - sizeB.x) - (posA.x + sizeA.x))/(relVel);
 | 
					 | 
				
			||||||
            lastContact.x = ((posB.x + sizeB.x) - (posA.x - sizeA.x))/(relVel);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        collidingX = true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(posB.y < posA.y){
 | 
					 | 
				
			||||||
        // Swap, because B is above A
 | 
					 | 
				
			||||||
        let temp: Vec2;
 | 
					 | 
				
			||||||
        temp = sizeA;
 | 
					 | 
				
			||||||
        sizeA = sizeB;
 | 
					 | 
				
			||||||
        sizeB = temp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        temp = posA;
 | 
					 | 
				
			||||||
        posA = posB;
 | 
					 | 
				
			||||||
        posB = temp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        temp = velA;
 | 
					 | 
				
			||||||
        velA = velB;
 | 
					 | 
				
			||||||
        velB = temp;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // A is top, B is bottom
 | 
					 | 
				
			||||||
    firstContact.y = Infinity;
 | 
					 | 
				
			||||||
    lastContact.y = Infinity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (posB.y - sizeB.y >= posA.y + sizeA.y){
 | 
					 | 
				
			||||||
        // If we aren't currently colliding
 | 
					 | 
				
			||||||
        let relVel = velA.y - velB.y;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if(relVel > 0){
 | 
					 | 
				
			||||||
            // If they are moving towards each other
 | 
					 | 
				
			||||||
            firstContact.y = ((posB.y - sizeB.y) - (posA.y + sizeA.y))/(relVel);
 | 
					 | 
				
			||||||
            lastContact.y = ((posB.y + sizeB.y) - (posA.y - sizeA.y))/(relVel);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        collidingY = true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return [firstContact, lastContact, collidingX, collidingY];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,302 +1,24 @@
 | 
				
			||||||
import PhysicsNode from "./PhysicsNode";
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
import StaticBody from "./StaticBody";
 | 
					import { Debug_Renderable, Updateable } from "../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
import Debug from "../Debug/Debug";
 | 
					 | 
				
			||||||
import MathUtils from "../Utils/MathUtils";
 | 
					 | 
				
			||||||
import Tilemap from "../Nodes/Tilemap";
 | 
					import Tilemap from "../Nodes/Tilemap";
 | 
				
			||||||
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
 | 
					import Receiver from "../Events/Receiver";
 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					import Emitter from "../Events/Emitter";
 | 
				
			||||||
import { getTimeOfCollision } from "./Colliders/Collisions";
 | 
					 | 
				
			||||||
import Collider from "./Colliders/Collider";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PhysicsManager {
 | 
					export default abstract class PhysicsManager implements Updateable, Debug_Renderable {
 | 
				
			||||||
 | 
						protected receiver: Receiver;
 | 
				
			||||||
    private physicsNodes: Array<PhysicsNode>;
 | 
						protected emitter: Emitter;
 | 
				
			||||||
    private tilemaps: Array<Tilemap>;
 | 
					 | 
				
			||||||
    private movements: Array<MovementData>;
 | 
					 | 
				
			||||||
    private tcols: Array<TileCollisionData> = [];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(){
 | 
						constructor(){
 | 
				
			||||||
        this.physicsNodes = new Array();
 | 
							this.receiver = new Receiver();
 | 
				
			||||||
        this.tilemaps = new Array();
 | 
							this.emitter = new Emitter();
 | 
				
			||||||
        this.movements = new Array();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
						abstract registerObject(object: GameNode): void;
 | 
				
			||||||
     * Adds a PhysicsNode to the manager to be handled in case of collisions
 | 
					
 | 
				
			||||||
     * @param node 
 | 
						abstract registerTilemap(tilemap: Tilemap): void;
 | 
				
			||||||
     */
 | 
					
 | 
				
			||||||
    add(node: PhysicsNode): void {
 | 
						abstract update(deltaT: number): void;
 | 
				
			||||||
        this.physicsNodes.push(node);
 | 
					
 | 
				
			||||||
    }
 | 
						abstract debug_render(ctx: CanvasRenderingContext2D): void;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Adds a tilemap node to the manager to be handled for collisions
 | 
					 | 
				
			||||||
     * @param tilemap 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    addTilemap(tilemap: Tilemap): void {
 | 
					 | 
				
			||||||
        this.tilemaps.push(tilemap);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Adds a movement to this frame. All movements are handled at the end of the frame
 | 
					 | 
				
			||||||
     * @param node 
 | 
					 | 
				
			||||||
     * @param velocity 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    addMovement(node: PhysicsNode, velocity: Vec2): void {
 | 
					 | 
				
			||||||
        this.movements.push(new MovementData(node, velocity));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Handles a collision between a physics node and a tilemap
 | 
					 | 
				
			||||||
     * @param node 
 | 
					 | 
				
			||||||
     * @param tilemap 
 | 
					 | 
				
			||||||
     * @param velocity 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private collideWithTilemap(node: PhysicsNode, tilemap: Tilemap, velocity: Vec2): void {
 | 
					 | 
				
			||||||
        if(tilemap instanceof OrthogonalTilemap){
 | 
					 | 
				
			||||||
            this.collideWithOrthogonalTilemap(node, tilemap, velocity);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Specifically handles a collision for orthogonal tilemaps
 | 
					 | 
				
			||||||
     * @param node 
 | 
					 | 
				
			||||||
     * @param tilemap 
 | 
					 | 
				
			||||||
     * @param velocity 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private collideWithOrthogonalTilemap(node: PhysicsNode, tilemap: OrthogonalTilemap, velocity: Vec2): void {
 | 
					 | 
				
			||||||
        // Get the starting position of the moving node
 | 
					 | 
				
			||||||
        let startPos = node.getCollider().getPosition();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Get the end position of the moving node
 | 
					 | 
				
			||||||
        let endPos = startPos.clone().add(velocity);
 | 
					 | 
				
			||||||
        let size = node.getCollider().getBoundingRect().getHalfSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Get the min and max x and y coordinates of the moving node
 | 
					 | 
				
			||||||
        let min = new Vec2(Math.min(startPos.x - size.x, endPos.x - size.x), Math.min(startPos.y - size.y, endPos.y - size.y));
 | 
					 | 
				
			||||||
        let max = new Vec2(Math.max(startPos.x + size.x, endPos.x + size.x), Math.max(startPos.y + size.y, endPos.y + size.y));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Convert the min/max x/y to the min and max row/col in the tilemap array
 | 
					 | 
				
			||||||
        let minIndex = tilemap.getColRowAt(min);
 | 
					 | 
				
			||||||
        let maxIndex = tilemap.getColRowAt(max);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Create an empty set of tilemap collisions (We'll handle all of them at the end)
 | 
					 | 
				
			||||||
        let tilemapCollisions = new Array<TileCollisionData>();
 | 
					 | 
				
			||||||
        this.tcols = [];
 | 
					 | 
				
			||||||
        let tileSize = tilemap.getTileSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Debug.log("tilemapCollision", "");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Loop over all possible tiles
 | 
					 | 
				
			||||||
        for(let col = minIndex.x; col <= maxIndex.x; col++){
 | 
					 | 
				
			||||||
            for(let row = minIndex.y; row <= maxIndex.y; row++){
 | 
					 | 
				
			||||||
                if(tilemap.isTileCollidable(col, row)){
 | 
					 | 
				
			||||||
                    Debug.log("tilemapCollision", "Colliding with Tile");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Get the position of this tile
 | 
					 | 
				
			||||||
                    let tilePos = new Vec2(col * tileSize.x + tileSize.x/2, row * tileSize.y + tileSize.y/2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Create a new collider for this tile
 | 
					 | 
				
			||||||
                    let collider = new Collider(new AABB(tilePos, tileSize.scaled(1/2)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Calculate collision area between the node and the tile
 | 
					 | 
				
			||||||
                    let dx = Math.min(startPos.x, tilePos.x) - Math.max(startPos.x + size.x, tilePos.x + size.x);
 | 
					 | 
				
			||||||
                    let dy = Math.min(startPos.y, tilePos.y) - Math.max(startPos.y + size.y, tilePos.y + size.y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // If we overlap, how much do we overlap by?
 | 
					 | 
				
			||||||
                    let overlap = 0;
 | 
					 | 
				
			||||||
                    if(dx * dy > 0){
 | 
					 | 
				
			||||||
                        overlap = dx * dy;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    this.tcols.push(new TileCollisionData(collider, overlap))
 | 
					 | 
				
			||||||
                    tilemapCollisions.push(new TileCollisionData(collider, overlap));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Now that we have all collisions, sort by collision area highest to lowest
 | 
					 | 
				
			||||||
        tilemapCollisions = tilemapCollisions.sort((a, b) => a.overlapArea - b.overlapArea);
 | 
					 | 
				
			||||||
        let areas = "";
 | 
					 | 
				
			||||||
        tilemapCollisions.forEach(col => areas += col.overlapArea + ", ")
 | 
					 | 
				
			||||||
        Debug.log("cols", areas)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Resolve the collisions in order of collision area (i.e. "closest" tiles are collided with first, so we can slide along a surface of tiles)
 | 
					 | 
				
			||||||
        tilemapCollisions.forEach(collision => {
 | 
					 | 
				
			||||||
            let [firstContact, _, collidingX, collidingY] = getTimeOfCollision(node.getCollider(), velocity, collision.collider, Vec2.ZERO);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Handle collision
 | 
					 | 
				
			||||||
            if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
					 | 
				
			||||||
                if(collidingX && collidingY){
 | 
					 | 
				
			||||||
                    // If we're already intersecting, freak out I guess? Probably should handle this in some way for if nodes get spawned inside of tiles
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    // Get the amount to scale x and y based on their initial collision times
 | 
					 | 
				
			||||||
                    let xScale = MathUtils.clamp(firstContact.x, 0, 1);
 | 
					 | 
				
			||||||
                    let yScale = MathUtils.clamp(firstContact.y, 0, 1);
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    // Handle special case of stickiness on perfect corner to corner collisions
 | 
					 | 
				
			||||||
                    if(xScale === yScale){
 | 
					 | 
				
			||||||
                        xScale = 1;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // If we are scaling y, we're on the ground, so tell the node it's grounded
 | 
					 | 
				
			||||||
                    // TODO - This is a bug, check to make sure our velocity is going downwards
 | 
					 | 
				
			||||||
                    // Maybe feed in a downward direction to check to be sure
 | 
					 | 
				
			||||||
                    if(yScale !== 1){
 | 
					 | 
				
			||||||
                        // If the collider is below us
 | 
					 | 
				
			||||||
                        if(collision.collider.getPosition().y > node.position.y){
 | 
					 | 
				
			||||||
                            node.setGrounded(true);
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            console.log("On ceiling")
 | 
					 | 
				
			||||||
                            node.setOnCeiling(true);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if(xScale !== 1){
 | 
					 | 
				
			||||||
                        node.setOnWall(true);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Scale the velocity of the node
 | 
					 | 
				
			||||||
                    velocity.scale(xScale, yScale);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private collideWithStaticNode(movingNode: PhysicsNode, staticNode: PhysicsNode, velocity: Vec2){
 | 
					 | 
				
			||||||
        let [firstContact, _, collidingX, collidingY] = getTimeOfCollision(movingNode.getCollider(), velocity, staticNode.getCollider(), Vec2.ZERO);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
					 | 
				
			||||||
            if(collidingX && collidingY){
 | 
					 | 
				
			||||||
                // If we're already intersecting, freak out I guess?
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // let contactTime = Math.min(firstContact.x, firstContact.y);
 | 
					 | 
				
			||||||
                // velocity.scale(contactTime);
 | 
					 | 
				
			||||||
                let xScale = MathUtils.clamp(firstContact.x, 0, 1);
 | 
					 | 
				
			||||||
                let yScale = MathUtils.clamp(firstContact.y, 0, 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                 // Handle special case of stickiness on perfect corner to corner collisions
 | 
					 | 
				
			||||||
                 if(xScale === yScale){
 | 
					 | 
				
			||||||
                    xScale = 1;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // If we are scaling y, we're on the ground, so tell the node it's grounded
 | 
					 | 
				
			||||||
                // TODO - This is a bug, check to make sure our velocity is going downwards
 | 
					 | 
				
			||||||
                // Maybe feed in a downward direction to check to be sure
 | 
					 | 
				
			||||||
                if(yScale !== 1){
 | 
					 | 
				
			||||||
                    // If the collider is below us
 | 
					 | 
				
			||||||
                    if(staticNode.position.y > movingNode.position.y){
 | 
					 | 
				
			||||||
                        movingNode.setGrounded(true);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        movingNode.setOnCeiling(true);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if(xScale !== 1){
 | 
					 | 
				
			||||||
                    movingNode.setOnWall(true);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Scale the velocity of the node
 | 
					 | 
				
			||||||
                velocity.scale(xScale, yScale);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    update(deltaT: number): void {
 | 
					 | 
				
			||||||
        for(let node of this.physicsNodes){
 | 
					 | 
				
			||||||
            if(!node.getLayer().isPaused()){
 | 
					 | 
				
			||||||
                node.update(deltaT);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let staticSet = new Array<PhysicsNode>();
 | 
					 | 
				
			||||||
        let dynamicSet = new Array<PhysicsNode>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // TODO: REALLY bad, the physics system has to be improved, but that isn't the focus for now
 | 
					 | 
				
			||||||
        for(let node of this.physicsNodes){
 | 
					 | 
				
			||||||
            if(node.isMoving()){
 | 
					 | 
				
			||||||
                dynamicSet.push(node);
 | 
					 | 
				
			||||||
                node.setMoving(false);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                staticSet.push(node);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // For now, we will only have the moving player, don't bother checking for collisions with other moving things
 | 
					 | 
				
			||||||
        for(let movingNode of dynamicSet){
 | 
					 | 
				
			||||||
            movingNode.setGrounded(false);
 | 
					 | 
				
			||||||
            movingNode.setOnCeiling(false);
 | 
					 | 
				
			||||||
            movingNode.setOnWall(false);
 | 
					 | 
				
			||||||
            // Get velocity of node
 | 
					 | 
				
			||||||
            let velocity = null;
 | 
					 | 
				
			||||||
            for(let data of this.movements){
 | 
					 | 
				
			||||||
                if(data.node === movingNode){
 | 
					 | 
				
			||||||
                    velocity = new Vec2(data.velocity.x, data.velocity.y);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // TODO handle collisions between dynamic nodes
 | 
					 | 
				
			||||||
            // We probably want to sort them by their left edges
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for(let staticNode of staticSet){
 | 
					 | 
				
			||||||
                this.collideWithStaticNode(movingNode, staticNode, velocity);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Handle Collisions with the tilemaps
 | 
					 | 
				
			||||||
            for(let tilemap of this.tilemaps){
 | 
					 | 
				
			||||||
                this.collideWithTilemap(movingNode, tilemap, velocity);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            movingNode.finishMove(velocity);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Reset movements
 | 
					 | 
				
			||||||
        this.movements = new Array();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    render(ctx: CanvasRenderingContext2D): void {
 | 
					 | 
				
			||||||
        let vpo;
 | 
					 | 
				
			||||||
        for(let node of this.physicsNodes){
 | 
					 | 
				
			||||||
            vpo = node.getViewportOriginWithParallax();
 | 
					 | 
				
			||||||
            let pos = node.getPosition().sub(node.getViewportOriginWithParallax());
 | 
					 | 
				
			||||||
            let size = (<AABB>node.getCollider().getCollisionShape()).getHalfSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ctx.lineWidth = 2;
 | 
					 | 
				
			||||||
            ctx.strokeStyle = "#FF0000";
 | 
					 | 
				
			||||||
            ctx.strokeRect(pos.x - size.x, pos.y-size.y, size.x*2, size.y*2);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for(let node of this.tcols){
 | 
					 | 
				
			||||||
            let pos = node.collider.getPosition().sub(vpo);
 | 
					 | 
				
			||||||
            let size = node.collider.getBoundingRect().getHalfSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ctx.lineWidth = 2;
 | 
					 | 
				
			||||||
            ctx.strokeStyle = "#FF0000";
 | 
					 | 
				
			||||||
            ctx.strokeRect(pos.x - size.x, pos.y-size.y, size.x*2, size.y*2);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Helper classes for internal data
 | 
					 | 
				
			||||||
// TODO: Move these to data
 | 
					 | 
				
			||||||
// When an object moves, store it's data as MovementData so all movements can be processed at the same time at the end of the frame
 | 
					 | 
				
			||||||
class MovementData {
 | 
					 | 
				
			||||||
    node: PhysicsNode;
 | 
					 | 
				
			||||||
    velocity: Vec2;
 | 
					 | 
				
			||||||
    constructor(node: PhysicsNode, velocity: Vec2){
 | 
					 | 
				
			||||||
        this.node = node;
 | 
					 | 
				
			||||||
        this.velocity = velocity;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Collision data objects for tilemaps
 | 
					 | 
				
			||||||
class TileCollisionData {
 | 
					 | 
				
			||||||
    collider: Collider;
 | 
					 | 
				
			||||||
    overlapArea: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(collider: Collider, overlapArea: number){
 | 
					 | 
				
			||||||
        this.collider = collider;
 | 
					 | 
				
			||||||
        this.overlapArea = overlapArea;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,99 +0,0 @@
 | 
				
			||||||
import Collider from "./Colliders/Collider";
 | 
					 | 
				
			||||||
import GameNode from "../Nodes/GameNode";
 | 
					 | 
				
			||||||
import PhysicsManager from "./PhysicsManager";
 | 
					 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * The representation of a physic-affected object in the game world. Sprites and other game nodes can be associated with
 | 
					 | 
				
			||||||
 * a physics node to move them around as well.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default abstract class PhysicsNode extends GameNode {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected collider: Collider = null;
 | 
					 | 
				
			||||||
    protected children: Array<GameNode>;
 | 
					 | 
				
			||||||
    private manager: PhysicsManager;
 | 
					 | 
				
			||||||
    protected moving: boolean;
 | 
					 | 
				
			||||||
    protected grounded: boolean;
 | 
					 | 
				
			||||||
    protected onCeiling: boolean;
 | 
					 | 
				
			||||||
    protected onWall: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(){
 | 
					 | 
				
			||||||
        super();
 | 
					 | 
				
			||||||
        this.children = new Array();
 | 
					 | 
				
			||||||
        this.grounded = false;
 | 
					 | 
				
			||||||
        this.onCeiling = false;
 | 
					 | 
				
			||||||
        this.onWall = false;
 | 
					 | 
				
			||||||
        this.moving = false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setGrounded(grounded: boolean): void {
 | 
					 | 
				
			||||||
        this.grounded = grounded;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isGrounded(): boolean {
 | 
					 | 
				
			||||||
        return this.grounded;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setOnCeiling(onCeiling: boolean): void {
 | 
					 | 
				
			||||||
        this.onCeiling = onCeiling;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isOnCeiling(): boolean {
 | 
					 | 
				
			||||||
        return this.onCeiling;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setOnWall(onWall: boolean): void {
 | 
					 | 
				
			||||||
        this.onWall = onWall;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isOnWall(): boolean {
 | 
					 | 
				
			||||||
        return this.onWall;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    addManager(manager: PhysicsManager): void {
 | 
					 | 
				
			||||||
        this.manager = manager;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    addChild(child: GameNode): void {
 | 
					 | 
				
			||||||
        this.children.push(child);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isCollidable(): boolean {
 | 
					 | 
				
			||||||
        return this.collider !== null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getCollider(): Collider {
 | 
					 | 
				
			||||||
        return this.collider;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    setMoving(moving: boolean): void {
 | 
					 | 
				
			||||||
        this.moving = moving;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isMoving(): boolean {
 | 
					 | 
				
			||||||
        return this.moving;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Register a movement to the physics manager that can be handled at the end of the frame
 | 
					 | 
				
			||||||
     * @param velocity 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    move(velocity: Vec2): void {
 | 
					 | 
				
			||||||
        this.moving = true;
 | 
					 | 
				
			||||||
        this.manager.addMovement(this, velocity);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Called by the physics manager to finish the movement and actually move the physics object and its children
 | 
					 | 
				
			||||||
     * @param velocity 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    finishMove(velocity: Vec2): void {
 | 
					 | 
				
			||||||
        this.position.add(velocity);
 | 
					 | 
				
			||||||
        this.collider.getPosition().add(velocity);
 | 
					 | 
				
			||||||
        for(let child of this.children){
 | 
					 | 
				
			||||||
            child.position.add(velocity);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    abstract create(): void;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,20 +0,0 @@
 | 
				
			||||||
import PhysicsNode from "./PhysicsNode";
 | 
					 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					 | 
				
			||||||
import Collider from "./Colliders/Collider";
 | 
					 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class StaticBody extends PhysicsNode {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(position: Vec2, size: Vec2){
 | 
					 | 
				
			||||||
        super();
 | 
					 | 
				
			||||||
        this.setPosition(position.x, position.y);
 | 
					 | 
				
			||||||
        let aabb = new AABB(position.clone(), size.scaled(1/2));
 | 
					 | 
				
			||||||
        this.collider = new Collider(aabb);
 | 
					 | 
				
			||||||
        this.moving = false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    create(): void {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    update(deltaT: number): void {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										103
									
								
								src/Player.ts
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								src/Player.ts
									
									
									
									
									
								
							| 
						 | 
					@ -1,103 +0,0 @@
 | 
				
			||||||
import PhysicsNode from "./Physics/PhysicsNode";
 | 
					 | 
				
			||||||
import Vec2 from "./DataTypes/Vec2";
 | 
					 | 
				
			||||||
import Debug from "./Debug/Debug";
 | 
					 | 
				
			||||||
import CanvasNode from "./Nodes/CanvasNode";
 | 
					 | 
				
			||||||
import { GameEventType } from "./Events/GameEventType";
 | 
					 | 
				
			||||||
import AABB from "./DataTypes/AABB";
 | 
					 | 
				
			||||||
import Collider from "./Physics/Colliders/Collider";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class Player extends PhysicsNode {
 | 
					 | 
				
			||||||
	velocity: Vec2;
 | 
					 | 
				
			||||||
	speed: number;
 | 
					 | 
				
			||||||
    debug: Debug;
 | 
					 | 
				
			||||||
    size: Vec2;
 | 
					 | 
				
			||||||
    gravity: number = 7000;
 | 
					 | 
				
			||||||
    type: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(type: string){
 | 
					 | 
				
			||||||
        super();
 | 
					 | 
				
			||||||
        this.type = type;
 | 
					 | 
				
			||||||
        this.velocity = new Vec2(0, 0);
 | 
					 | 
				
			||||||
        this.speed = 600;
 | 
					 | 
				
			||||||
        this.size = new Vec2(50, 50);
 | 
					 | 
				
			||||||
        this.position = new Vec2(0, 0);
 | 
					 | 
				
			||||||
        if(this.type === "topdown"){
 | 
					 | 
				
			||||||
            this.position = new Vec2(100, 100);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.collider = new Collider(new AABB(this.position.clone(), this.size.scaled(1/2)));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    create(): void {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sprite: CanvasNode;
 | 
					 | 
				
			||||||
    setSprite(sprite: CanvasNode): void {
 | 
					 | 
				
			||||||
        this.sprite = sprite;
 | 
					 | 
				
			||||||
        sprite.position = this.position.clone();
 | 
					 | 
				
			||||||
        sprite.setSize(this.size);
 | 
					 | 
				
			||||||
        this.children.push(sprite);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    update(deltaT: number): void {
 | 
					 | 
				
			||||||
        if(this.type === "topdown"){
 | 
					 | 
				
			||||||
            let dir = this.topdown_computeDirection();
 | 
					 | 
				
			||||||
            this.velocity = this.topdown_computeVelocity(dir, deltaT);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            let dir = this.platformer_computeDirection();
 | 
					 | 
				
			||||||
            this.velocity = this.platformer_computeVelocity(dir, deltaT);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.move(new Vec2(this.velocity.x * deltaT, this.velocity.y * deltaT));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Debug.log("player", "Pos: " + this.sprite.getPosition() + ", Size: " + this.sprite.getSize());
 | 
					 | 
				
			||||||
        Debug.log("playerbound", "Pos: " + this.sprite.getBoundary().getCenter() + ", Size: " + this.sprite.getBoundary().getHalfSize());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    topdown_computeDirection(): Vec2 {
 | 
					 | 
				
			||||||
        let dir = new Vec2(0, 0);
 | 
					 | 
				
			||||||
		dir.x += this.input.isPressed('a') ? -1 : 0;
 | 
					 | 
				
			||||||
        dir.x += this.input.isPressed('d') ? 1 : 0;
 | 
					 | 
				
			||||||
        dir.y += this.input.isPressed('w') ? -1 : 0;
 | 
					 | 
				
			||||||
        dir.y += this.input.isPressed('s') ? 1 : 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dir.normalize();        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return dir;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    topdown_computeVelocity(dir: Vec2, deltaT: number): Vec2 {
 | 
					 | 
				
			||||||
        let vel = new Vec2(dir.x * this.speed, dir.y * this.speed);
 | 
					 | 
				
			||||||
        return vel
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    platformer_computeDirection(): Vec2 {
 | 
					 | 
				
			||||||
        let dir = new Vec2(0, 0);
 | 
					 | 
				
			||||||
		dir.x += this.input.isPressed('a') ? -1 : 0;
 | 
					 | 
				
			||||||
        dir.x += this.input.isPressed('d') ? 1 : 0;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if(this.grounded){
 | 
					 | 
				
			||||||
            dir.y += this.input.isJustPressed('w') ? -1 : 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return dir;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    platformer_computeVelocity(dir: Vec2, deltaT: number): Vec2 {
 | 
					 | 
				
			||||||
        let vel = new Vec2(0, this.velocity.y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if(this.grounded){
 | 
					 | 
				
			||||||
            if(dir.y === -1){
 | 
					 | 
				
			||||||
                // Jumping
 | 
					 | 
				
			||||||
                this.emitter.fireEvent(GameEventType.PLAY_SOUND, {key: "player_jump"});
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            vel.y = dir.y*1800;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        vel.y += this.gravity * deltaT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        vel.x = dir.x * this.speed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return vel
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
import Scene from "./Scene/Scene";
 | 
					import Scene from "./Scene/Scene";
 | 
				
			||||||
import { GameEventType } from "./Events/GameEventType"
 | 
					 | 
				
			||||||
import Point from "./Nodes/Graphics/Point";
 | 
					import Point from "./Nodes/Graphics/Point";
 | 
				
			||||||
import Rect from "./Nodes/Graphics/Rect";
 | 
					import Rect from "./Nodes/Graphics/Rect";
 | 
				
			||||||
import Layer from "./Scene/Layer";
 | 
					import Layer from "./Scene/Layer";
 | 
				
			||||||
| 
						 | 
					@ -7,9 +6,6 @@ import SceneGraphQuadTree from "./SceneGraph/SceneGraphQuadTree"
 | 
				
			||||||
import Vec2 from "./DataTypes/Vec2";
 | 
					import Vec2 from "./DataTypes/Vec2";
 | 
				
			||||||
import InputReceiver from "./Input/InputReceiver";
 | 
					import InputReceiver from "./Input/InputReceiver";
 | 
				
			||||||
import Color from "./Utils/Color";
 | 
					import Color from "./Utils/Color";
 | 
				
			||||||
import CanvasNode from "./Nodes/CanvasNode";
 | 
					 | 
				
			||||||
import Graphic from "./Nodes/Graphic";
 | 
					 | 
				
			||||||
import RandUtils from "./Utils/RandUtils";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class QuadTreeScene extends Scene {
 | 
					export default class QuadTreeScene extends Scene {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,14 +37,14 @@ export default class QuadTreeScene extends Scene {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateScene(deltaT: number): void {
 | 
					    updateScene(deltaT: number): void {
 | 
				
			||||||
        this.view.setPosition(InputReceiver.getInstance().getGlobalMousePosition());
 | 
					        this.view.position.copy(InputReceiver.getInstance().getGlobalMousePosition());
 | 
				
			||||||
        for(let point of this.points){
 | 
					        for(let point of this.points){
 | 
				
			||||||
            point.setColor(Color.RED);
 | 
					            point.setColor(Color.RED);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            point.position.add(Vec2.UP.rotateCCW(Math.random()*2*Math.PI).add(point.position.vecTo(this.view.position).normalize().scale(0.1)));
 | 
					            point.position.add(Vec2.UP.rotateCCW(Math.random()*2*Math.PI).add(point.position.vecTo(this.view.position).normalize().scale(0.1)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let results = this.sceneGraph.getNodesInRegion(this.view.getBoundary());
 | 
					        let results = this.sceneGraph.getNodesInRegion(this.view.boundary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for(let result of results){
 | 
					        for(let result of results){
 | 
				
			||||||
            if(result instanceof Point){
 | 
					            if(result instanceof Point){
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ export default class CanvasNodeFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add instance to scene
 | 
							// Add instance to scene
 | 
				
			||||||
		instance.setScene(this.scene);
 | 
							instance.setScene(this.scene);
 | 
				
			||||||
		instance.setId(this.scene.generateId());
 | 
							instance.id = this.scene.generateId();
 | 
				
			||||||
		this.scene.getSceneGraph().addNode(instance);
 | 
							this.scene.getSceneGraph().addNode(instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add instance to layer
 | 
							// Add instance to layer
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ export default class CanvasNodeFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add instance to scene
 | 
							// Add instance to scene
 | 
				
			||||||
		instance.setScene(this.scene);
 | 
							instance.setScene(this.scene);
 | 
				
			||||||
		instance.setId(this.scene.generateId());
 | 
							instance.id = this.scene.generateId();
 | 
				
			||||||
		this.scene.getSceneGraph().addNode(instance);
 | 
							this.scene.getSceneGraph().addNode(instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add instance to layer
 | 
							// Add instance to layer
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,7 @@ export default class CanvasNodeFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add instance to scene
 | 
							// Add instance to scene
 | 
				
			||||||
		instance.setScene(this.scene);
 | 
							instance.setScene(this.scene);
 | 
				
			||||||
		instance.setId(this.scene.generateId());
 | 
							instance.id = this.scene.generateId();
 | 
				
			||||||
		this.scene.getSceneGraph().addNode(instance);
 | 
							this.scene.getSceneGraph().addNode(instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add instance to layer
 | 
							// Add instance to layer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,17 @@
 | 
				
			||||||
import Scene from "../Scene";
 | 
					import Scene from "../Scene";
 | 
				
			||||||
import PhysicsNodeFactory from "./PhysicsNodeFactory";
 | 
					 | 
				
			||||||
import CanvasNodeFactory from "./CanvasNodeFactory";
 | 
					import CanvasNodeFactory from "./CanvasNodeFactory";
 | 
				
			||||||
import TilemapFactory from "./TilemapFactory";
 | 
					import TilemapFactory from "./TilemapFactory";
 | 
				
			||||||
import PhysicsManager from "../../Physics/PhysicsManager";
 | 
					import PhysicsManager from "../../Physics/PhysicsManager";
 | 
				
			||||||
import SceneGraph from "../../SceneGraph/SceneGraph";
 | 
					 | 
				
			||||||
import Tilemap from "../../Nodes/Tilemap";
 | 
					import Tilemap from "../../Nodes/Tilemap";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class FactoryManager {
 | 
					export default class FactoryManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Constructors are called here to allow assignment of their functions to functions in this class
 | 
					    // Constructors are called here to allow assignment of their functions to functions in this class
 | 
				
			||||||
    private canvasNodeFactory: CanvasNodeFactory = new CanvasNodeFactory();
 | 
					    private canvasNodeFactory: CanvasNodeFactory = new CanvasNodeFactory();
 | 
				
			||||||
    private physicsNodeFactory: PhysicsNodeFactory = new PhysicsNodeFactory();
 | 
					 | 
				
			||||||
    private tilemapFactory: TilemapFactory = new TilemapFactory();
 | 
					    private tilemapFactory: TilemapFactory = new TilemapFactory();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(scene: Scene, physicsManager: PhysicsManager, tilemaps: Array<Tilemap>){
 | 
					    constructor(scene: Scene, physicsManager: PhysicsManager, tilemaps: Array<Tilemap>){
 | 
				
			||||||
        this.canvasNodeFactory.init(scene);
 | 
					        this.canvasNodeFactory.init(scene);
 | 
				
			||||||
        this.physicsNodeFactory.init(scene, physicsManager);
 | 
					 | 
				
			||||||
        this.tilemapFactory.init(scene, tilemaps, physicsManager);
 | 
					        this.tilemapFactory.init(scene, tilemaps, physicsManager);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +19,5 @@ export default class FactoryManager {
 | 
				
			||||||
    uiElement = this.canvasNodeFactory.addUIElement;
 | 
					    uiElement = this.canvasNodeFactory.addUIElement;
 | 
				
			||||||
    sprite = this.canvasNodeFactory.addSprite;
 | 
					    sprite = this.canvasNodeFactory.addSprite;
 | 
				
			||||||
    graphic = this.canvasNodeFactory.addGraphic;
 | 
					    graphic = this.canvasNodeFactory.addGraphic;
 | 
				
			||||||
    physics = this.physicsNodeFactory.add;
 | 
					 | 
				
			||||||
    tilemap = this.tilemapFactory.add;
 | 
					    tilemap = this.tilemapFactory.add;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,34 +0,0 @@
 | 
				
			||||||
import Scene from "../Scene";
 | 
					 | 
				
			||||||
import PhysicsNode from "../../Physics/PhysicsNode";
 | 
					 | 
				
			||||||
import PhysicsManager from "../../Physics/PhysicsManager";
 | 
					 | 
				
			||||||
import Layer from "../Layer";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class PhysicsNodeFactory {
 | 
					 | 
				
			||||||
	private scene: Scene;
 | 
					 | 
				
			||||||
	private physicsManager: PhysicsManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	init(scene: Scene, physicsManager: PhysicsManager): void {
 | 
					 | 
				
			||||||
		this.scene = scene;
 | 
					 | 
				
			||||||
        this.physicsManager = physicsManager;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO: Currently this doesn't care about layers
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Adds a new PhysicsNode to the scene on the specified Layer
 | 
					 | 
				
			||||||
	 * @param constr The constructor of the PhysicsNode to be added to the scene
 | 
					 | 
				
			||||||
	 * @param layer The layer on which to add the PhysicsNode
 | 
					 | 
				
			||||||
	 * @param args Any additional arguments to send to the PhysicsNode constructor
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	add = <T extends PhysicsNode>(constr: new (...a: any) => T, layer: Layer, ...args: any): T => {
 | 
					 | 
				
			||||||
		let instance = new constr(...args);
 | 
					 | 
				
			||||||
		instance.setScene(this.scene);
 | 
					 | 
				
			||||||
		instance.setId(this.scene.generateId());
 | 
					 | 
				
			||||||
        instance.addManager(this.physicsManager);
 | 
					 | 
				
			||||||
		instance.create();
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		layer.addNode(instance);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.physicsManager.add(instance);
 | 
					 | 
				
			||||||
		return instance;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@ import Tileset from "../../DataTypes/Tilesets/Tileset";
 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
import { TiledCollectionTile } from "../../DataTypes/Tilesets/TiledData";
 | 
					import { TiledCollectionTile } from "../../DataTypes/Tilesets/TiledData";
 | 
				
			||||||
import Sprite from "../../Nodes/Sprites/Sprite";
 | 
					import Sprite from "../../Nodes/Sprites/Sprite";
 | 
				
			||||||
import StaticBody from "../../Physics/StaticBody";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class TilemapFactory {
 | 
					export default class TilemapFactory {
 | 
				
			||||||
    private scene: Scene;
 | 
					    private scene: Scene;
 | 
				
			||||||
| 
						 | 
					@ -71,7 +70,7 @@ export default class TilemapFactory {
 | 
				
			||||||
            if(layer.type === "tilelayer"){
 | 
					            if(layer.type === "tilelayer"){
 | 
				
			||||||
                // Create a new tilemap object for the layer
 | 
					                // Create a new tilemap object for the layer
 | 
				
			||||||
                let tilemap = new constr(tilemapData, layer, tilesets, scale);
 | 
					                let tilemap = new constr(tilemapData, layer, tilesets, scale);
 | 
				
			||||||
                tilemap.setId(this.scene.generateId());
 | 
					                tilemap.id = this.scene.generateId();
 | 
				
			||||||
                tilemap.setScene(this.scene);
 | 
					                tilemap.setScene(this.scene);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
                // Add tilemap to scene
 | 
					                // Add tilemap to scene
 | 
				
			||||||
| 
						 | 
					@ -80,8 +79,8 @@ export default class TilemapFactory {
 | 
				
			||||||
                sceneLayer.addNode(tilemap);
 | 
					                sceneLayer.addNode(tilemap);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
                // Register tilemap with physics if it's collidable
 | 
					                // Register tilemap with physics if it's collidable
 | 
				
			||||||
                if(tilemap.isCollidable()){
 | 
					                if(tilemap.isCollidable){
 | 
				
			||||||
                    this.physicsManager.addTilemap(tilemap);
 | 
					                    tilemap.addPhysics();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // Layer is an object layer, so add each object as a sprite to a new layer
 | 
					                // Layer is an object layer, so add each object as a sprite to a new layer
 | 
				
			||||||
| 
						 | 
					@ -107,10 +106,10 @@ export default class TilemapFactory {
 | 
				
			||||||
                            let offset = tileset.getImageOffsetForTile(obj.gid);
 | 
					                            let offset = tileset.getImageOffsetForTile(obj.gid);
 | 
				
			||||||
                            sprite = this.scene.add.sprite(imageKey, sceneLayer);
 | 
					                            sprite = this.scene.add.sprite(imageKey, sceneLayer);
 | 
				
			||||||
                            let size = tileset.getTileSize().clone();
 | 
					                            let size = tileset.getTileSize().clone();
 | 
				
			||||||
                            sprite.setPosition((obj.x + size.x/2)*scale.x, (obj.y - size.y/2)*scale.y);
 | 
					                            sprite.position.set((obj.x + size.x/2)*scale.x, (obj.y - size.y/2)*scale.y);
 | 
				
			||||||
                            sprite.setImageOffset(offset);
 | 
					                            sprite.setImageOffset(offset);
 | 
				
			||||||
                            sprite.setSize(size);
 | 
					                            sprite.size.copy(size);
 | 
				
			||||||
                            sprite.setScale(new Vec2(scale.x, scale.y));
 | 
					                            sprite.scale.set(scale.x, scale.y);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,22 +119,16 @@ export default class TilemapFactory {
 | 
				
			||||||
                            if(obj.gid === tile.id){
 | 
					                            if(obj.gid === tile.id){
 | 
				
			||||||
                                let imageKey = tile.image;
 | 
					                                let imageKey = tile.image;
 | 
				
			||||||
                                sprite = this.scene.add.sprite(imageKey, sceneLayer);
 | 
					                                sprite = this.scene.add.sprite(imageKey, sceneLayer);
 | 
				
			||||||
                                sprite.setPosition((obj.x + tile.imagewidth/2)*scale.x, (obj.y - tile.imageheight/2)*scale.y);
 | 
					                                sprite.position.set((obj.x + tile.imagewidth/2)*scale.x, (obj.y - tile.imageheight/2)*scale.y);
 | 
				
			||||||
                                sprite.setScale(new Vec2(scale.x, scale.y));
 | 
					                                sprite.scale.set(scale.x, scale.y);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Now we have sprite. Associate it with our physics object if there is one
 | 
					                    // Now we have sprite. Associate it with our physics object if there is one
 | 
				
			||||||
                    if(collidable){
 | 
					                    if(collidable){
 | 
				
			||||||
                        let pos = sprite.getPosition().clone();
 | 
					                        sprite.addPhysics();
 | 
				
			||||||
                        let size = sprite.getSize().clone().mult(sprite.getScale());
 | 
					 | 
				
			||||||
                        pos.x = Math.floor(pos.x);
 | 
					 | 
				
			||||||
                        pos.y = Math.floor(pos.y);
 | 
					 | 
				
			||||||
                        let staticBody = this.scene.add.physics(StaticBody, sceneLayer, pos, size);
 | 
					 | 
				
			||||||
                        staticBody.addChild(sprite);
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
import Stack from "../DataTypes/Stack";
 | 
					 | 
				
			||||||
import Layer from "./Layer";
 | 
					import Layer from "./Layer";
 | 
				
			||||||
import Viewport from "../SceneGraph/Viewport";
 | 
					import Viewport from "../SceneGraph/Viewport";
 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
import SceneGraph from "../SceneGraph/SceneGraph";
 | 
					import SceneGraph from "../SceneGraph/SceneGraph";
 | 
				
			||||||
import PhysicsManager from "../Physics/PhysicsManager";
 | 
					import PhysicsManager from "../Physics/PhysicsManager";
 | 
				
			||||||
 | 
					import BasicPhysicsManager from "../Physics/BasicPhysicsManager";
 | 
				
			||||||
import SceneGraphArray from "../SceneGraph/SceneGraphArray";
 | 
					import SceneGraphArray from "../SceneGraph/SceneGraphArray";
 | 
				
			||||||
import FactoryManager from "./Factories/FactoryManager";
 | 
					import FactoryManager from "./Factories/FactoryManager";
 | 
				
			||||||
import Tilemap from "../Nodes/Tilemap";
 | 
					import Tilemap from "../Nodes/Tilemap";
 | 
				
			||||||
| 
						 | 
					@ -12,32 +12,41 @@ import GameLoop from "../Loop/GameLoop";
 | 
				
			||||||
import SceneManager from "./SceneManager";
 | 
					import SceneManager from "./SceneManager";
 | 
				
			||||||
import Receiver from "../Events/Receiver";
 | 
					import Receiver from "../Events/Receiver";
 | 
				
			||||||
import Emitter from "../Events/Emitter";
 | 
					import Emitter from "../Events/Emitter";
 | 
				
			||||||
 | 
					import { Renderable, Updateable } from "../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Scene{
 | 
					export default class Scene implements Updateable, Renderable {
 | 
				
			||||||
 | 
					    /** The size of the game world. */
 | 
				
			||||||
    protected worldSize: Vec2;
 | 
					    protected worldSize: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The viewport. */
 | 
				
			||||||
    protected viewport: Viewport;
 | 
					    protected viewport: Viewport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** A flag that represents whether this scene is running or not. */
 | 
				
			||||||
    protected running: boolean;
 | 
					    protected running: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The overall game loop. */
 | 
				
			||||||
    protected game: GameLoop;
 | 
					    protected game: GameLoop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The manager of this scene. */
 | 
				
			||||||
    protected sceneManager: SceneManager;
 | 
					    protected sceneManager: SceneManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The receiver for this scene. */
 | 
				
			||||||
    protected receiver: Receiver;
 | 
					    protected receiver: Receiver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The emitter for this scene. */
 | 
				
			||||||
    protected emitter: Emitter;
 | 
					    protected emitter: Emitter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** This list of tilemaps in this scene. */
 | 
				
			||||||
    protected tilemaps: Array<Tilemap>;
 | 
					    protected tilemaps: Array<Tilemap>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /** The scene graph of the Scene*/
 | 
				
			||||||
     * The scene graph of the Scene - can be exchanged with other SceneGraphs for more variation
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected sceneGraph: SceneGraph;
 | 
					    protected sceneGraph: SceneGraph;
 | 
				
			||||||
    protected physicsManager: PhysicsManager;
 | 
					    protected physicsManager: PhysicsManager;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /**
 | 
					    /** An interface that allows the adding of different nodes to the scene */
 | 
				
			||||||
    * An interface that allows the adding of different nodes to the scene
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
    public add: FactoryManager;
 | 
					    public add: FactoryManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /** An interface that allows the loading of different files for use in the scene */
 | 
				
			||||||
     * An interface that allows the loading of different files for use in the scene
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public load: ResourceManager;
 | 
					    public load: ResourceManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(viewport: Viewport, sceneManager: SceneManager, game: GameLoop){
 | 
					    constructor(viewport: Viewport, sceneManager: SceneManager, game: GameLoop){
 | 
				
			||||||
| 
						 | 
					@ -52,40 +61,28 @@ export default class Scene{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.tilemaps = new Array();
 | 
					        this.tilemaps = new Array();
 | 
				
			||||||
        this.sceneGraph = new SceneGraphArray(this.viewport, this);
 | 
					        this.sceneGraph = new SceneGraphArray(this.viewport, this);
 | 
				
			||||||
        this.physicsManager = new PhysicsManager();
 | 
					        this.physicsManager = new BasicPhysicsManager();
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.add = new FactoryManager(this, this.physicsManager, this.tilemaps);
 | 
					        this.add = new FactoryManager(this, this.physicsManager, this.tilemaps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.load = ResourceManager.getInstance();
 | 
					        this.load = ResourceManager.getInstance();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /** A lifecycle method that gets called when a new scene is created. Load all files you wish to access in the scene here. */
 | 
				
			||||||
     * A function that gets called when a new scene is created. Load all files you wish to access in the scene here.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    loadScene(): void {}
 | 
					    loadScene(): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /** A lifecycle method that gets called on scene destruction. Specify which files you no longer need for garbage collection. */
 | 
				
			||||||
     * A function that gets called on scene destruction. Specify which files you no longer need for garbage collection.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    unloadScene(): void {}
 | 
					    unloadScene(): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /** A lifecycle method called strictly after loadScene(). Create any game objects you wish to use in the scene here. */
 | 
				
			||||||
     * Called strictly after loadScene() is called. Create any game objects you wish to use in the scene here.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    startScene(): void {}
 | 
					    startScene(): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Called every frame of the game. This is where you can dynamically do things like add in new enemies
 | 
					     * A lifecycle method called every frame of the game. This is where you can dynamically do things like add in new enemies
 | 
				
			||||||
     * @param delta 
 | 
					     * @param delta 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    updateScene(deltaT: number): void {}
 | 
					    updateScene(deltaT: number): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Updates all scene elements
 | 
					 | 
				
			||||||
     * @param deltaT 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    update(deltaT: number): void {
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
        this.updateScene(deltaT);
 | 
					        this.updateScene(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,10 +103,6 @@ export default class Scene{
 | 
				
			||||||
        this.viewport.update(deltaT);
 | 
					        this.viewport.update(deltaT);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Render all CanvasNodes and Tilemaps in the Scene
 | 
					 | 
				
			||||||
     * @param ctx 
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    render(ctx: CanvasRenderingContext2D): void {
 | 
					    render(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
        // For webGL, pass a visible set to the renderer
 | 
					        // For webGL, pass a visible set to the renderer
 | 
				
			||||||
        // We need to keep track of the order of things.
 | 
					        // We need to keep track of the order of things.
 | 
				
			||||||
| 
						 | 
					@ -127,7 +120,7 @@ export default class Scene{
 | 
				
			||||||
        visibleSet.forEach(node => node.render(ctx));
 | 
					        visibleSet.forEach(node => node.render(ctx));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Debug render the physicsManager
 | 
					        // Debug render the physicsManager
 | 
				
			||||||
        this.physicsManager.render(ctx);
 | 
					        this.physicsManager.debug_render(ctx);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setRunning(running: boolean): void {
 | 
					    setRunning(running: boolean): void {
 | 
				
			||||||
| 
						 | 
					@ -145,9 +138,7 @@ export default class Scene{
 | 
				
			||||||
        return this.sceneGraph.addLayer();
 | 
					        return this.sceneGraph.addLayer();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /** Returns the viewport associated with this scene */
 | 
				
			||||||
     * Returns the viewport associated with this scene
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    getViewport(): Viewport {
 | 
					    getViewport(): Viewport {
 | 
				
			||||||
        return this.viewport;
 | 
					        return this.viewport;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -160,6 +151,10 @@ export default class Scene{
 | 
				
			||||||
        return this.sceneGraph;
 | 
					        return this.sceneGraph;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPhysicsManager(): PhysicsManager {
 | 
				
			||||||
 | 
					        return this.physicsManager;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    generateId(): number {
 | 
					    generateId(): number {
 | 
				
			||||||
        return this.sceneManager.generateId();
 | 
					        return this.sceneManager.generateId();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
import Scene from "../Scene/Scene";
 | 
					import Scene from "../Scene/Scene";
 | 
				
			||||||
import Layer from "../Scene/Layer";
 | 
					import Layer from "../Scene/Layer";
 | 
				
			||||||
import Stack from "../DataTypes/Stack";
 | 
					import Stack from "../DataTypes/Stack";
 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * An abstract interface of a SceneGraph. Exposes methods for use by other code, but leaves the implementation up to the subclasses.
 | 
					 * An abstract interface of a SceneGraph. Exposes methods for use by other code, but leaves the implementation up to the subclasses.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import Viewport from "./Viewport";
 | 
				
			||||||
import Scene from "../Scene/Scene";
 | 
					import Scene from "../Scene/Scene";
 | 
				
			||||||
import Stack from "../DataTypes/Stack";
 | 
					import Stack from "../DataTypes/Stack";
 | 
				
			||||||
import Layer from "../Scene/Layer"
 | 
					import Layer from "../Scene/Layer"
 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
import Stats from "../Debug/Stats";
 | 
					import Stats from "../Debug/Stats";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SceneGraphArray extends SceneGraph{
 | 
					export default class SceneGraphArray extends SceneGraph{
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,7 @@ export default class SceneGraphArray extends SceneGraph{
 | 
				
			||||||
        let results = [];
 | 
					        let results = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for(let node of this.nodeList){
 | 
					        for(let node of this.nodeList){
 | 
				
			||||||
            if(boundary.overlaps(node.getBoundary())){
 | 
					            if(boundary.overlaps(node.boundary)){
 | 
				
			||||||
                results.push(node);
 | 
					                results.push(node);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -94,8 +94,7 @@ export default class SceneGraphArray extends SceneGraph{
 | 
				
			||||||
        // Sort by depth, then by visible set by y-value
 | 
					        // Sort by depth, then by visible set by y-value
 | 
				
			||||||
        visibleSet.sort((a, b) => {
 | 
					        visibleSet.sort((a, b) => {
 | 
				
			||||||
            if(a.getLayer().getDepth() === b.getLayer().getDepth()){
 | 
					            if(a.getLayer().getDepth() === b.getLayer().getDepth()){
 | 
				
			||||||
                return (a.getPosition().y + a.getSize().y*a.getScale().y)
 | 
					                return (a.boundary.bottom) - (b.boundary.bottom);
 | 
				
			||||||
                - (b.getPosition().y + b.getSize().y*b.getScale().y);
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                return a.getLayer().getDepth() - b.getLayer().getDepth();
 | 
					                return a.getLayer().getDepth() - b.getLayer().getDepth();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import Viewport from "./Viewport";
 | 
				
			||||||
import Scene from "../Scene/Scene";
 | 
					import Scene from "../Scene/Scene";
 | 
				
			||||||
import RegionQuadTree from "../DataTypes/RegionQuadTree";
 | 
					import RegionQuadTree from "../DataTypes/RegionQuadTree";
 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
import Stats from "../Debug/Stats";
 | 
					import Stats from "../Debug/Stats";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SceneGraphQuadTree extends SceneGraph {
 | 
					export default class SceneGraphQuadTree extends SceneGraph {
 | 
				
			||||||
| 
						 | 
					@ -80,8 +80,7 @@ export default class SceneGraphQuadTree extends SceneGraph {
 | 
				
			||||||
        // Sort by depth, then by visible set by y-value
 | 
					        // Sort by depth, then by visible set by y-value
 | 
				
			||||||
        visibleSet.sort((a, b) => {
 | 
					        visibleSet.sort((a, b) => {
 | 
				
			||||||
            if(a.getLayer().getDepth() === b.getLayer().getDepth()){
 | 
					            if(a.getLayer().getDepth() === b.getLayer().getDepth()){
 | 
				
			||||||
                return (a.getPosition().y + a.getSize().y*a.getScale().y)
 | 
					                return (a.boundary.bottom) - (b.boundary.bottom);
 | 
				
			||||||
                - (b.getPosition().y + b.getSize().y*b.getScale().y);
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                return a.getLayer().getDepth() - b.getLayer().getDepth();
 | 
					                return a.getLayer().getDepth() - b.getLayer().getDepth();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
import CanvasNode from "../Nodes/CanvasNode";
 | 
					import CanvasNode from "../Nodes/CanvasNode";
 | 
				
			||||||
import MathUtils from "../Utils/MathUtils";
 | 
					import MathUtils from "../Utils/MathUtils";
 | 
				
			||||||
import Queue from "../DataTypes/Queue";
 | 
					import Queue from "../DataTypes/Queue";
 | 
				
			||||||
import AABB from "../DataTypes/AABB";
 | 
					import AABB from "../DataTypes/Shapes/AABB";
 | 
				
			||||||
import Debug from "../Debug/Debug";
 | 
					import Debug from "../Debug/Debug";
 | 
				
			||||||
import InputReceiver from "../Input/InputReceiver";
 | 
					import InputReceiver from "../Input/InputReceiver";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,11 +43,11 @@ export default class Viewport {
 | 
				
			||||||
     * Returns the position of the viewport as a Vec2
 | 
					     * Returns the position of the viewport as a Vec2
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getCenter(): Vec2 {
 | 
					    getCenter(): Vec2 {
 | 
				
			||||||
        return this.view.getCenter();
 | 
					        return this.view.center;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getOrigin(): Vec2 {
 | 
					    getOrigin(): Vec2 {
 | 
				
			||||||
        return this.view.getCenter().clone().sub(this.view.getHalfSize())
 | 
					        return this.view.center.clone().sub(this.view.halfSize)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -134,10 +134,10 @@ export default class Viewport {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    includes(node: CanvasNode): boolean {
 | 
					    includes(node: CanvasNode): boolean {
 | 
				
			||||||
        let parallax = node.getLayer().getParallax();
 | 
					        let parallax = node.getLayer().getParallax();
 | 
				
			||||||
        let center = this.view.getCenter().clone();
 | 
					        let center = this.view.center.clone();
 | 
				
			||||||
        this.view.getCenter().mult(parallax);
 | 
					        this.view.center.mult(parallax);
 | 
				
			||||||
        let overlaps = this.view.overlaps(node.getBoundary());
 | 
					        let overlaps = this.view.overlaps(node.boundary);
 | 
				
			||||||
        this.view.setCenter(center);
 | 
					        this.view.center = center
 | 
				
			||||||
        return overlaps;
 | 
					        return overlaps;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,8 +155,8 @@ export default class Viewport {
 | 
				
			||||||
        let hheight = (upperY - lowerY)/2;
 | 
					        let hheight = (upperY - lowerY)/2;
 | 
				
			||||||
        let x = lowerX + hwidth;
 | 
					        let x = lowerX + hwidth;
 | 
				
			||||||
        let y = lowerY + hheight;
 | 
					        let y = lowerY + hheight;
 | 
				
			||||||
        this.boundary.setCenter(new Vec2(x, y));
 | 
					        this.boundary.center.set(x, y);
 | 
				
			||||||
        this.boundary.setHalfSize(new Vec2(hwidth, hheight));
 | 
					        this.boundary.halfSize.set(hwidth, hheight);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -202,7 +202,7 @@ export default class Viewport {
 | 
				
			||||||
        // If viewport is following an object
 | 
					        // If viewport is following an object
 | 
				
			||||||
        if(this.following){
 | 
					        if(this.following){
 | 
				
			||||||
            // Update our list of previous positions
 | 
					            // Update our list of previous positions
 | 
				
			||||||
            this.lastPositions.enqueue(this.following.getPosition().clone());
 | 
					            this.lastPositions.enqueue(this.following.position.clone());
 | 
				
			||||||
            if(this.lastPositions.getSize() > this.smoothingFactor){
 | 
					            if(this.lastPositions.getSize() > this.smoothingFactor){
 | 
				
			||||||
                this.lastPositions.dequeue();
 | 
					                this.lastPositions.dequeue();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -222,7 +222,7 @@ export default class Viewport {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Debug.log("vp", "Viewport pos: " + pos.toString())
 | 
					            Debug.log("vp", "Viewport pos: " + pos.toString())
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            this.view.setCenter(pos);
 | 
					            this.view.center.copy(pos);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if(this.lastPositions.getSize() > this.smoothingFactor){
 | 
					            if(this.lastPositions.getSize() > this.smoothingFactor){
 | 
				
			||||||
                this.lastPositions.dequeue();
 | 
					                this.lastPositions.dequeue();
 | 
				
			||||||
| 
						 | 
					@ -241,7 +241,7 @@ export default class Viewport {
 | 
				
			||||||
            pos.y = Math.floor(pos.y);
 | 
					            pos.y = Math.floor(pos.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Debug.log("vp", "Viewport pos: " + pos.toString())
 | 
					            Debug.log("vp", "Viewport pos: " + pos.toString())
 | 
				
			||||||
            this.view.setCenter(pos);
 | 
					            this.view.center.copy(pos);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
import Scene from "./Scene/Scene";
 | 
					import Scene from "./Scene/Scene";
 | 
				
			||||||
import OrthogonalTilemap from "./Nodes/Tilemaps/OrthogonalTilemap";
 | 
					 | 
				
			||||||
import Player from "./Player";
 | 
					 | 
				
			||||||
import Rect from "./Nodes/Graphics/Rect";
 | 
					import Rect from "./Nodes/Graphics/Rect";
 | 
				
			||||||
import Color from "./Utils/Color";
 | 
					import Color from "./Utils/Color";
 | 
				
			||||||
import Vec2 from "./DataTypes/Vec2";
 | 
					import Vec2 from "./DataTypes/Vec2";
 | 
				
			||||||
| 
						 | 
					@ -23,7 +21,7 @@ export default class SecondScene extends Scene {
 | 
				
			||||||
        bar.setColor(new Color(255, 100, 0));
 | 
					        bar.setColor(new Color(255, 100, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.load.onLoadProgress = (percentProgress: number) => {
 | 
					        this.load.onLoadProgress = (percentProgress: number) => {
 | 
				
			||||||
            bar.setSize(295 * percentProgress, bar.getSize().y);
 | 
					            //bar.setSize(295 * percentProgress, bar.getSize().y);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.load.onLoadComplete = () => {
 | 
					        this.load.onLoadComplete = () => {
 | 
				
			||||||
| 
						 | 
					@ -32,76 +30,76 @@ export default class SecondScene extends Scene {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    startScene(){
 | 
					    startScene(){
 | 
				
			||||||
        // Add the tilemap
 | 
					        // // Add the tilemap
 | 
				
			||||||
        let mainLayer = this.add.tilemap("level2")[1];
 | 
					        // let mainLayer = this.add.tilemap("level2")[1];
 | 
				
			||||||
        mainLayer.setYSort(true);
 | 
					        // mainLayer.setYSort(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add a player
 | 
					        // // Add a player
 | 
				
			||||||
        let player = this.add.physics(Player, mainLayer, "topdown");
 | 
					        // let player = this.add.physics(Player, mainLayer, "topdown");
 | 
				
			||||||
        let playerSprite = this.add.sprite("player", mainLayer);
 | 
					        // let playerSprite = this.add.sprite("player", mainLayer);
 | 
				
			||||||
        player.setSprite(playerSprite);
 | 
					        // player.setSprite(playerSprite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.viewport.follow(player);
 | 
					        // this.viewport.follow(player);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Initialize UI
 | 
					        // // Initialize UI
 | 
				
			||||||
        let uiLayer = this.addLayer();
 | 
					        // let uiLayer = this.addLayer();
 | 
				
			||||||
        uiLayer.setParallax(0, 0);
 | 
					        // uiLayer.setParallax(0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let recordButton = this.add.uiElement(Button, uiLayer);
 | 
					        // let recordButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        recordButton.setSize(100, 50);
 | 
					        // recordButton.setSize(100, 50);
 | 
				
			||||||
        recordButton.setText("Record");
 | 
					        // recordButton.setText("Record");
 | 
				
			||||||
        recordButton.setPosition(400, 30);
 | 
					        // recordButton.setPosition(400, 30);
 | 
				
			||||||
        recordButton.onClickEventId = GameEventType.START_RECORDING;
 | 
					        // recordButton.onClickEventId = GameEventType.START_RECORDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let stopButton = this.add.uiElement(Button, uiLayer);
 | 
					        // let stopButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        stopButton.setSize(100, 50);
 | 
					        // stopButton.setSize(100, 50);
 | 
				
			||||||
        stopButton.setText("Stop");
 | 
					        // stopButton.setText("Stop");
 | 
				
			||||||
        stopButton.setPosition(550, 30);
 | 
					        // stopButton.setPosition(550, 30);
 | 
				
			||||||
        stopButton.onClickEventId = GameEventType.STOP_RECORDING;
 | 
					        // stopButton.onClickEventId = GameEventType.STOP_RECORDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let playButton = this.add.uiElement(Button, uiLayer);
 | 
					        // let playButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        playButton.setSize(100, 50);
 | 
					        // playButton.setSize(100, 50);
 | 
				
			||||||
        playButton.setText("Play");
 | 
					        // playButton.setText("Play");
 | 
				
			||||||
        playButton.setPosition(700, 30);
 | 
					        // playButton.setPosition(700, 30);
 | 
				
			||||||
        playButton.onClickEventId = GameEventType.PLAY_RECORDING;
 | 
					        // playButton.onClickEventId = GameEventType.PLAY_RECORDING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let cycleFramerateButton = this.add.uiElement(Button, uiLayer);
 | 
					        // let cycleFramerateButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        cycleFramerateButton.setSize(150, 50);
 | 
					        // cycleFramerateButton.setSize(150, 50);
 | 
				
			||||||
        cycleFramerateButton.setText("Cycle FPS");
 | 
					        // cycleFramerateButton.setText("Cycle FPS");
 | 
				
			||||||
        cycleFramerateButton.setPosition(5, 400);
 | 
					        // cycleFramerateButton.setPosition(5, 400);
 | 
				
			||||||
        let i = 0;
 | 
					        // let i = 0;
 | 
				
			||||||
        let fps = [15, 30, 60];
 | 
					        // let fps = [15, 30, 60];
 | 
				
			||||||
        cycleFramerateButton.onClick = () => {
 | 
					        // cycleFramerateButton.onClick = () => {
 | 
				
			||||||
            this.game.setMaxUpdateFPS(fps[i]);
 | 
					        //     this.game.setMaxUpdateFPS(fps[i]);
 | 
				
			||||||
            i = (i + 1) % 3;
 | 
					        //     i = (i + 1) % 3;
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Pause Menu
 | 
					        // // Pause Menu
 | 
				
			||||||
        let pauseLayer = this.addLayer();
 | 
					        // let pauseLayer = this.addLayer();
 | 
				
			||||||
        pauseLayer.setParallax(0, 0);
 | 
					        // pauseLayer.setParallax(0, 0);
 | 
				
			||||||
        pauseLayer.disable();
 | 
					        // pauseLayer.disable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let pauseButton = this.add.uiElement(Button, uiLayer);
 | 
					        // let pauseButton = this.add.uiElement(Button, uiLayer);
 | 
				
			||||||
        pauseButton.setSize(100, 50);
 | 
					        // pauseButton.setSize(100, 50);
 | 
				
			||||||
        pauseButton.setText("Pause");
 | 
					        // pauseButton.setText("Pause");
 | 
				
			||||||
        pauseButton.setPosition(700, 400);
 | 
					        // pauseButton.setPosition(700, 400);
 | 
				
			||||||
        pauseButton.onClick = () => {
 | 
					        // pauseButton.onClick = () => {
 | 
				
			||||||
            this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(true));
 | 
					        //     this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(true));
 | 
				
			||||||
            pauseLayer.enable();
 | 
					        //     pauseLayer.enable();
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let modalBackground = this.add.uiElement(UIElement, pauseLayer);
 | 
					        // let modalBackground = this.add.uiElement(UIElement, pauseLayer);
 | 
				
			||||||
        modalBackground.setSize(400, 200);
 | 
					        // modalBackground.setSize(400, 200);
 | 
				
			||||||
        modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
 | 
					        // modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
 | 
				
			||||||
        modalBackground.setPosition(200, 100);
 | 
					        // modalBackground.setPosition(200, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let resumeButton = this.add.uiElement(Button, pauseLayer);
 | 
					        // let resumeButton = this.add.uiElement(Button, pauseLayer);
 | 
				
			||||||
        resumeButton.setSize(100, 50);
 | 
					        // resumeButton.setSize(100, 50);
 | 
				
			||||||
        resumeButton.setText("Resume");
 | 
					        // resumeButton.setText("Resume");
 | 
				
			||||||
        resumeButton.setPosition(400, 200);
 | 
					        // resumeButton.setPosition(400, 200);
 | 
				
			||||||
        resumeButton.onClick = () => {
 | 
					        // resumeButton.onClick = () => {
 | 
				
			||||||
            this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(false));
 | 
					        //     this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(false));
 | 
				
			||||||
            pauseLayer.disable();
 | 
					        //     pauseLayer.disable();
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								src/Utils/PhysicsUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Utils/PhysicsUtils.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import { Physical } from "../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class PhysicsUtils {
 | 
				
			||||||
 | 
						static sweepAndPrune(nodes: Array<Physical>){
 | 
				
			||||||
 | 
							// Sort 
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								src/Utils/SortingUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Utils/SortingUtils.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					export default class SortingUtils {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param arr 
 | 
				
			||||||
 | 
						 * @param comparator Compares element a and b in the array. Returns -1 if a < b, 0 if a = b, and 1 if a > b
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						static insertionSort<T>(arr: Array<T>, comparator: (a: T, b: T) => number): void {
 | 
				
			||||||
 | 
							let i = 1;
 | 
				
			||||||
 | 
							let j;
 | 
				
			||||||
 | 
							while(i < arr.length){
 | 
				
			||||||
 | 
								j = i;
 | 
				
			||||||
 | 
								while(j > 0 && comparator(arr[j-1], arr[j]) > 0){
 | 
				
			||||||
 | 
									SortingUtils.swap(arr, j-1, j);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								i += 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static swap<T>(arr: Array<T>, i: number, j: number){
 | 
				
			||||||
 | 
							let temp = arr[i];
 | 
				
			||||||
 | 
							arr[i] = arr[j];
 | 
				
			||||||
 | 
							arr[j] = temp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,7 @@ export default class BoidBehavior extends State {
 | 
				
			||||||
            speed = MathUtils.clamp(speed, BoidBehavior.MIN_SPEED, BoidBehavior.MAX_SPEED);
 | 
					            speed = MathUtils.clamp(speed, BoidBehavior.MIN_SPEED, BoidBehavior.MAX_SPEED);
 | 
				
			||||||
            this.actor.velocity.scale(speed);
 | 
					            this.actor.velocity.scale(speed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(this.actor.getId() < 1){
 | 
					            if(this.actor.id < 1){
 | 
				
			||||||
                Debug.log("BoidSep", "Separation: " + separationForce.toString());
 | 
					                Debug.log("BoidSep", "Separation: " + separationForce.toString());
 | 
				
			||||||
                Debug.log("BoidAl", "Alignment: " + alignmentForce.toString());
 | 
					                Debug.log("BoidAl", "Alignment: " + alignmentForce.toString());
 | 
				
			||||||
                Debug.log("BoidCo", "Cohesion: " + cohesionForce.toString());
 | 
					                Debug.log("BoidCo", "Cohesion: " + cohesionForce.toString());
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,7 @@ export default class BoidBehavior extends State {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(this.actor.getId() < 1){
 | 
					        if(this.actor.id < 1){
 | 
				
			||||||
            Debug.log("BoidDir", "Velocity: " + this.actor.velocity.toString());
 | 
					            Debug.log("BoidDir", "Velocity: " + this.actor.velocity.toString());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import AABB from "../../DataTypes/AABB";
 | 
					import AABB from "../../DataTypes/Shapes/AABB";
 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
import Point from "../../Nodes/Graphics/Point";
 | 
					import Point from "../../Nodes/Graphics/Point";
 | 
				
			||||||
import Scene from "../../Scene/Scene";
 | 
					import Scene from "../../Scene/Scene";
 | 
				
			||||||
| 
						 | 
					@ -21,14 +21,14 @@ export default class FlockBehavior {
 | 
				
			||||||
        this.actor = actor;
 | 
					        this.actor = actor;
 | 
				
			||||||
        this.flock = flock;
 | 
					        this.flock = flock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.visibleRegion = new AABB(this.actor.getPosition().clone(), new Vec2(visionRange, visionRange));
 | 
					        this.visibleRegion = new AABB(this.actor.position.clone(), new Vec2(visionRange, visionRange));
 | 
				
			||||||
        this.avoidRadius = avoidRadius;
 | 
					        this.avoidRadius = avoidRadius;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update(): void {
 | 
					    update(): void {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Update the visible region
 | 
					        // Update the visible region
 | 
				
			||||||
        this.visibleRegion.setCenter(this.actor.getPosition().clone());
 | 
					        this.visibleRegion.center.copy(this.actor.position);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let neighbors = this.scene.getSceneGraph().getNodesInRegion(this.visibleRegion);
 | 
					        let neighbors = this.scene.getSceneGraph().getNodesInRegion(this.visibleRegion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,7 +46,7 @@ export default class FlockBehavior {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Draw a group
 | 
					        // Draw a group
 | 
				
			||||||
        if(this.actor.getId() < 1){
 | 
					        if(this.actor.id < 1){
 | 
				
			||||||
            this.actor.setColor(Color.GREEN);
 | 
					            this.actor.setColor(Color.GREEN);
 | 
				
			||||||
            for(let neighbor of neighbors){
 | 
					            for(let neighbor of neighbors){
 | 
				
			||||||
                if(neighbor === this.actor) continue;
 | 
					                if(neighbor === this.actor) continue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
import StateMachine from "../../DataTypes/State/StateMachine";
 | 
					import StateMachine from "../../DataTypes/State/StateMachine";
 | 
				
			||||||
import { CustomGameEventType } from "../CustomGameEventType";
 | 
					import { CustomGameEventType } from "../CustomGameEventType";
 | 
				
			||||||
import Goomba from "../MarioClone/Goomba";
 | 
					 | 
				
			||||||
import Idle from "../Enemies/Idle";
 | 
					import Idle from "../Enemies/Idle";
 | 
				
			||||||
import Jump from "../Enemies/Jump";
 | 
					import Jump from "../Enemies/Jump";
 | 
				
			||||||
import Walk from "../Enemies/Walk";
 | 
					import Walk from "../Enemies/Walk";
 | 
				
			||||||
import Debug from "../../Debug/Debug";
 | 
					import Debug from "../../Debug/Debug";
 | 
				
			||||||
 | 
					import GameNode from "../../Nodes/GameNode";
 | 
				
			||||||
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum GoombaStates {
 | 
					export enum GoombaStates {
 | 
				
			||||||
	IDLE = "idle",
 | 
						IDLE = "idle",
 | 
				
			||||||
| 
						 | 
					@ -14,10 +15,13 @@ export enum GoombaStates {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class GoombaController extends StateMachine {
 | 
					export default class GoombaController extends StateMachine {
 | 
				
			||||||
	owner: Goomba;
 | 
						owner: GameNode;
 | 
				
			||||||
	jumpy: boolean;
 | 
						jumpy: boolean;
 | 
				
			||||||
 | 
						direction: Vec2 = Vec2.ZERO;
 | 
				
			||||||
 | 
						velocity: Vec2 = Vec2.ZERO;
 | 
				
			||||||
 | 
						speed: number = 200;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(owner: Goomba, jumpy: boolean){
 | 
						constructor(owner: GameNode, jumpy: boolean){
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner = owner;
 | 
							this.owner = owner;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,21 @@
 | 
				
			||||||
import State from "../../DataTypes/State/State";
 | 
					import State from "../../DataTypes/State/State";
 | 
				
			||||||
import StateMachine from "../../DataTypes/State/StateMachine";
 | 
					import StateMachine from "../../DataTypes/State/StateMachine";
 | 
				
			||||||
import Goomba from "../MarioClone/Goomba";
 | 
					import GameNode from "../../Nodes/GameNode";
 | 
				
			||||||
 | 
					import GoombaController from "./GoombaController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default abstract class GoombaState extends State {
 | 
					export default abstract class GoombaState extends State {
 | 
				
			||||||
	owner: Goomba;
 | 
						owner: GameNode;
 | 
				
			||||||
	gravity: number = 7000;
 | 
						gravity: number = 7000;
 | 
				
			||||||
 | 
						parent: GoombaController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(parent: StateMachine, owner: Goomba){
 | 
						constructor(parent: StateMachine, owner: GameNode){
 | 
				
			||||||
		super(parent);
 | 
							super(parent);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		this.owner = owner;
 | 
							this.owner = owner;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		// Do gravity;
 | 
							// Do gravity
 | 
				
			||||||
		this.owner.velocity.y += this.gravity*deltaT;
 | 
							this.parent.velocity.y += this.gravity*deltaT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import OnGround from "./OnGround";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Idle extends OnGround {
 | 
					export default class Idle extends OnGround {
 | 
				
			||||||
	onEnter(): void {
 | 
						onEnter(): void {
 | 
				
			||||||
		this.owner.speed = this.owner.speed;
 | 
							this.parent.speed = this.parent.speed;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handleInput(event: GameEvent) {
 | 
						handleInput(event: GameEvent) {
 | 
				
			||||||
| 
						 | 
					@ -22,8 +22,8 @@ export default class Idle extends OnGround {
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		super.update(deltaT);
 | 
							super.update(deltaT);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		this.owner.velocity.x = 0;
 | 
							this.parent.velocity.x = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -11,17 +11,17 @@ export default class Jump extends GoombaState {
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		super.update(deltaT);
 | 
							super.update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.owner.isGrounded()){
 | 
							if(this.owner.onGround){
 | 
				
			||||||
			this.finished(GoombaStates.PREVIOUS);
 | 
								this.finished(GoombaStates.PREVIOUS);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.owner.isOnCeiling()){
 | 
							if(this.owner.onCeiling){
 | 
				
			||||||
			this.owner.velocity.y = 0;
 | 
								this.parent.velocity.y = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.velocity.x += this.owner.direction.x * this.owner.speed/3.5 - 0.3*this.owner.velocity.x;
 | 
							this.parent.velocity.x += this.parent.direction.x * this.parent.speed/3.5 - 0.3*this.parent.velocity.x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onExit(): void {}
 | 
						onExit(): void {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,19 +7,19 @@ export default class OnGround extends GoombaState {
 | 
				
			||||||
	onEnter(): void {}
 | 
						onEnter(): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handleInput(event: GameEvent): void {
 | 
						handleInput(event: GameEvent): void {
 | 
				
			||||||
		if(event.type === CustomGameEventType.PLAYER_JUMP && (<GoombaController>this.parentStateMachine).jumpy){
 | 
							if(event.type === CustomGameEventType.PLAYER_JUMP && (<GoombaController>this.parent).jumpy){
 | 
				
			||||||
			this.finished(GoombaStates.JUMP);
 | 
								this.finished(GoombaStates.JUMP);
 | 
				
			||||||
			this.owner.velocity.y = -2000;
 | 
								this.parent.velocity.y = -2000;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		if(this.owner.velocity.y > 0){
 | 
							if(this.parent.velocity.y > 0){
 | 
				
			||||||
			this.owner.velocity.y = 0;
 | 
								this.parent.velocity.y = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		super.update(deltaT);
 | 
							super.update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(!this.owner.isGrounded()){
 | 
							if(!this.owner.onGround){
 | 
				
			||||||
			this.finished(GoombaStates.JUMP);
 | 
								this.finished(GoombaStates.JUMP);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,21 +3,21 @@ import OnGround from "./OnGround";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Walk extends OnGround {
 | 
					export default class Walk extends OnGround {
 | 
				
			||||||
	onEnter(): void {
 | 
						onEnter(): void {
 | 
				
			||||||
		if(this.owner.direction.isZero()){
 | 
							if(this.parent.direction.isZero()){
 | 
				
			||||||
			this.owner.direction = new Vec2(-1, 0);
 | 
								this.parent.direction = new Vec2(-1, 0);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		super.update(deltaT);
 | 
							super.update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.owner.isOnWall()){
 | 
							if(this.owner.onWall){
 | 
				
			||||||
			// Flip around
 | 
								// Flip around
 | 
				
			||||||
			this.owner.direction.x *= -1;
 | 
								this.parent.direction.x *= -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.velocity.x = this.owner.direction.x * this.owner.speed;
 | 
							this.parent.velocity.x = this.parent.direction.x * this.parent.speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,30 +0,0 @@
 | 
				
			||||||
import AABB from "../../DataTypes/AABB";
 | 
					 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					 | 
				
			||||||
import Sprite from "../../Nodes/Sprites/Sprite";
 | 
					 | 
				
			||||||
import Collider from "../../Physics/Colliders/Collider";
 | 
					 | 
				
			||||||
import PhysicsNode from "../../Physics/PhysicsNode";
 | 
					 | 
				
			||||||
import GoombaController, { GoombaStates } from "../Enemies/GoombaController";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class Goomba extends PhysicsNode {
 | 
					 | 
				
			||||||
	controller: GoombaController;
 | 
					 | 
				
			||||||
	velocity: Vec2 = Vec2.ZERO;
 | 
					 | 
				
			||||||
	speed: number = 200;
 | 
					 | 
				
			||||||
	direction: Vec2 = Vec2.ZERO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	constructor(position: Vec2, canJump: boolean){
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.position.copy(position);
 | 
					 | 
				
			||||||
		this.velocity = Vec2.ZERO;
 | 
					 | 
				
			||||||
		this.controller = new GoombaController(this, canJump);
 | 
					 | 
				
			||||||
		this.controller.initialize(GoombaStates.IDLE);
 | 
					 | 
				
			||||||
		this.collider = new Collider(new AABB(position, new Vec2(32, 32)))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	create(): void {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	update(deltaT: number): void {
 | 
					 | 
				
			||||||
		this.controller.update(deltaT);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
import Scene from "../../Scene/Scene";
 | 
					import Scene from "../../Scene/Scene";
 | 
				
			||||||
import Rect from "../../Nodes/Graphics/Rect";
 | 
					import Rect from "../../Nodes/Graphics/Rect";
 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
import Player from "./Player";
 | 
					 | 
				
			||||||
import Color from "../../Utils/Color";
 | 
					import Color from "../../Utils/Color";
 | 
				
			||||||
import Goomba from "./Goomba";
 | 
					import PlayerController from "../Player/PlayerStates/Platformer/PlayerController";
 | 
				
			||||||
 | 
					import { PlayerStates } from "../Player/PlayerStates/Platformer/PlayerController";
 | 
				
			||||||
 | 
					import GoombaController from "../Enemies/GoombaController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class MarioClone extends Scene {
 | 
					export default class MarioClone extends Scene {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,19 +17,25 @@ export default class MarioClone extends Scene {
 | 
				
			||||||
		let layer = this.addLayer();
 | 
							let layer = this.addLayer();
 | 
				
			||||||
		this.add.tilemap("level", new Vec2(2, 2));
 | 
							this.add.tilemap("level", new Vec2(2, 2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let player = this.add.physics(Player, layer, new Vec2(0, 0));
 | 
							let player = this.add.graphic(Rect, layer, new Vec2(0, 0), new Vec2(64, 64));
 | 
				
			||||||
		let playerSprite = this.add.graphic(Rect, layer, new Vec2(0, 0), new Vec2(64, 64));
 | 
							player.setColor(Color.BLUE);
 | 
				
			||||||
		playerSprite.setColor(Color.BLUE);
 | 
							player.addPhysics();
 | 
				
			||||||
		player.addChild(playerSprite);
 | 
					
 | 
				
			||||||
		this.viewport.follow(playerSprite);
 | 
							this.viewport.follow(player);
 | 
				
			||||||
		this.viewport.setBounds(0, 0, 5120, 1280);
 | 
							this.viewport.setBounds(0, 0, 5120, 1280);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let ai = new PlayerController(player);
 | 
				
			||||||
 | 
							ai.initialize(PlayerStates.IDLE);
 | 
				
			||||||
 | 
							player.update = (deltaT: number) => {ai.update(deltaT)};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for(let xPos of [14, 20, 25, 30, 33, 37, 49, 55, 58, 70, 74]){
 | 
							for(let xPos of [14, 20, 25, 30, 33, 37, 49, 55, 58, 70, 74]){
 | 
				
			||||||
			let goomba = this.add.physics(Goomba, layer, new Vec2(64*xPos, 0), true);
 | 
								let goomba = this.add.sprite("goomba", layer);
 | 
				
			||||||
			let goombaSprite = this.add.sprite("goomba", layer);
 | 
								let ai = new GoombaController(goomba, false);
 | 
				
			||||||
			goombaSprite.setPosition(64*xPos, 0);
 | 
								ai.initialize("idle");
 | 
				
			||||||
			goombaSprite.setScale(new Vec2(2, 2));
 | 
								goomba.update = (deltaT: number) => {ai.update(deltaT)};
 | 
				
			||||||
			goomba.addChild(goombaSprite);
 | 
								goomba.position.set(64*xPos, 0);
 | 
				
			||||||
 | 
								goomba.scale.set(2, 2);
 | 
				
			||||||
 | 
								goomba.addPhysics();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,33 +0,0 @@
 | 
				
			||||||
import AABB from "../../DataTypes/AABB";
 | 
					 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					 | 
				
			||||||
import Debug from "../../Debug/Debug";
 | 
					 | 
				
			||||||
import Collider from "../../Physics/Colliders/Collider";
 | 
					 | 
				
			||||||
import PhysicsNode from "../../Physics/PhysicsNode";
 | 
					 | 
				
			||||||
import PlayerController from "../Player/PlayerStates/Platformer/PlayerController";
 | 
					 | 
				
			||||||
import { PlayerStates } from "../Player/PlayerStates/Platformer/PlayerController";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class Player extends PhysicsNode {
 | 
					 | 
				
			||||||
	protected controller: PlayerController
 | 
					 | 
				
			||||||
	velocity: Vec2;
 | 
					 | 
				
			||||||
	speed: number = 400;
 | 
					 | 
				
			||||||
	MIN_SPEED: number = 400;
 | 
					 | 
				
			||||||
	MAX_SPEED: number = 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	constructor(position: Vec2){
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		this.position.copy(position);
 | 
					 | 
				
			||||||
		this.velocity = Vec2.ZERO;
 | 
					 | 
				
			||||||
		this.controller = new PlayerController(this);
 | 
					 | 
				
			||||||
		this.controller.initialize(PlayerStates.IDLE);
 | 
					 | 
				
			||||||
		this.collider = new Collider(new AABB(Vec2.ZERO, new Vec2(32, 32)))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	create(): void {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	update(deltaT: number): void {
 | 
					 | 
				
			||||||
		this.controller.update(deltaT);
 | 
					 | 
				
			||||||
		Debug.log("playerVel", "Pos: " + this.position.toString() + ", Vel: " + this.velocity.toString())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,7 @@ export default class MoveTopDown extends State {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Otherwise, we are still moving, so update position
 | 
					        // Otherwise, we are still moving, so update position
 | 
				
			||||||
        let velocity = this.direction.normalize().scale(this.speed);
 | 
					        let velocity = this.direction.normalize().scale(this.speed);
 | 
				
			||||||
        this.owner.position.add(velocity.scale(deltaT));
 | 
					        this.owner.move(velocity.scale(deltaT));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Emit an event to tell the world we are moving
 | 
					        // Emit an event to tell the world we are moving
 | 
				
			||||||
        this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
					        this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import PlayerState from "./PlayerState";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Idle extends OnGround {
 | 
					export default class Idle extends OnGround {
 | 
				
			||||||
	onEnter(): void {
 | 
						onEnter(): void {
 | 
				
			||||||
		this.owner.speed = this.owner.MIN_SPEED;
 | 
							this.parent.speed = this.parent.MIN_SPEED;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
| 
						 | 
					@ -20,8 +20,8 @@ export default class Idle extends OnGround {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		this.owner.velocity.x = 0;
 | 
							this.parent.velocity.x = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -14,20 +14,20 @@ export default class Jump extends PlayerState {
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		super.update(deltaT);
 | 
							super.update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.owner.isGrounded()){
 | 
							if(this.owner.onGround){
 | 
				
			||||||
			this.finished(PlayerStates.PREVIOUS);
 | 
								this.finished(PlayerStates.PREVIOUS);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.owner.isOnCeiling()){
 | 
							if(this.owner.onCeiling){
 | 
				
			||||||
			this.owner.velocity.y = 0;
 | 
								this.parent.velocity.y = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		let dir = this.getInputDirection();
 | 
							let dir = this.getInputDirection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.velocity.x += dir.x * this.owner.speed/3.5 - 0.3*this.owner.velocity.x;
 | 
							this.parent.velocity.x += dir.x * this.parent.speed/3.5 - 0.3*this.parent.velocity.x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
							this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onExit(): void {}
 | 
						onExit(): void {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,16 +8,16 @@ export default class OnGround extends PlayerState {
 | 
				
			||||||
	handleInput(event: GameEvent): void {}
 | 
						handleInput(event: GameEvent): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		if(this.owner.velocity.y > 0){
 | 
							if(this.parent.velocity.y > 0){
 | 
				
			||||||
			this.owner.velocity.y = 0;
 | 
								this.parent.velocity.y = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		super.update(deltaT);
 | 
							super.update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.input.isJustPressed("w") || this.input.isJustPressed("space")){
 | 
							if(this.input.isJustPressed("w") || this.input.isJustPressed("space")){
 | 
				
			||||||
			this.finished("jump");
 | 
								this.finished("jump");
 | 
				
			||||||
			this.owner.velocity.y = -2000;
 | 
								this.parent.velocity.y = -2000;
 | 
				
			||||||
			this.emitter.fireEvent(CustomGameEventType.PLAYER_JUMP)
 | 
								this.emitter.fireEvent(CustomGameEventType.PLAYER_JUMP)
 | 
				
			||||||
		} else if(!this.owner.isGrounded()){
 | 
							} else if(!this.owner.onGround){
 | 
				
			||||||
			this.finished("jump");
 | 
								this.finished("jump");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
import StateMachine from "../../../../DataTypes/State/StateMachine";
 | 
					import StateMachine from "../../../../DataTypes/State/StateMachine";
 | 
				
			||||||
import Debug from "../../../../Debug/Debug";
 | 
					import Debug from "../../../../Debug/Debug";
 | 
				
			||||||
import Player from "../../../MarioClone/Player";
 | 
					 | 
				
			||||||
import Idle from "./Idle";
 | 
					import Idle from "./Idle";
 | 
				
			||||||
import Jump from "./Jump";
 | 
					import Jump from "./Jump";
 | 
				
			||||||
import Walk from "./Walk";
 | 
					import Walk from "./Walk";
 | 
				
			||||||
import Run from "./Run";
 | 
					import Run from "./Run";
 | 
				
			||||||
 | 
					import GameNode from "../../../../Nodes/GameNode";
 | 
				
			||||||
 | 
					import Vec2 from "../../../../DataTypes/Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum PlayerStates {
 | 
					export enum PlayerStates {
 | 
				
			||||||
	WALK = "walk",
 | 
						WALK = "walk",
 | 
				
			||||||
| 
						 | 
					@ -15,9 +16,13 @@ export enum PlayerStates {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PlayerController extends StateMachine {
 | 
					export default class PlayerController extends StateMachine {
 | 
				
			||||||
    protected owner: Player;
 | 
						protected owner: GameNode;
 | 
				
			||||||
 | 
						velocity: Vec2 = Vec2.ZERO;
 | 
				
			||||||
 | 
						speed: number = 400;
 | 
				
			||||||
 | 
						MIN_SPEED: number = 400;
 | 
				
			||||||
 | 
						MAX_SPEED: number = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(owner: Player){
 | 
					    constructor(owner: GameNode){
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
		this.owner = owner;
 | 
							this.owner = owner;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,17 @@ import State from "../../../../DataTypes/State/State";
 | 
				
			||||||
import StateMachine from "../../../../DataTypes/State/StateMachine";
 | 
					import StateMachine from "../../../../DataTypes/State/StateMachine";
 | 
				
			||||||
import Vec2 from "../../../../DataTypes/Vec2";
 | 
					import Vec2 from "../../../../DataTypes/Vec2";
 | 
				
			||||||
import InputReceiver from "../../../../Input/InputReceiver";
 | 
					import InputReceiver from "../../../../Input/InputReceiver";
 | 
				
			||||||
import CanvasNode from "../../../../Nodes/CanvasNode";
 | 
					import GameNode from "../../../../Nodes/GameNode";
 | 
				
			||||||
import Player from "../../../MarioClone/Player";
 | 
					import PlayerController from "./PlayerController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default abstract class PlayerState extends State {
 | 
					export default abstract class PlayerState extends State {
 | 
				
			||||||
	input: InputReceiver = InputReceiver.getInstance();
 | 
						input: InputReceiver = InputReceiver.getInstance();
 | 
				
			||||||
	owner: Player;
 | 
						owner: GameNode;
 | 
				
			||||||
	gravity: number = 7000;
 | 
						gravity: number = 7000;
 | 
				
			||||||
 | 
						parent: PlayerController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(parent: StateMachine, owner: Player){
 | 
						constructor(parent: StateMachine, owner: GameNode){
 | 
				
			||||||
		super(parent);
 | 
							super(parent);
 | 
				
			||||||
		this.owner = owner;
 | 
							this.owner = owner;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -27,7 +29,7 @@ export default abstract class PlayerState extends State {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
		// Do gravity;
 | 
							// Do gravity
 | 
				
			||||||
		this.owner.velocity.y += this.gravity*deltaT;
 | 
							this.parent.velocity.y += this.gravity*deltaT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import { PlayerStates } from "./PlayerController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Run extends OnGround {
 | 
					export default class Run extends OnGround {
 | 
				
			||||||
	onEnter(): void {
 | 
						onEnter(): void {
 | 
				
			||||||
		this.owner.speed = this.owner.MAX_SPEED;
 | 
							this.parent.speed = this.parent.MAX_SPEED;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
| 
						 | 
					@ -20,9 +20,9 @@ export default class Run extends OnGround {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.velocity.x = dir.x * this.owner.speed
 | 
							this.parent.velocity.x = dir.x * this.parent.speed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
							this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import { PlayerStates } from "./PlayerController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Walk extends OnGround {
 | 
					export default class Walk extends OnGround {
 | 
				
			||||||
	onEnter(): void {
 | 
						onEnter(): void {
 | 
				
			||||||
		this.owner.speed = this.owner.MAX_SPEED/2;
 | 
							this.parent.speed = this.parent.MAX_SPEED/2;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	update(deltaT: number): void {
 | 
						update(deltaT: number): void {
 | 
				
			||||||
| 
						 | 
					@ -20,9 +20,9 @@ export default class Walk extends OnGround {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.owner.velocity.x = dir.x * this.owner.speed
 | 
							this.parent.velocity.x = dir.x * this.parent.speed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
							this.emitter.fireEvent(CustomGameEventType.PLAYER_MOVE, {position: this.owner.position.clone()});
 | 
				
			||||||
		this.owner.move(this.owner.velocity.scaled(deltaT));
 | 
							this.owner.move(this.parent.velocity.scaled(deltaT));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ function main(){
 | 
				
			||||||
    let game = new GameLoop({canvasSize: {x: 800, y: 600}});
 | 
					    let game = new GameLoop({canvasSize: {x: 800, y: 600}});
 | 
				
			||||||
    game.start();
 | 
					    game.start();
 | 
				
			||||||
    let sm = game.getSceneManager();
 | 
					    let sm = game.getSceneManager();
 | 
				
			||||||
    sm.addScene(BoidDemo);
 | 
					    sm.addScene(MarioClone);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CanvasRenderingContext2D.prototype.roundedRect = function(x: number, y: number, w: number, h: number, r: number): void {
 | 
					CanvasRenderingContext2D.prototype.roundedRect = function(x: number, y: number, w: number, h: number, r: number): void {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        "src/Physics/Colliders/AABB.ts",
 | 
					        "src/Physics/Colliders/AABB.ts",
 | 
				
			||||||
        "src/Physics/Colliders/Collider.ts",
 | 
					        "src/Physics/Colliders/Collider.ts",
 | 
				
			||||||
        "src/Physics/PhysicsManager.ts",
 | 
					        "src/Physics/PhysicsManager_Old.ts",
 | 
				
			||||||
        "src/Physics/PhysicsNode.ts",
 | 
					        "src/Physics/PhysicsNode.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        "src/Playback/Recorder.ts",
 | 
					        "src/Playback/Recorder.ts",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user