converted all code to work with AABBs
This commit is contained in:
		
							parent
							
								
									a32066468f
								
							
						
					
					
						commit
						254462993a
					
				| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
 | 
					import Shape from "./Shape";
 | 
				
			||||||
import Vec2 from "./Vec2";
 | 
					import Vec2 from "./Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class AABB {
 | 
					export default class AABB extends Shape {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected center: Vec2;
 | 
					    protected center: Vec2;
 | 
				
			||||||
    protected halfSize: Vec2;
 | 
					    protected halfSize: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(center?: Vec2, halfSize?: Vec2){
 | 
					    constructor(center?: Vec2, halfSize?: Vec2){
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
        this.center = center ? center : new Vec2(0, 0);
 | 
					        this.center = center ? center : new Vec2(0, 0);
 | 
				
			||||||
        this.halfSize = halfSize ? halfSize : new Vec2(0, 0);
 | 
					        this.halfSize = halfSize ? halfSize : new Vec2(0, 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -26,6 +28,22 @@ export default class AABB {
 | 
				
			||||||
        return this.halfSize.y;
 | 
					        return this.halfSize.y;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get top(): number {
 | 
				
			||||||
 | 
					        return this.y - this.hh;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get bottom(): number {
 | 
				
			||||||
 | 
					        return this.y + this.hh;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get left(): number {
 | 
				
			||||||
 | 
					        return this.x - this.hw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get right(): number {
 | 
				
			||||||
 | 
					        return this.x + this.hw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getCenter(): Vec2 {
 | 
					    getCenter(): Vec2 {
 | 
				
			||||||
        return this.center;
 | 
					        return this.center;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -34,6 +52,10 @@ export default class AABB {
 | 
				
			||||||
        this.center = center;
 | 
					        this.center = center;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getBoundingRect(): AABB {
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getHalfSize(): Vec2 {
 | 
					    getHalfSize(): Vec2 {
 | 
				
			||||||
        return this.halfSize;
 | 
					        return this.halfSize;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -100,4 +122,19 @@ export default class AABB {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO - Implement this generally and use it in the tilemap
 | 
				
			||||||
 | 
					    overlapArea(other: AABB): number {
 | 
				
			||||||
 | 
					        let leftx = Math.max(this.x - this.hw, other.x - other.hw);
 | 
				
			||||||
 | 
					        let rightx = Math.min(this.x + this.hw, other.x + other.hw);
 | 
				
			||||||
 | 
					        let dx = rightx - leftx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let lefty = Math.max(this.y - this.hh, other.y - other.hh);
 | 
				
			||||||
 | 
					        let righty = Math.min(this.y + this.hh, other.y + other.hh);
 | 
				
			||||||
 | 
					        let dy = righty - lefty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(dx < 0 || dy < 0) return 0;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return dx*dy;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -123,8 +123,9 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
     * Returns all items at this point.
 | 
					     * Returns all items in this region
 | 
				
			||||||
     * @param point The point to query at
 | 
					     * @param boundary The region to check
 | 
				
			||||||
 | 
					     * @param inclusionCheck Allows for additional inclusion checks to further refine searches
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    queryRegion(boundary: AABB): Array<T> {
 | 
					    queryRegion(boundary: AABB): Array<T> {
 | 
				
			||||||
        // A matrix to keep track of our results
 | 
					        // A matrix to keep track of our results
 | 
				
			||||||
| 
						 | 
					@ -212,6 +213,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
 | 
				
			||||||
     * @param ctx 
 | 
					     * @param ctx 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public render_demo(ctx: CanvasRenderingContext2D): void {
 | 
					    public render_demo(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
        ctx.strokeStyle = "#0000FF";
 | 
					        ctx.strokeStyle = "#0000FF";
 | 
				
			||||||
        ctx.strokeRect(this.boundary.x - this.boundary.hw, this.boundary.y - this.boundary.hh, 2*this.boundary.hw, 2*this.boundary.hh);
 | 
					        ctx.strokeRect(this.boundary.x - this.boundary.hw, this.boundary.y - this.boundary.hh, 2*this.boundary.hw, 2*this.boundary.hh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/DataTypes/Shape.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/DataTypes/Shape.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					import AABB from "./AABB";
 | 
				
			||||||
 | 
					import Vec2 from "./Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class Shape {
 | 
				
			||||||
 | 
					    abstract setCenter(center: Vec2): void;
 | 
				
			||||||
 | 
					    abstract getCenter(): Vec2;
 | 
				
			||||||
 | 
					    abstract getBoundingRect(): AABB;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,26 +4,31 @@
 | 
				
			||||||
export default class Vec2 {
 | 
					export default class Vec2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Store x and y in an array
 | 
						// Store x and y in an array
 | 
				
			||||||
	private vec: Float32Array;
 | 
						//private vec: Float32Array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected _x: number;
 | 
				
			||||||
 | 
						protected _y: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**	
 | 
						/**	
 | 
				
			||||||
	 * When this vector changes its value, do something
 | 
						 * When this vector changes its value, do something
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private onChange: Function;
 | 
						private onChange: Function = () => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(x: number = 0, y: number = 0) {
 | 
						constructor(x: number = 0, y: number = 0) {
 | 
				
			||||||
		this.vec = new Float32Array(2);
 | 
							// this.vec = new Float32Array(2);
 | 
				
			||||||
		this.vec[0] = x;
 | 
							// this.vec[0] = x;
 | 
				
			||||||
		this.vec[1] = y;
 | 
							// this.vec[1] = y;
 | 
				
			||||||
 | 
							this._x = x;
 | 
				
			||||||
 | 
							this._y = y;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Expose x and y with getters and setters
 | 
						// Expose x and y with getters and setters
 | 
				
			||||||
	get x() {
 | 
						get x() {
 | 
				
			||||||
		return this.vec[0];
 | 
							return this._x; //this.vec[0];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set x(x: number) {
 | 
						set x(x: number) {
 | 
				
			||||||
		this.vec[0] = x;
 | 
							this._x = x;//this.vec[0] = x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.onChange){
 | 
							if(this.onChange){
 | 
				
			||||||
			this.onChange();
 | 
								this.onChange();
 | 
				
			||||||
| 
						 | 
					@ -31,11 +36,11 @@ export default class Vec2 {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	get y() {
 | 
						get y() {
 | 
				
			||||||
		return this.vec[1];
 | 
							return this._y;//this.vec[1];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set y(y: number) {
 | 
						set y(y: number) {
 | 
				
			||||||
		this.vec[1] = y;
 | 
							this._y = y;//this.vec[1] = y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(this.onChange){
 | 
							if(this.onChange){
 | 
				
			||||||
			this.onChange();
 | 
								this.onChange();
 | 
				
			||||||
| 
						 | 
					@ -181,6 +186,17 @@ export default class Vec2 {
 | 
				
			||||||
		return this;
 | 
							return this;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Divides this vector with another vector element-wise
 | 
				
			||||||
 | 
						 * @param other 
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						div(other: Vec2): Vec2 {
 | 
				
			||||||
 | 
							if(other.x === 0 || other.y === 0) throw "Divide by zero error";
 | 
				
			||||||
 | 
							this.x /= other.x;
 | 
				
			||||||
 | 
							this.y /= other.y;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns the squared distance between this vector and another vector
 | 
						 * Returns the squared distance between this vector and another vector
 | 
				
			||||||
	 * @param other 
 | 
						 * @param other 
 | 
				
			||||||
| 
						 | 
					@ -218,4 +234,8 @@ export default class Vec2 {
 | 
				
			||||||
	setOnChange(f: Function): void {
 | 
						setOnChange(f: Function): void {
 | 
				
			||||||
		this.onChange = f;
 | 
							this.onChange = f;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						getOnChange(): string {
 | 
				
			||||||
 | 
							return this.onChange.toString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ export default class InputReceiver{
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getGlobalMousePosition(): Vec2 {
 | 
						getGlobalMousePosition(): Vec2 {
 | 
				
			||||||
		return this.mousePosition.clone().add(this.viewport.getPosition());
 | 
							return this.mousePosition.clone().add(this.viewport.getOrigin());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getMousePressPosition(): Vec2 {
 | 
						getMousePressPosition(): Vec2 {
 | 
				
			||||||
| 
						 | 
					@ -128,7 +128,7 @@ export default class InputReceiver{
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getGlobalMousePressPosition(): Vec2 {
 | 
						getGlobalMousePressPosition(): Vec2 {
 | 
				
			||||||
		return this.mousePressPosition.clone().add(this.viewport.getPosition());
 | 
							return this.mousePressPosition.clone().add(this.viewport.getOrigin());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setViewport(viewport: Viewport): void {
 | 
						setViewport(viewport: Viewport): void {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,6 @@ export default class GameLoop {
 | 
				
			||||||
    constructor(config?: object){
 | 
					    constructor(config?: object){
 | 
				
			||||||
        // Typecast the config object to a GameConfig object
 | 
					        // Typecast the config object to a GameConfig object
 | 
				
			||||||
        let gameConfig = config ? <GameConfig>config : new GameConfig();
 | 
					        let gameConfig = config ? <GameConfig>config : new GameConfig();
 | 
				
			||||||
        console.log(gameConfig)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.maxFPS = 60;
 | 
					        this.maxFPS = 60;
 | 
				
			||||||
        this.simulationTimestep = Math.floor(1000/this.maxFPS);
 | 
					        this.simulationTimestep = Math.floor(1000/this.maxFPS);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import Button from "./Nodes/UIElements/Button";
 | 
				
			||||||
import Layer from "./Scene/Layer";
 | 
					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";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class MainScene extends Scene {
 | 
					export default class MainScene extends Scene {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +18,7 @@ export default class MainScene extends Scene {
 | 
				
			||||||
        this.load.tilemap("background", "assets/tilemaps/Background.json");
 | 
					        this.load.tilemap("background", "assets/tilemaps/Background.json");
 | 
				
			||||||
        this.load.image("player", "assets/sprites/player.png");
 | 
					        this.load.image("player", "assets/sprites/player.png");
 | 
				
			||||||
        this.load.audio("player_jump", "assets/sounds/jump-3.wav");
 | 
					        this.load.audio("player_jump", "assets/sounds/jump-3.wav");
 | 
				
			||||||
        this.load.audio("level_music", "assets/sounds/level.wav");
 | 
					        //this.load.audio("level_music", "assets/sounds/level.wav");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let loadingScreen = this.addLayer();
 | 
					        let loadingScreen = this.addLayer();
 | 
				
			||||||
        let box = this.add.graphic(Rect, loadingScreen, new Vec2(200, 300), new Vec2(400, 60));
 | 
					        let box = this.add.graphic(Rect, loadingScreen, new Vec2(200, 300), new Vec2(400, 60));
 | 
				
			||||||
| 
						 | 
					@ -35,17 +36,23 @@ export default class MainScene extends Scene {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    startScene(){
 | 
					    startScene(){
 | 
				
			||||||
 | 
					        // Set world size
 | 
				
			||||||
 | 
					        this.worldSize = new Vec2(2560, 1280)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Use a quadtree
 | 
				
			||||||
 | 
					        this.sceneGraph = new SceneGraphQuadTree(this.viewport, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add the background tilemap
 | 
					        // Add the background tilemap
 | 
				
			||||||
        let backgroundTilemapLayer = this.add.tilemap("background")[0];
 | 
					        let backgroundTilemapLayer = this.add.tilemap("background", new Vec2(4, 4))[0];
 | 
				
			||||||
        // ...and make it have parallax
 | 
					        // ...and make it have parallax
 | 
				
			||||||
        backgroundTilemapLayer.setParallax(0.5, 0.8);
 | 
					        backgroundTilemapLayer.setParallax(0.5, 0.8);
 | 
				
			||||||
        backgroundTilemapLayer.setAlpha(0.5);
 | 
					        backgroundTilemapLayer.setAlpha(0.5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add the music and start playing it on a loop
 | 
					        // Add the music and start playing it on a loop
 | 
				
			||||||
        this.emitter.fireEvent(GameEventType.PLAY_SOUND, {key: "level_music", loop: true, holdReference: true});
 | 
					        //this.emitter.fireEvent(GameEventType.PLAY_SOUND, {key: "level_music", loop: true, holdReference: true});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add the tilemap
 | 
					        // Add the tilemap
 | 
				
			||||||
        this.add.tilemap("platformer");
 | 
					        this.add.tilemap("platformer", new Vec2(4, 4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create the main game layer
 | 
					        // Create the main game layer
 | 
				
			||||||
        let mainLayer = this.addLayer();
 | 
					        let mainLayer = this.addLayer();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(){
 | 
						constructor(){
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
 | 
							this.position.setOnChange(this.positionChanged);
 | 
				
			||||||
		this._size = new Vec2(0, 0);
 | 
							this._size = new Vec2(0, 0);
 | 
				
			||||||
		this._size.setOnChange(this.sizeChanged);
 | 
							this._size.setOnChange(this.sizeChanged);
 | 
				
			||||||
		this._scale = new Vec2(1, 1);
 | 
							this._scale = new Vec2(1, 1);
 | 
				
			||||||
| 
						 | 
					@ -37,7 +38,7 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set scale(scale: Vec2){
 | 
						set scale(scale: Vec2){
 | 
				
			||||||
		this._scale = scale;
 | 
							this._scale = scale;
 | 
				
			||||||
		this._scale.setOnChange(this.sizeChanged);
 | 
							this._scale.setOnChange(this.scaleChanged);
 | 
				
			||||||
		this.scaleChanged();
 | 
							this.scaleChanged();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,15 +69,15 @@ export default abstract class CanvasNode extends GameNode implements Region {
 | 
				
			||||||
		this.scale = scale;
 | 
							this.scale = scale;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	positionChanged = (): void => {
 | 
						protected positionChanged = (): void => {
 | 
				
			||||||
		this.updateBoundary();
 | 
							this.updateBoundary();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sizeChanged = (): void => {
 | 
						protected sizeChanged = (): void => {
 | 
				
			||||||
		this.updateBoundary();
 | 
							this.updateBoundary();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	scaleChanged = (): void => {
 | 
						protected scaleChanged = (): void => {
 | 
				
			||||||
		this.updateBoundary();
 | 
							this.updateBoundary();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ 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 } from "../DataTypes/Interfaces/Descriptors"
 | 
					import { Positioned, Unique } from "../DataTypes/Interfaces/Descriptors"
 | 
				
			||||||
 | 
					import UIElement from "./UIElement";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The representation of an object in the game world
 | 
					 * The representation of an object in the game world
 | 
				
			||||||
| 
						 | 
					@ -76,11 +77,11 @@ export default abstract class GameNode implements Positioned, Unique {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Called if the position vector is modified or replaced
 | 
						 * Called if the position vector is modified or replaced
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected positionChanged(){}
 | 
						protected positionChanged = (): void => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 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?
 | 
				
			||||||
	protected getViewportOriginWithParallax(): Vec2 {
 | 
						getViewportOriginWithParallax(): Vec2 {
 | 
				
			||||||
		return this.scene.getViewport().getPosition().clone().mult(this.layer.getParallax());
 | 
							return this.scene.getViewport().getOrigin().mult(this.layer.getParallax());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	abstract update(deltaT: number): void;
 | 
						abstract update(deltaT: number): void;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,8 +30,15 @@ export default class Sprite extends CanvasNode {
 | 
				
			||||||
    render(ctx: CanvasRenderingContext2D): void {
 | 
					    render(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
        let image = ResourceManager.getInstance().getImage(this.imageId);
 | 
					        let image = ResourceManager.getInstance().getImage(this.imageId);
 | 
				
			||||||
        let origin = this.getViewportOriginWithParallax();
 | 
					        let origin = this.getViewportOriginWithParallax();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ctx.drawImage(image,
 | 
					        ctx.drawImage(image,
 | 
				
			||||||
            this.imageOffset.x, this.imageOffset.y, this.size.x, this.size.y,
 | 
					            this.imageOffset.x, this.imageOffset.y, this.size.x, this.size.y,
 | 
				
			||||||
            this.position.x - origin.x, this.position.y - origin.y, this.size.x * this.scale.x, this.size.y * this.scale.y);
 | 
					            this.position.x - origin.x - this.size.x*this.scale.x/2, this.position.y - origin.y - this.size.y*this.scale.y/2,
 | 
				
			||||||
 | 
					            this.size.x * this.scale.x, this.size.y * this.scale.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ctx.lineWidth = 4;
 | 
				
			||||||
 | 
					        ctx.strokeStyle = "#00FF00"
 | 
				
			||||||
 | 
					        let b = this.getBoundary();
 | 
				
			||||||
 | 
					        ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2, b.hh*2);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import { TiledTilemapData, TiledLayerData } from "../DataTypes/Tilesets/TiledDat
 | 
				
			||||||
export default abstract class Tilemap extends GameNode {
 | 
					export default abstract class Tilemap extends GameNode {
 | 
				
			||||||
    // A tileset represents the tiles within one specific image loaded from a file
 | 
					    // A tileset represents the tiles within one specific image loaded from a file
 | 
				
			||||||
    protected tilesets: Array<Tileset>;
 | 
					    protected tilesets: Array<Tileset>;
 | 
				
			||||||
    protected worldSize: Vec2;
 | 
					    protected size: Vec2;
 | 
				
			||||||
    protected tileSize: Vec2;
 | 
					    protected tileSize: Vec2;
 | 
				
			||||||
    protected scale: Vec2;
 | 
					    protected scale: Vec2;
 | 
				
			||||||
    public data: Array<number>;
 | 
					    public data: Array<number>;
 | 
				
			||||||
| 
						 | 
					@ -17,23 +17,23 @@ export default abstract class Tilemap extends GameNode {
 | 
				
			||||||
	public visible: boolean;
 | 
						public visible: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: Make this no longer be specific to Tiled
 | 
					    // TODO: Make this no longer be specific to Tiled
 | 
				
			||||||
    constructor(tilemapData: TiledTilemapData, layer: TiledLayerData, tilesets: Array<Tileset>) {
 | 
					    constructor(tilemapData: TiledTilemapData, layer: TiledLayerData, tilesets: Array<Tileset>, scale: Vec2) {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.tilesets = tilesets;
 | 
					        this.tilesets = tilesets;
 | 
				
			||||||
        this.worldSize = new Vec2(0, 0);
 | 
					        this.size = new Vec2(0, 0);
 | 
				
			||||||
        this.tileSize = new Vec2(0, 0);
 | 
					        this.tileSize = new Vec2(0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Defer parsing of the data to child classes - this allows for isometric vs. orthographic tilemaps and handling of Tiled data or other data
 | 
					        // Defer parsing of the data to child classes - this allows for isometric vs. orthographic tilemaps and handling of Tiled data or other data
 | 
				
			||||||
        this.parseTilemapData(tilemapData, layer);
 | 
					        this.parseTilemapData(tilemapData, layer);
 | 
				
			||||||
        this.scale = new Vec2(4, 4);
 | 
					        this.scale = scale.clone();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getTilesets(): Tileset[] {
 | 
					    getTilesets(): Tileset[] {
 | 
				
			||||||
        return this.tilesets;
 | 
					        return this.tilesets;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getWorldSize(): Vec2 {
 | 
					    getsize(): Vec2 {
 | 
				
			||||||
        return this.worldSize;
 | 
					        return this.size;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getTileSize(): Vec2 {
 | 
					    getTileSize(): Vec2 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ export default class OrthogonalTilemap extends Tilemap {
 | 
				
			||||||
     * @param layer 
 | 
					     * @param layer 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void {
 | 
					    protected parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void {
 | 
				
			||||||
        this.worldSize.set(tilemapData.width, tilemapData.height);
 | 
					        this.size.set(tilemapData.width, tilemapData.height);
 | 
				
			||||||
        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;
 | 
				
			||||||
| 
						 | 
					@ -34,12 +34,12 @@ export default class OrthogonalTilemap extends Tilemap {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getTileAt(worldCoords: Vec2): number {
 | 
					    getTileAt(worldCoords: Vec2): number {
 | 
				
			||||||
        let localCoords = this.getColRowAt(worldCoords);
 | 
					        let localCoords = this.getColRowAt(worldCoords);
 | 
				
			||||||
        if(localCoords.x < 0 || localCoords.x >= this.worldSize.x || localCoords.y < 0 || localCoords.y >= this.worldSize.y){
 | 
					        if(localCoords.x < 0 || localCoords.x >= this.size.x || localCoords.y < 0 || localCoords.y >= this.size.y){
 | 
				
			||||||
            // There are no tiles in negative positions or out of bounds positions
 | 
					            // There are no tiles in negative positions or out of bounds positions
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this.data[localCoords.y * this.worldSize.x + localCoords.x]
 | 
					        return this.data[localCoords.y * this.size.x + localCoords.x]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -50,11 +50,11 @@ export default class OrthogonalTilemap extends Tilemap {
 | 
				
			||||||
    isTileCollidable(indexOrCol: number, row?: number): boolean {
 | 
					    isTileCollidable(indexOrCol: number, row?: number): boolean {
 | 
				
			||||||
        let index = 0;
 | 
					        let index = 0;
 | 
				
			||||||
        if(row){
 | 
					        if(row){
 | 
				
			||||||
            if(indexOrCol < 0 || indexOrCol >= this.worldSize.x || row < 0 || row >= this.worldSize.y){
 | 
					            if(indexOrCol < 0 || indexOrCol >= this.size.x || row < 0 || row >= this.size.y){
 | 
				
			||||||
                // There are no tiles in negative positions or out of bounds positions
 | 
					                // There are no tiles in negative positions or out of bounds positions
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            index = row * this.worldSize.x + indexOrCol;
 | 
					            index = row * this.size.x + indexOrCol;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if(indexOrCol < 0 || indexOrCol >= this.data.length){
 | 
					            if(indexOrCol < 0 || indexOrCol >= this.data.length){
 | 
				
			||||||
                // Tiles that don't exist aren't collidable
 | 
					                // Tiles that don't exist aren't collidable
 | 
				
			||||||
| 
						 | 
					@ -93,7 +93,7 @@ export default class OrthogonalTilemap extends Tilemap {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for(let tileset of this.tilesets){
 | 
					                for(let tileset of this.tilesets){
 | 
				
			||||||
                    if(tileset.hasTile(tileIndex)){
 | 
					                    if(tileset.hasTile(tileIndex)){
 | 
				
			||||||
                        tileset.renderTile(ctx, tileIndex, i, this.worldSize, origin, this.scale);
 | 
					                        tileset.renderTile(ctx, tileIndex, i, this.size, origin, this.scale);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -179,22 +179,29 @@ export default class UIElement extends CanvasNode{
 | 
				
			||||||
		let previousAlpha = ctx.globalAlpha;
 | 
							let previousAlpha = ctx.globalAlpha;
 | 
				
			||||||
		ctx.globalAlpha = this.getLayer().getAlpha();
 | 
							ctx.globalAlpha = this.getLayer().getAlpha();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let origin = this.scene.getViewport().getPosition().clone().mult(this.layer.getParallax());
 | 
							let origin = this.getViewportOriginWithParallax();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.font = this.fontSize + "px " + this.font;
 | 
							ctx.font = this.fontSize + "px " + this.font;
 | 
				
			||||||
		let offset = this.calculateOffset(ctx);
 | 
							let offset = this.calculateOffset(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Stroke and fill a rounded rect and give it text
 | 
							// Stroke and fill a rounded rect and give it text
 | 
				
			||||||
		ctx.fillStyle = this.calculateBackgroundColor();
 | 
							ctx.fillStyle = this.calculateBackgroundColor();
 | 
				
			||||||
		ctx.fillRoundedRect(this.position.x - origin.x, this.position.y - origin.y, this.size.x, this.size.y, this.borderRadius);
 | 
							ctx.fillRoundedRect(this.position.x - origin.x - this.size.x/2, this.position.y - origin.y - this.size.y/2,
 | 
				
			||||||
 | 
								this.size.x, this.size.y, this.borderRadius);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		ctx.strokeStyle = this.calculateBorderColor();
 | 
							ctx.strokeStyle = this.calculateBorderColor();
 | 
				
			||||||
		ctx.lineWidth = this.borderWidth;
 | 
							ctx.lineWidth = this.borderWidth;
 | 
				
			||||||
		ctx.strokeRoundedRect(this.position.x - origin.x, this.position.y - origin.y, this.size.x, this.size.y, this.borderRadius);
 | 
							ctx.strokeRoundedRect(this.position.x - origin.x - this.size.x/2, this.position.y - origin.y - this.size.y/2,
 | 
				
			||||||
 | 
								this.size.x, this.size.y, this.borderRadius);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.fillStyle = this.calculateTextColor();
 | 
							ctx.fillStyle = this.calculateTextColor();
 | 
				
			||||||
		ctx.fillText(this.text, this.position.x + offset.x - origin.x, this.position.y + offset.y - origin.y);
 | 
							ctx.fillText(this.text, this.position.x + offset.x - origin.x - this.size.x/2, this.position.y + offset.y - origin.y - this.size.y/2);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
		ctx.globalAlpha = previousAlpha;
 | 
							ctx.globalAlpha = previousAlpha;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.lineWidth = 4;
 | 
				
			||||||
 | 
					        ctx.strokeStyle = "#00FF00"
 | 
				
			||||||
 | 
					        let b = this.getBoundary();
 | 
				
			||||||
 | 
					        ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2, b.hh*2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,29 +0,0 @@
 | 
				
			||||||
import Collider from "./Collider";
 | 
					 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class AABBCollider extends Collider {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    isCollidingWith(other: Collider): boolean {
 | 
					 | 
				
			||||||
        if(other instanceof AABBCollider){
 | 
					 | 
				
			||||||
            if(other.position.x > this.position.x && other.position.x < this.position.x + this.size.x){
 | 
					 | 
				
			||||||
                return other.position.y > this.position.y && other.position.y < this.position.y + this.size.y;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    willCollideWith(other: Collider, thisVel: Vec2, otherVel: Vec2): boolean {
 | 
					 | 
				
			||||||
        if(other instanceof AABBCollider){
 | 
					 | 
				
			||||||
            let thisPos = new Vec2(this.position.x + thisVel.x, this.position.y + thisVel.y);
 | 
					 | 
				
			||||||
            let otherPos = new Vec2(other.position.x + otherVel.x, other.position.y + otherVel.y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(otherPos.x > thisPos.x && otherPos.x < thisPos.x + this.size.x){
 | 
					 | 
				
			||||||
                return otherPos.y > thisPos.y && otherPos.y < thisPos.y + this.size.y;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    update(deltaT: number): void {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,39 @@
 | 
				
			||||||
import GameNode from "../../Nodes/GameNode";
 | 
					import AABB from "../../DataTypes/AABB";
 | 
				
			||||||
 | 
					import { Positioned } from "../../DataTypes/Interfaces/Descriptors";
 | 
				
			||||||
 | 
					import Shape from "../../DataTypes/Shape";
 | 
				
			||||||
import Vec2 from "../../DataTypes/Vec2";
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default abstract class Collider extends GameNode {
 | 
					export default class Collider implements Positioned {
 | 
				
			||||||
    protected size: Vec2;
 | 
					    protected shape: Shape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getSize(): Vec2 {
 | 
					    constructor(shape: Shape){
 | 
				
			||||||
        return this.size;
 | 
					        this.shape = shape;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: Make this accept vector arguments and number arguments
 | 
					    setPosition(position: Vec2): void {
 | 
				
			||||||
    setSize(size: Vec2): void {
 | 
					        this.shape.setCenter(position);
 | 
				
			||||||
        this.size = size;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract isCollidingWith(other: Collider): boolean;
 | 
					    getPosition(): Vec2 {
 | 
				
			||||||
 | 
					        return this.shape.getCenter();
 | 
				
			||||||
    abstract willCollideWith(other: Collider, thisVel: Vec2, otherVel: Vec2): boolean;
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								src/Physics/Colliders/Collisions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/Physics/Colliders/Collisions.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,97 @@
 | 
				
			||||||
 | 
					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];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,16 @@ import Debug from "../Debug/Debug";
 | 
				
			||||||
import MathUtils from "../Utils/MathUtils";
 | 
					import MathUtils from "../Utils/MathUtils";
 | 
				
			||||||
import Tilemap from "../Nodes/Tilemap";
 | 
					import Tilemap from "../Nodes/Tilemap";
 | 
				
			||||||
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
 | 
					import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
 | 
				
			||||||
 | 
					import AABB from "../DataTypes/AABB";
 | 
				
			||||||
 | 
					import { getTimeOfCollision } from "./Colliders/Collisions";
 | 
				
			||||||
 | 
					import Collider from "./Colliders/Collider";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PhysicsManager {
 | 
					export default class PhysicsManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private physicsNodes: Array<PhysicsNode>;
 | 
					    private physicsNodes: Array<PhysicsNode>;
 | 
				
			||||||
    private tilemaps: Array<Tilemap>;
 | 
					    private tilemaps: Array<Tilemap>;
 | 
				
			||||||
    private movements: Array<MovementData>;
 | 
					    private movements: Array<MovementData>;
 | 
				
			||||||
 | 
					    private tcols: Array<TileCollisionData> = [];
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
    constructor(){
 | 
					    constructor(){
 | 
				
			||||||
        this.physicsNodes = new Array();
 | 
					        this.physicsNodes = new Array();
 | 
				
			||||||
| 
						 | 
					@ -63,14 +67,14 @@ export default class PhysicsManager {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private collideWithOrthogonalTilemap(node: PhysicsNode, tilemap: OrthogonalTilemap, velocity: Vec2): void {
 | 
					    private collideWithOrthogonalTilemap(node: PhysicsNode, tilemap: OrthogonalTilemap, velocity: Vec2): void {
 | 
				
			||||||
        // Get the starting position of the moving node
 | 
					        // Get the starting position of the moving node
 | 
				
			||||||
        let startPos = node.getPosition();
 | 
					        let startPos = node.getCollider().getPosition();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get the end position of the moving node
 | 
					        // Get the end position of the moving node
 | 
				
			||||||
        let endPos = startPos.clone().add(velocity);
 | 
					        let endPos = startPos.clone().add(velocity);
 | 
				
			||||||
        let size = node.getCollider().getSize();
 | 
					        let size = node.getCollider().getBoundingRect().getHalfSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get the min and max x and y coordinates of the moving node
 | 
					        // Get the min and max x and y coordinates of the moving node
 | 
				
			||||||
        let min = new Vec2(Math.min(startPos.x, endPos.x), Math.min(startPos.y, endPos.y));
 | 
					        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));
 | 
					        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
 | 
					        // Convert the min/max x/y to the min and max row/col in the tilemap array
 | 
				
			||||||
| 
						 | 
					@ -79,6 +83,7 @@ export default class PhysicsManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create an empty set of tilemap collisions (We'll handle all of them at the end)
 | 
					        // Create an empty set of tilemap collisions (We'll handle all of them at the end)
 | 
				
			||||||
        let tilemapCollisions = new Array<TileCollisionData>();
 | 
					        let tilemapCollisions = new Array<TileCollisionData>();
 | 
				
			||||||
 | 
					        this.tcols = [];
 | 
				
			||||||
        let tileSize = tilemap.getTileSize();
 | 
					        let tileSize = tilemap.getTileSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Debug.log("tilemapCollision", "");
 | 
					        Debug.log("tilemapCollision", "");
 | 
				
			||||||
| 
						 | 
					@ -90,7 +95,10 @@ export default class PhysicsManager {
 | 
				
			||||||
                    Debug.log("tilemapCollision", "Colliding with Tile");
 | 
					                    Debug.log("tilemapCollision", "Colliding with Tile");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Get the position of this tile
 | 
					                    // Get the position of this tile
 | 
				
			||||||
                    let tilePos = new Vec2(col * tileSize.x, row * tileSize.y);
 | 
					                    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
 | 
					                    // 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 dx = Math.min(startPos.x, tilePos.x) - Math.max(startPos.x + size.x, tilePos.x + size.x);
 | 
				
			||||||
| 
						 | 
					@ -102,17 +110,21 @@ export default class PhysicsManager {
 | 
				
			||||||
                        overlap = dx * dy;
 | 
					                        overlap = dx * dy;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    tilemapCollisions.push(new TileCollisionData(tilePos, overlap));
 | 
					                    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
 | 
					        // Now that we have all collisions, sort by collision area highest to lowest
 | 
				
			||||||
        tilemapCollisions = tilemapCollisions.sort((a, b) => a.overlapArea - b.overlapArea);
 | 
					        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)
 | 
					        // 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 => {
 | 
					        tilemapCollisions.forEach(collision => {
 | 
				
			||||||
            let [firstContact, _, collidingX, collidingY] = this.getTimeOfAABBCollision(startPos, size, velocity, collision.position, tileSize, new Vec2(0, 0));
 | 
					            let [firstContact, _, collidingX, collidingY] = getTimeOfCollision(node.getCollider(), velocity, collision.collider, Vec2.ZERO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Handle collision
 | 
					            // Handle collision
 | 
				
			||||||
            if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
					            if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
				
			||||||
| 
						 | 
					@ -143,13 +155,7 @@ export default class PhysicsManager {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private collideWithStaticNode(movingNode: PhysicsNode, staticNode: PhysicsNode, velocity: Vec2){
 | 
					    private collideWithStaticNode(movingNode: PhysicsNode, staticNode: PhysicsNode, velocity: Vec2){
 | 
				
			||||||
        let sizeA = movingNode.getCollider().getSize();
 | 
					        let [firstContact, _, collidingX, collidingY] = getTimeOfCollision(movingNode.getCollider(), velocity, staticNode.getCollider(), Vec2.ZERO);
 | 
				
			||||||
        let posA = movingNode.getPosition();
 | 
					 | 
				
			||||||
        let velA = velocity;
 | 
					 | 
				
			||||||
        let sizeB = staticNode.getCollider().getSize();
 | 
					 | 
				
			||||||
        let posB = staticNode.getPosition();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let [firstContact, _, collidingX, collidingY] = this.getTimeOfAABBCollision(posA, sizeA, velA, posB, sizeB, new Vec2(0, 0));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
					        if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
 | 
				
			||||||
            if(collidingX && collidingY){
 | 
					            if(collidingX && collidingY){
 | 
				
			||||||
| 
						 | 
					@ -178,88 +184,6 @@ export default class PhysicsManager {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Gets the collision time of two AABBs using continuous collision checking. Returns vectors representing the time
 | 
					 | 
				
			||||||
     * of the start and end of the collision and booleans for whether or not the objects are currently overlapping
 | 
					 | 
				
			||||||
     * (before they move).
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private getTimeOfAABBCollision(posA: Vec2, sizeA: Vec2, velA: Vec2, posB: Vec2, sizeB: Vec2, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
 | 
					 | 
				
			||||||
        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 >= 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 - (posA.x + (sizeA.x)))/(relVel);
 | 
					 | 
				
			||||||
                lastContact.x = ((posB.x + sizeB.x) - posA.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 >= 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 - (posA.y + (sizeA.y)))/(relVel);
 | 
					 | 
				
			||||||
                lastContact.y = ((posB.y + sizeB.y) - posA.y)/(relVel);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            collidingY = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return [firstContact, lastContact, collidingX, collidingY];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    update(deltaT: number): void {
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
        for(let node of this.physicsNodes){
 | 
					        for(let node of this.physicsNodes){
 | 
				
			||||||
            if(!node.getLayer().isPaused()){
 | 
					            if(!node.getLayer().isPaused()){
 | 
				
			||||||
| 
						 | 
					@ -309,6 +233,28 @@ export default class PhysicsManager {
 | 
				
			||||||
        // Reset movements
 | 
					        // Reset movements
 | 
				
			||||||
        this.movements = new Array();
 | 
					        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
 | 
					// Helper classes for internal data
 | 
				
			||||||
| 
						 | 
					@ -325,10 +271,11 @@ class MovementData {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Collision data objects for tilemaps
 | 
					// Collision data objects for tilemaps
 | 
				
			||||||
class TileCollisionData {
 | 
					class TileCollisionData {
 | 
				
			||||||
    position: Vec2;
 | 
					    collider: Collider;
 | 
				
			||||||
    overlapArea: number;
 | 
					    overlapArea: number;
 | 
				
			||||||
    constructor(position: Vec2, overlapArea: number){
 | 
					
 | 
				
			||||||
        this.position = position;
 | 
					    constructor(collider: Collider, overlapArea: number){
 | 
				
			||||||
 | 
					        this.collider = collider;
 | 
				
			||||||
        this.overlapArea = overlapArea;
 | 
					        this.overlapArea = overlapArea;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -67,7 +67,7 @@ export default abstract class PhysicsNode extends GameNode {
 | 
				
			||||||
        this.position.add(velocity);
 | 
					        this.position.add(velocity);
 | 
				
			||||||
        this.collider.getPosition().add(velocity);
 | 
					        this.collider.getPosition().add(velocity);
 | 
				
			||||||
        for(let child of this.children){
 | 
					        for(let child of this.children){
 | 
				
			||||||
            child.getPosition().add(velocity);
 | 
					            child.position.add(velocity);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,15 @@
 | 
				
			||||||
import PhysicsNode from "./PhysicsNode";
 | 
					import PhysicsNode from "./PhysicsNode";
 | 
				
			||||||
import Vec2 from "../DataTypes/Vec2";
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
import AABBCollider from "./Colliders/AABBCollider";
 | 
					import Collider from "./Colliders/Collider";
 | 
				
			||||||
 | 
					import AABB from "../DataTypes/AABB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class StaticBody extends PhysicsNode {
 | 
					export default class StaticBody extends PhysicsNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(position: Vec2, size: Vec2){
 | 
					    constructor(position: Vec2, size: Vec2){
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.setPosition(position.x, position.y);
 | 
					        this.setPosition(position.x, position.y);
 | 
				
			||||||
        this.collider = new AABBCollider();
 | 
					        let aabb = new AABB(position.clone(), size.scaled(1/2));
 | 
				
			||||||
        this.collider.setPosition(position.x, position.y);
 | 
					        this.collider = new Collider(aabb);
 | 
				
			||||||
        this.collider.setSize(new Vec2(size.x, size.y));
 | 
					 | 
				
			||||||
        this.moving = false;
 | 
					        this.moving = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
import PhysicsNode from "./Physics/PhysicsNode";
 | 
					import PhysicsNode from "./Physics/PhysicsNode";
 | 
				
			||||||
import Vec2 from "./DataTypes/Vec2";
 | 
					import Vec2 from "./DataTypes/Vec2";
 | 
				
			||||||
import Debug from "./Debug/Debug";
 | 
					import Debug from "./Debug/Debug";
 | 
				
			||||||
import AABBCollider from "./Physics/Colliders/AABBCollider";
 | 
					 | 
				
			||||||
import CanvasNode from "./Nodes/CanvasNode";
 | 
					import CanvasNode from "./Nodes/CanvasNode";
 | 
				
			||||||
import { GameEventType } from "./Events/GameEventType";
 | 
					import { GameEventType } from "./Events/GameEventType";
 | 
				
			||||||
 | 
					import AABB from "./DataTypes/AABB";
 | 
				
			||||||
 | 
					import Collider from "./Physics/Colliders/Collider";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Player extends PhysicsNode {
 | 
					export default class Player extends PhysicsNode {
 | 
				
			||||||
	velocity: Vec2;
 | 
						velocity: Vec2;
 | 
				
			||||||
| 
						 | 
					@ -19,18 +20,20 @@ export default class Player extends PhysicsNode {
 | 
				
			||||||
        this.velocity = new Vec2(0, 0);
 | 
					        this.velocity = new Vec2(0, 0);
 | 
				
			||||||
        this.speed = 600;
 | 
					        this.speed = 600;
 | 
				
			||||||
        this.size = new Vec2(50, 50);
 | 
					        this.size = new Vec2(50, 50);
 | 
				
			||||||
        this.collider = new AABBCollider();
 | 
					 | 
				
			||||||
        this.collider.setSize(this.size);
 | 
					 | 
				
			||||||
        this.position = new Vec2(0, 0);
 | 
					        this.position = new Vec2(0, 0);
 | 
				
			||||||
        if(this.type === "topdown"){
 | 
					        if(this.type === "topdown"){
 | 
				
			||||||
            this.position = new Vec2(100, 100);
 | 
					            this.position = new Vec2(100, 100);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.collider = new Collider(new AABB(this.position.clone(), this.size.scaled(1/2)));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    create(): void {};
 | 
					    create(): void {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sprite: CanvasNode;
 | 
				
			||||||
    setSprite(sprite: CanvasNode): void {
 | 
					    setSprite(sprite: CanvasNode): void {
 | 
				
			||||||
        sprite.setPosition(this.position);
 | 
					        this.sprite = sprite;
 | 
				
			||||||
 | 
					        sprite.position = this.position.clone();
 | 
				
			||||||
        sprite.setSize(this.size);
 | 
					        sprite.setSize(this.size);
 | 
				
			||||||
        this.children.push(sprite);
 | 
					        this.children.push(sprite);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -46,7 +49,8 @@ export default class Player extends PhysicsNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.move(new Vec2(this.velocity.x * deltaT, this.velocity.y * deltaT));
 | 
							this.move(new Vec2(this.velocity.x * deltaT, this.velocity.y * deltaT));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Debug.log("player", "Player Pos: " + this.position + ", Player Vel: " + this.velocity);
 | 
					        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 {
 | 
					    topdown_computeDirection(): Vec2 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ export default class TilemapFactory {
 | 
				
			||||||
     * @param constr The constructor of the desired tilemap
 | 
					     * @param constr The constructor of the desired tilemap
 | 
				
			||||||
     * @param args Additional arguments to send to the tilemap constructor
 | 
					     * @param args Additional arguments to send to the tilemap constructor
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
	add = (key: string): Array<Layer> => {
 | 
						add = (key: string, scale: Vec2 = new Vec2(1, 1)): Array<Layer> => {
 | 
				
			||||||
        // Get Tilemap Data
 | 
					        // Get Tilemap Data
 | 
				
			||||||
        let tilemapData = this.resourceManager.getTilemap(key);
 | 
					        let tilemapData = this.resourceManager.getTilemap(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,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);
 | 
					                let tilemap = new constr(tilemapData, layer, tilesets, scale);
 | 
				
			||||||
                tilemap.setId(this.scene.generateId());
 | 
					                tilemap.setId(this.scene.generateId());
 | 
				
			||||||
                tilemap.setScene(this.scene);
 | 
					                tilemap.setScene(this.scene);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					@ -107,10 +107,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*4, (obj.y - size.y)*4);
 | 
					                            sprite.setPosition((obj.x + size.x/2)*scale.x, (obj.y - size.y/2)*scale.y);
 | 
				
			||||||
                            sprite.setImageOffset(offset);
 | 
					                            sprite.setImageOffset(offset);
 | 
				
			||||||
                            sprite.setSize(size);
 | 
					                            sprite.setSize(size);
 | 
				
			||||||
                            sprite.setScale(new Vec2(4, 4));
 | 
					                            sprite.setScale(new Vec2(scale.x, scale.y));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,8 +120,8 @@ 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*4, (obj.y - tile.imageheight)*4);
 | 
					                                sprite.setPosition((obj.x + tile.imagewidth/2)*scale.x, (obj.y - tile.imageheight/2)*scale.y);
 | 
				
			||||||
                                sprite.setScale(new Vec2(4, 4));
 | 
					                                sprite.setScale(new Vec2(scale.x, scale.y));
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -129,9 +129,9 @@ export default class TilemapFactory {
 | 
				
			||||||
                    // 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();
 | 
					                        let pos = sprite.getPosition().clone();
 | 
				
			||||||
 | 
					                        let size = sprite.getSize().clone().mult(sprite.getScale());
 | 
				
			||||||
                        pos.x = Math.floor(pos.x);
 | 
					                        pos.x = Math.floor(pos.x);
 | 
				
			||||||
                        pos.y = Math.floor(pos.y);
 | 
					                        pos.y = Math.floor(pos.y);
 | 
				
			||||||
                        let size = sprite.getSize().clone().mult(sprite.getScale());
 | 
					 | 
				
			||||||
                        let staticBody = this.scene.add.physics(StaticBody, sceneLayer, pos, size);
 | 
					                        let staticBody = this.scene.add.physics(StaticBody, sceneLayer, pos, size);
 | 
				
			||||||
                        staticBody.addChild(sprite);
 | 
					                        staticBody.addChild(sprite);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,6 +125,9 @@ export default class Scene{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Render visible set
 | 
					        // Render visible set
 | 
				
			||||||
        visibleSet.forEach(node => node.render(ctx));
 | 
					        visibleSet.forEach(node => node.render(ctx));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Debug render the physicsManager
 | 
				
			||||||
 | 
					        this.physicsManager.render(ctx);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setRunning(running: boolean): void {
 | 
					    setRunning(running: boolean): void {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ export default class SceneManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.resourceManager.unloadAllResources();
 | 
							this.resourceManager.unloadAllResources();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.viewport.setPosition(0, 0);
 | 
							this.viewport.setCenter(0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.addScene(constr);
 | 
							this.addScene(constr);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,14 +56,9 @@ export default class SceneGraphQuadTree extends SceneGraph {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getVisibleSet(): Array<CanvasNode> {
 | 
					    getVisibleSet(): Array<CanvasNode> {
 | 
				
			||||||
        let visibleSet = new Array<CanvasNode>();
 | 
					        let visibleSet = this.qt.queryRegion(this.viewport.getView());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO - Currently just gets all of them
 | 
					        visibleSet = visibleSet.filter(node => !node.getLayer().isHidden());
 | 
				
			||||||
        this.qt.forEach((node: CanvasNode) => {
 | 
					 | 
				
			||||||
            if(!node.getLayer().isHidden() && this.viewport.includes(node)){
 | 
					 | 
				
			||||||
                visibleSet.push(node);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 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) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,11 +4,12 @@ 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 Debug from "../Debug/Debug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Viewport {
 | 
					export default class Viewport {
 | 
				
			||||||
	private position: Vec2;
 | 
					    private view: AABB;
 | 
				
			||||||
	private size: Vec2;
 | 
						private boundary: AABB;
 | 
				
			||||||
	private bounds: Vec4;
 | 
					 | 
				
			||||||
    private following: GameNode;
 | 
					    private following: GameNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -22,9 +23,8 @@ export default class Viewport {
 | 
				
			||||||
    private smoothingFactor: number;
 | 
					    private smoothingFactor: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(){
 | 
					    constructor(){
 | 
				
			||||||
        this.position = new Vec2(0, 0);
 | 
					        this.view = new AABB(Vec2.ZERO, Vec2.ZERO);
 | 
				
			||||||
        this.size = new Vec2(0, 0);
 | 
					        this.boundary = new AABB(Vec2.ZERO, Vec2.ZERO);
 | 
				
			||||||
        this.bounds = new Vec4(0, 0, 0, 0);
 | 
					 | 
				
			||||||
        this.lastPositions = new Queue();
 | 
					        this.lastPositions = new Queue();
 | 
				
			||||||
        this.smoothingFactor = 10;
 | 
					        this.smoothingFactor = 10;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -32,8 +32,19 @@ export default class Viewport {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the position of the viewport as a Vec2
 | 
					     * Returns the position of the viewport as a Vec2
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getPosition(): Vec2 {
 | 
					    getCenter(): Vec2 {
 | 
				
			||||||
        return this.position;
 | 
					        return this.view.getCenter();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getOrigin(): Vec2 {
 | 
				
			||||||
 | 
					        return this.view.getCenter().clone().sub(this.view.getHalfSize())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the region visible to this viewport
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getView(): AABB {
 | 
				
			||||||
 | 
					        return this.view;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -41,7 +52,7 @@ export default class Viewport {
 | 
				
			||||||
     * @param vecOrX 
 | 
					     * @param vecOrX 
 | 
				
			||||||
     * @param y 
 | 
					     * @param y 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    setPosition(vecOrX: Vec2 | number, y: number = null): void {
 | 
					    setCenter(vecOrX: Vec2 | number, y: number = null): void {
 | 
				
			||||||
        let pos: Vec2;
 | 
					        let pos: Vec2;
 | 
				
			||||||
		if(vecOrX instanceof Vec2){
 | 
							if(vecOrX instanceof Vec2){
 | 
				
			||||||
            pos = vecOrX;
 | 
					            pos = vecOrX;
 | 
				
			||||||
| 
						 | 
					@ -56,8 +67,8 @@ export default class Viewport {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the size of the viewport as a Vec2
 | 
					     * Returns the size of the viewport as a Vec2
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getSize(): Vec2{
 | 
					    getHalfSize(): Vec2 {
 | 
				
			||||||
        return this.size;
 | 
					        return this.view.getHalfSize();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -67,9 +78,17 @@ export default class Viewport {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    setSize(vecOrX: Vec2 | number, y: number = null): void {
 | 
					    setSize(vecOrX: Vec2 | number, y: number = null): void {
 | 
				
			||||||
		if(vecOrX instanceof Vec2){
 | 
							if(vecOrX instanceof Vec2){
 | 
				
			||||||
			this.size.set(vecOrX.x, vecOrX.y);
 | 
								this.view.setHalfSize(vecOrX.scaled(1/2));
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this.size.set(vecOrX, y);
 | 
								this.view.setHalfSize(new Vec2(vecOrX/2, y/2));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setHalfSize(vecOrX: Vec2 | number, y: number = null): void {
 | 
				
			||||||
 | 
							if(vecOrX instanceof Vec2){
 | 
				
			||||||
 | 
								this.view.setHalfSize(vecOrX.clone());
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								this.view.setHalfSize(new Vec2(vecOrX, y));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,19 +106,12 @@ export default class Viewport {
 | 
				
			||||||
     * @param node 
 | 
					     * @param node 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    includes(node: CanvasNode): boolean {
 | 
					    includes(node: CanvasNode): boolean {
 | 
				
			||||||
        let nodePos = node.getPosition();
 | 
					 | 
				
			||||||
        let nodeSize = node.getSize();
 | 
					 | 
				
			||||||
        let nodeScale = node.getScale();
 | 
					 | 
				
			||||||
        let parallax = node.getLayer().getParallax();
 | 
					        let parallax = node.getLayer().getParallax();
 | 
				
			||||||
        let originX = this.position.x*parallax.x;
 | 
					        let center = this.view.getCenter().clone();
 | 
				
			||||||
        let originY = this.position.y*parallax.y;
 | 
					        this.view.getCenter().mult(parallax);
 | 
				
			||||||
        if(nodePos.x + nodeSize.x * nodeScale.x > originX && nodePos.x < originX + this.size.x){
 | 
					        let overlaps = this.view.overlaps(node.getBoundary());
 | 
				
			||||||
            if(nodePos.y + nodeSize.y * nodeScale.y > originY && nodePos.y < originY + this.size.y){
 | 
					        this.view.setCenter(center);
 | 
				
			||||||
                return true;
 | 
					        return overlaps;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: Put some error handling on this for trying to make the bounds too small for the viewport
 | 
						// TODO: Put some error handling on this for trying to make the bounds too small for the viewport
 | 
				
			||||||
| 
						 | 
					@ -112,7 +124,12 @@ export default class Viewport {
 | 
				
			||||||
     * @param upperY 
 | 
					     * @param upperY 
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    setBounds(lowerX: number, lowerY: number, upperX: number, upperY: number): void {
 | 
					    setBounds(lowerX: number, lowerY: number, upperX: number, upperY: number): void {
 | 
				
			||||||
        this.bounds = new Vec4(lowerX, lowerY, upperX, upperY);
 | 
					        let hwidth = (upperX - lowerX)/2;
 | 
				
			||||||
 | 
					        let hheight = (upperY - lowerY)/2;
 | 
				
			||||||
 | 
					        let x = lowerX + hwidth;
 | 
				
			||||||
 | 
					        let y = lowerY + hheight;
 | 
				
			||||||
 | 
					        this.boundary.setCenter(new Vec2(x, y));
 | 
				
			||||||
 | 
					        this.boundary.setHalfSize(new Vec2(hwidth, hheight));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -138,11 +155,10 @@ export default class Viewport {
 | 
				
			||||||
            pos.scale(1/this.lastPositions.getSize());
 | 
					            pos.scale(1/this.lastPositions.getSize());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Set this position either to the object or to its bounds
 | 
					            // Set this position either to the object or to its bounds
 | 
				
			||||||
            this.position.x = pos.x - this.size.x/2;
 | 
					            pos.x = MathUtils.clamp(pos.x, this.boundary.left + this.view.hw, this.boundary.right - this.view.hw);
 | 
				
			||||||
            this.position.y = pos.y - this.size.y/2;
 | 
					            pos.y = MathUtils.clamp(pos.y, this.boundary.top + this.view.hh, this.boundary.bottom - this.view.hh);
 | 
				
			||||||
            let [min, max] = this.bounds.split();
 | 
					
 | 
				
			||||||
            this.position.x = MathUtils.clamp(this.position.x, min.x, max.x - this.size.x);
 | 
					            this.view.setCenter(pos);
 | 
				
			||||||
            this.position.y = MathUtils.clamp(this.position.y, min.y, max.y - this.size.y);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,14 @@
 | 
				
			||||||
import GameLoop from "./Loop/GameLoop";
 | 
					import GameLoop from "./Loop/GameLoop";
 | 
				
			||||||
import {} from "./index";
 | 
					import {} from "./index";
 | 
				
			||||||
 | 
					import MainScene from "./MainScene"
 | 
				
			||||||
import QuadTreeScene from "./QuadTreeScene";
 | 
					import QuadTreeScene from "./QuadTreeScene";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function main(){
 | 
					function main(){
 | 
				
			||||||
    // Create the game object
 | 
					    // Create the game object
 | 
				
			||||||
    let game = new GameLoop({viewportSize: {x: 500, y: 500}});
 | 
					    let game = new GameLoop({viewportSize: {x: 800, y: 600}});
 | 
				
			||||||
    game.start();
 | 
					    game.start();
 | 
				
			||||||
    let sm = game.getSceneManager();
 | 
					    let sm = game.getSceneManager();
 | 
				
			||||||
    sm.addScene(QuadTreeScene);
 | 
					    sm.addScene(MainScene);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user