transferred game engine code to typescript
This commit is contained in:
		
							parent
							
								
									e5d0678b5d
								
							
						
					
					
						commit
						40a05fdbdb
					
				
							
								
								
									
										3
									
								
								src/DataTypes/Collection.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/DataTypes/Collection.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					export default interface Collection{
 | 
				
			||||||
 | 
						forEach(func: Function): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/DataTypes/Map.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/DataTypes/Map.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					import Collection from "./Collection";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Map<T> implements Collection{
 | 
				
			||||||
 | 
						private map: Record<string, T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							this.map = {};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						add(key: string, value: T): void {
 | 
				
			||||||
 | 
							this.map[key] = value;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get(key: string): T {
 | 
				
			||||||
 | 
							return this.map[key];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set(key: string, value: T): void {
 | 
				
			||||||
 | 
							this.add(key, value);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						has(key: string): boolean {
 | 
				
			||||||
 | 
							return this.map[key] !== undefined;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						keys(): Array<string> {
 | 
				
			||||||
 | 
							return Object.keys(this.map);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						forEach(func: Function): void {
 | 
				
			||||||
 | 
							Object.keys(this.map).forEach(key => func(key));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/DataTypes/Queue.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/DataTypes/Queue.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,61 @@
 | 
				
			||||||
 | 
					import Collection from "./Collection";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Queue<T> implements Collection{
 | 
				
			||||||
 | 
						readonly MAX_ELEMENTS: number;
 | 
				
			||||||
 | 
						private q: Array<T>;
 | 
				
			||||||
 | 
						private head: number;
 | 
				
			||||||
 | 
						private tail: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(maxElements: number = 100){
 | 
				
			||||||
 | 
					        this.MAX_ELEMENTS = maxElements;
 | 
				
			||||||
 | 
					        this.q = new Array(this.MAX_ELEMENTS);
 | 
				
			||||||
 | 
					        this.head = 0;
 | 
				
			||||||
 | 
					        this.tail = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enqueue(item: T): void{
 | 
				
			||||||
 | 
					        if((this.tail + 1) % this.MAX_ELEMENTS === this.head){
 | 
				
			||||||
 | 
					            throw "Queue full - cannot add element"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.q[this.tail] = item;
 | 
				
			||||||
 | 
					        this.tail = (this.tail + 1) % this.MAX_ELEMENTS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dequeue(): T {
 | 
				
			||||||
 | 
					        if(this.head === this.tail){
 | 
				
			||||||
 | 
					            throw "Queue empty - cannot remove element"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let item = this.q[this.head];
 | 
				
			||||||
 | 
					        this.head = (this.head + 1) % this.MAX_ELEMENTS;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return item;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    peekNext(): T {
 | 
				
			||||||
 | 
					        if(this.head === this.tail){
 | 
				
			||||||
 | 
					            throw "Queue empty - cannot get element"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let item = this.q[this.head];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return item;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hasItems(): boolean {
 | 
				
			||||||
 | 
					        return this.head !== this.tail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clear(): void {
 | 
				
			||||||
 | 
					        this.head = this.tail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    forEach(func: Function): void {
 | 
				
			||||||
 | 
					        let i = this.head;
 | 
				
			||||||
 | 
					        while(i !== this.tail){
 | 
				
			||||||
 | 
					            func(this.q[i]);
 | 
				
			||||||
 | 
					            i = (i + 1) % this.MAX_ELEMENTS;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/DataTypes/Stack.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/DataTypes/Stack.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					import Collection from "./Collection";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Stack<T> implements Collection{
 | 
				
			||||||
 | 
						readonly MAX_ELEMENTS: number;
 | 
				
			||||||
 | 
						private stack: Array<T>;
 | 
				
			||||||
 | 
						private head: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(maxElements: number = 100){
 | 
				
			||||||
 | 
					        this.MAX_ELEMENTS = maxElements;
 | 
				
			||||||
 | 
					        this.stack = new Array<T>(this.MAX_ELEMENTS);
 | 
				
			||||||
 | 
					        this.head = -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Adds an item to the top of the stack
 | 
				
			||||||
 | 
					     * @param {*} item The new item to add to the stack
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    push(item: T): void {
 | 
				
			||||||
 | 
					        if(this.head + 1 === this.MAX_ELEMENTS){
 | 
				
			||||||
 | 
					            throw "Stack full - cannot add element";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.head += 1;
 | 
				
			||||||
 | 
					        this.stack[this.head] = item;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Removes an item from the top of the stack
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    pop(): T{
 | 
				
			||||||
 | 
					        if(this.head === -1){
 | 
				
			||||||
 | 
					            throw "Stack empty - cannot remove element";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.head -= 1;
 | 
				
			||||||
 | 
					        return this.stack[this.head + 1];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Removes all elements from the stack
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    clear(): void{
 | 
				
			||||||
 | 
					        this.head = -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the element currently at the top of the stack
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    peek(): T {
 | 
				
			||||||
 | 
					        if(this.head === -1){
 | 
				
			||||||
 | 
					            throw "Stack empty - cannot get element";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return this.stack[this.head];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of items currently in the stack
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    size(): number {
 | 
				
			||||||
 | 
					        return this.head + 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    forEach(func: Function): void{
 | 
				
			||||||
 | 
					        let i = 0;
 | 
				
			||||||
 | 
					        while(i <= this.head){
 | 
				
			||||||
 | 
					            func(this.stack[i]);
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										79
									
								
								src/DataTypes/Vec2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/DataTypes/Vec2.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,79 @@
 | 
				
			||||||
 | 
					export default class Vec2{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public x : number;
 | 
				
			||||||
 | 
						public y : number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(x : number = 0, y : number = 0){
 | 
				
			||||||
 | 
							this.x = x;
 | 
				
			||||||
 | 
							this.y = y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						magSq() : number{
 | 
				
			||||||
 | 
							return this.x*this.x + this.y*this.y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mag() : number {
 | 
				
			||||||
 | 
							return Math.sqrt(this.magSq());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						normalize() : Vec2{
 | 
				
			||||||
 | 
							if(this.x === 0 && this.y === 0) return this;
 | 
				
			||||||
 | 
							let mag = this.mag();
 | 
				
			||||||
 | 
							this.x /= mag;
 | 
				
			||||||
 | 
							this.y /= mag;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setToAngle(angle : number) : Vec2{
 | 
				
			||||||
 | 
							this.x = Math.cos(angle);
 | 
				
			||||||
 | 
							this.y = Math.sin(angle);
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scaleTo(magnitude : number) : Vec2{
 | 
				
			||||||
 | 
							return this.normalize().scale(magnitude);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scale(factor : number, yFactor : number = null) : Vec2{
 | 
				
			||||||
 | 
							if(yFactor !== null){
 | 
				
			||||||
 | 
								this.x *= factor;
 | 
				
			||||||
 | 
								this.y *= yFactor;
 | 
				
			||||||
 | 
								return this;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.x *= factor;
 | 
				
			||||||
 | 
							this.y *= factor;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rotate(angle : number) : Vec2{
 | 
				
			||||||
 | 
							let cs = Math.cos(angle);
 | 
				
			||||||
 | 
							let sn = Math.sin(angle);
 | 
				
			||||||
 | 
							let tempX = this.x*cs - this.y*sn;
 | 
				
			||||||
 | 
							let tempY = this.x*sn + this.y*cs;
 | 
				
			||||||
 | 
							this.x = tempX;
 | 
				
			||||||
 | 
							this.y = tempY;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set(x : number, y : number) : Vec2{
 | 
				
			||||||
 | 
							this.x = x;
 | 
				
			||||||
 | 
							this.y = y;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						add(other : Vec2) : Vec2{
 | 
				
			||||||
 | 
							this.x += other.x;
 | 
				
			||||||
 | 
							this.y += other.y;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sub(other : Vec2) : Vec2{
 | 
				
			||||||
 | 
							this.x -= other.x;
 | 
				
			||||||
 | 
							this.y -= other.y;
 | 
				
			||||||
 | 
							return this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						toString() : string{
 | 
				
			||||||
 | 
							return "(" + this.x + ", " + this.y + ")";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/DataTypes/Vec4.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/DataTypes/Vec4.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					import Vec2 from "./Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Vec4{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public x : number;
 | 
				
			||||||
 | 
						public y : number;
 | 
				
			||||||
 | 
						public z : number;
 | 
				
			||||||
 | 
						public w : number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(x : number = 0, y : number = 0, z : number = 0, w : number = 0){
 | 
				
			||||||
 | 
					        this.x = x;
 | 
				
			||||||
 | 
					        this.y = y;
 | 
				
			||||||
 | 
					        this.z = z;
 | 
				
			||||||
 | 
					        this.w = w;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    split() : [Vec2, Vec2]{
 | 
				
			||||||
 | 
					        return [new Vec2(this.x, this.y), new Vec2(this.z, this.w)];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/Events/EventQueue.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/Events/EventQueue.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,66 @@
 | 
				
			||||||
 | 
					import Queue from "../DataTypes/Queue";
 | 
				
			||||||
 | 
					import Map from "../DataTypes/Map";
 | 
				
			||||||
 | 
					import GameEvent from "./GameEvent";
 | 
				
			||||||
 | 
					import Receiver from "./Receiver";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class EventQueue {
 | 
				
			||||||
 | 
						private static instance: EventQueue = null;
 | 
				
			||||||
 | 
						private readonly MAX_SIZE: number;
 | 
				
			||||||
 | 
						private q: Queue<GameEvent>;
 | 
				
			||||||
 | 
						private receivers: Map<Array<Receiver>>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private constructor(){
 | 
				
			||||||
 | 
					        this.MAX_SIZE = 100;
 | 
				
			||||||
 | 
					        this.q = new Queue<GameEvent>(this.MAX_SIZE);
 | 
				
			||||||
 | 
					        this.receivers = new Map<Array<Receiver>>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						static getInstance(): EventQueue {
 | 
				
			||||||
 | 
							if(this.instance === null){
 | 
				
			||||||
 | 
								this.instance = new EventQueue();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							return this.instance;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addEvent(event: GameEvent): void {
 | 
				
			||||||
 | 
					        this.q.enqueue(event);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subscribe(receiver: Receiver, type: string | Array<string>): void {
 | 
				
			||||||
 | 
					        if(type instanceof Array){
 | 
				
			||||||
 | 
					            // If it is an array, subscribe to all event types
 | 
				
			||||||
 | 
					            for(let t of type){
 | 
				
			||||||
 | 
					                this.addListener(receiver, t);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.addListener(receiver, type);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private addListener(receiver: Receiver, type: string): void {
 | 
				
			||||||
 | 
							if(this.receivers.has(type)){
 | 
				
			||||||
 | 
								this.receivers.get(type).push(receiver);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								this.receivers.add(type, [receiver]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					    update(deltaT: number): void{
 | 
				
			||||||
 | 
					        while(this.q.hasItems()){
 | 
				
			||||||
 | 
								let event = this.q.dequeue();
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
					            if(this.receivers.has(event.type)){
 | 
				
			||||||
 | 
					                for(let receiver of this.receivers.get(event.type)){
 | 
				
			||||||
 | 
					                    receiver.receive(event);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
					            if(this.receivers.has("all")){
 | 
				
			||||||
 | 
					                for(let receiver of this.receivers.get("all")){
 | 
				
			||||||
 | 
					                    receiver.receive(event);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/Events/GameEvent.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Events/GameEvent.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					import Map from "../DataTypes/Map"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class GameEvent{
 | 
				
			||||||
 | 
						public type: string;
 | 
				
			||||||
 | 
						public data: Map<any>;
 | 
				
			||||||
 | 
						public time: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(type: string, data: Map<any> | Record<string, any> = null){
 | 
				
			||||||
 | 
					        if (data === null) {
 | 
				
			||||||
 | 
					            this.data = new Map<any>();
 | 
				
			||||||
 | 
					        } else if (!(data instanceof Map)){
 | 
				
			||||||
 | 
					            // data is a raw object, unpack
 | 
				
			||||||
 | 
					            this.data = new Map<any>();
 | 
				
			||||||
 | 
					            for(let key in data){
 | 
				
			||||||
 | 
					                this.data.add(key, data[key]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.data = data;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.type = type;
 | 
				
			||||||
 | 
					        this.time = Date.now();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    toString(): string {
 | 
				
			||||||
 | 
					        return this.type + ": @" + this.time;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/Events/Receiver.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Events/Receiver.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					import Queue from "../DataTypes/Queue";
 | 
				
			||||||
 | 
					import GameEvent from "./GameEvent";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Receiver{
 | 
				
			||||||
 | 
						readonly MAX_SIZE: number;
 | 
				
			||||||
 | 
						private q: Queue<GameEvent>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							this.MAX_SIZE = 100;
 | 
				
			||||||
 | 
					        this.q = new Queue(this.MAX_SIZE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						receive(event: GameEvent): void {
 | 
				
			||||||
 | 
							this.q.enqueue(event);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getNextEvent(): GameEvent {
 | 
				
			||||||
 | 
							return this.q.dequeue();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						peekNextEvent(): GameEvent {
 | 
				
			||||||
 | 
							return this.q.peekNext()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hasNextEvent(): boolean {
 | 
				
			||||||
 | 
							return this.q.hasItems();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ignoreEvents(): void {
 | 
				
			||||||
 | 
							this.q.clear();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/GameState/GameState.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/GameState/GameState.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					import Stack from "../DataTypes/Stack";
 | 
				
			||||||
 | 
					import Scene from "./Scene";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class GameState{
 | 
				
			||||||
 | 
						private sceneStack: Stack<Scene>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(){
 | 
				
			||||||
 | 
					        this.sceneStack = new Stack(10);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addScene(scene: Scene, pauseScenesBelow: boolean = true): void {
 | 
				
			||||||
 | 
					        this.sceneStack.forEach((scene: Scene) => scene.setPaused(pauseScenesBelow));
 | 
				
			||||||
 | 
					        this.sceneStack.push(scene);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    removeScene(startNewTopScene: boolean = true): void {
 | 
				
			||||||
 | 
					        this.sceneStack.pop();
 | 
				
			||||||
 | 
					        this.sceneStack.peek().setPaused(!startNewTopScene);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    changeScene(scene: Scene): void {
 | 
				
			||||||
 | 
					        this.sceneStack.clear();
 | 
				
			||||||
 | 
					        this.sceneStack.push(scene);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
 | 
					        this.sceneStack.forEach((scene: Scene) => scene.update(deltaT));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
 | 
					        this.sceneStack.forEach((scene: Scene) => scene.render(ctx));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										56
									
								
								src/GameState/Scene.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/GameState/Scene.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import Viewport from "../SceneGraph/Viewport";
 | 
				
			||||||
 | 
					import SceneGraph from "../SceneGraph/SceneGraph";
 | 
				
			||||||
 | 
					import SceneGraphArray from "../SceneGraph/SceneGraphArray";
 | 
				
			||||||
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Scene{
 | 
				
			||||||
 | 
						private viewport: Viewport
 | 
				
			||||||
 | 
						private worldSize: Vec2;
 | 
				
			||||||
 | 
						private sceneGraph: SceneGraph;
 | 
				
			||||||
 | 
						private paused: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(){
 | 
				
			||||||
 | 
					        this.viewport = new Viewport();
 | 
				
			||||||
 | 
					        this.viewport.setSize(800, 500);
 | 
				
			||||||
 | 
					        // TODO: Find a way to make this not a hard-coded value
 | 
				
			||||||
 | 
					        this.worldSize = new Vec2(1600, 1000);
 | 
				
			||||||
 | 
					        this.viewport.setBounds(0, 0, 1600, 1000);
 | 
				
			||||||
 | 
					        this.sceneGraph = new SceneGraphArray(this.viewport);
 | 
				
			||||||
 | 
					        this.paused = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setPaused(pauseValue: boolean): void {
 | 
				
			||||||
 | 
					        this.paused = pauseValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    isPaused(): boolean {
 | 
				
			||||||
 | 
					        return this.paused;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    getViewport(): Viewport {
 | 
				
			||||||
 | 
					        return this.viewport;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    add(children: Array<GameNode> | GameNode): void {
 | 
				
			||||||
 | 
					        if(children instanceof Array){
 | 
				
			||||||
 | 
					            for(let child of children){
 | 
				
			||||||
 | 
					                this.sceneGraph.addNode(child);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.sceneGraph.addNode(children);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
 | 
					        if(!this.paused){
 | 
				
			||||||
 | 
					            this.viewport.update(deltaT);
 | 
				
			||||||
 | 
					            this.sceneGraph.update(deltaT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
 | 
					        let visibleSet = this.sceneGraph.getVisibleSet();
 | 
				
			||||||
 | 
					        visibleSet.forEach(node => node.render(ctx, this.viewport.getPosition(), this.viewport.getSize()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/Input/InputHandler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Input/InputHandler.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,51 @@
 | 
				
			||||||
 | 
					import EventQueue from "../Events/EventQueue";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import GameEvent from "../Events/GameEvent";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class InputHandler{
 | 
				
			||||||
 | 
						private eventQueue: EventQueue;
 | 
				
			||||||
 | 
						 
 | 
				
			||||||
 | 
					    constructor(canvas: HTMLCanvasElement){
 | 
				
			||||||
 | 
							this.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					        canvas.onmousedown = (event) => this.handleMouseDown(event, canvas);
 | 
				
			||||||
 | 
					        canvas.onmouseup = (event) => this.handleMouseUp(event, canvas);
 | 
				
			||||||
 | 
					        document.onkeydown = this.handleKeyDown;
 | 
				
			||||||
 | 
					        document.onkeyup = this.handleKeyUp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handleMouseDown = (event: MouseEvent, canvas: HTMLCanvasElement): void => {
 | 
				
			||||||
 | 
							let pos = this.getMousePosition(event, canvas);
 | 
				
			||||||
 | 
					        let gameEvent = new GameEvent("mouse_down", {position: pos});
 | 
				
			||||||
 | 
					        this.eventQueue.addEvent(gameEvent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handleMouseUp = (event: MouseEvent, canvas: HTMLCanvasElement): void => {
 | 
				
			||||||
 | 
					        let pos = this.getMousePosition(event, canvas);
 | 
				
			||||||
 | 
					        let gameEvent = new GameEvent("mouse_up", {position: pos});
 | 
				
			||||||
 | 
					        this.eventQueue.addEvent(gameEvent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handleKeyDown = (event: KeyboardEvent): void => {
 | 
				
			||||||
 | 
					        let key = this.getKey(event);
 | 
				
			||||||
 | 
					        let gameEvent = new GameEvent("key_down", {key: key});
 | 
				
			||||||
 | 
					        this.eventQueue.addEvent(gameEvent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handleKeyUp = (event: KeyboardEvent): void => {
 | 
				
			||||||
 | 
					        let key = this.getKey(event);
 | 
				
			||||||
 | 
					        let gameEvent = new GameEvent("key_up", {key: key});
 | 
				
			||||||
 | 
					        this.eventQueue.addEvent(gameEvent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private getKey(keyEvent: KeyboardEvent){
 | 
				
			||||||
 | 
					        return keyEvent.key.toLowerCase();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private getMousePosition(mouseEvent: MouseEvent, canvas: HTMLCanvasElement): Vec2 {
 | 
				
			||||||
 | 
					        let rect = canvas.getBoundingClientRect();
 | 
				
			||||||
 | 
					        let x = mouseEvent.clientX - rect.left;
 | 
				
			||||||
 | 
					        let y = mouseEvent.clientY - rect.top;
 | 
				
			||||||
 | 
					        return new Vec2(x, y);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/Input/InputReceiver.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/Input/InputReceiver.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,93 @@
 | 
				
			||||||
 | 
					import Receiver from "../Events/Receiver";
 | 
				
			||||||
 | 
					import Map from "../DataTypes/Map";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import EventQueue from "../Events/EventQueue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class InputReceiver{
 | 
				
			||||||
 | 
						private static instance: InputReceiver = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private mousePressed: boolean;
 | 
				
			||||||
 | 
						private mouseJustPressed: boolean;
 | 
				
			||||||
 | 
						private keyJustPressed: Map<boolean>;
 | 
				
			||||||
 | 
						private keyPressed: Map<boolean>;
 | 
				
			||||||
 | 
						private mousePressPosition: Vec2;
 | 
				
			||||||
 | 
						private eventQueue: EventQueue;
 | 
				
			||||||
 | 
						private receiver: Receiver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private constructor(){
 | 
				
			||||||
 | 
							this.mousePressed = false;
 | 
				
			||||||
 | 
							this.mouseJustPressed = false;
 | 
				
			||||||
 | 
							this.receiver = new Receiver();
 | 
				
			||||||
 | 
							this.keyJustPressed = new Map<boolean>();
 | 
				
			||||||
 | 
							this.keyPressed = new Map<boolean>();
 | 
				
			||||||
 | 
							this.mousePressPosition = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
 | 
							this.eventQueue.subscribe(this.receiver, ["mouse_down", "mouse_up", "key_down", "key_up"]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static getInstance(): InputReceiver{
 | 
				
			||||||
 | 
							if(this.instance === null){
 | 
				
			||||||
 | 
								this.instance = new InputReceiver();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return this.instance;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						update(deltaT: number): void {
 | 
				
			||||||
 | 
							// Reset the justPressed values to false
 | 
				
			||||||
 | 
							this.mouseJustPressed = false;
 | 
				
			||||||
 | 
							this.keyJustPressed.forEach((key: string) => this.keyJustPressed.set(key, false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while(this.receiver.hasNextEvent()){
 | 
				
			||||||
 | 
								let event = this.receiver.getNextEvent();
 | 
				
			||||||
 | 
								if(event.type === "mouse_down"){
 | 
				
			||||||
 | 
									this.mouseJustPressed = true;
 | 
				
			||||||
 | 
									this.mousePressed = true;
 | 
				
			||||||
 | 
									this.mousePressPosition = event.data.get("position");	
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(event.type === "mouse_up"){
 | 
				
			||||||
 | 
									this.mousePressed = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(event.type === "key_down"){
 | 
				
			||||||
 | 
									let key = event.data.get("key")
 | 
				
			||||||
 | 
									this.keyJustPressed.set(key, true);
 | 
				
			||||||
 | 
									this.keyPressed.set(key, true);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(event.type === "key_up"){
 | 
				
			||||||
 | 
									let key = event.data.get("key")
 | 
				
			||||||
 | 
									this.keyPressed.set(key, false);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isJustPressed(key: string): boolean {
 | 
				
			||||||
 | 
							if(this.keyJustPressed.has(key)){
 | 
				
			||||||
 | 
								return this.keyJustPressed.get(key)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isPressed(key: string): boolean {
 | 
				
			||||||
 | 
							if(this.keyPressed.has(key)){
 | 
				
			||||||
 | 
								return this.keyPressed.get(key)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isMouseJustPressed(): boolean {
 | 
				
			||||||
 | 
							return this.mouseJustPressed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isMousePressed(): boolean {
 | 
				
			||||||
 | 
							return this.mousePressed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getMousePressPosition(): Vec2 {
 | 
				
			||||||
 | 
							return this.mousePressPosition;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										153
									
								
								src/Loop/GameLoop.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/Loop/GameLoop.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,153 @@
 | 
				
			||||||
 | 
					import EventQueue from "../Events/EventQueue";
 | 
				
			||||||
 | 
					import InputReceiver from "../Input/InputReceiver";
 | 
				
			||||||
 | 
					import InputHandler from "../Input/InputHandler";
 | 
				
			||||||
 | 
					import Recorder from "../Playback/Recorder";
 | 
				
			||||||
 | 
					import GameState from "../GameState/GameState";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class GameLoop{
 | 
				
			||||||
 | 
						// The amount of time to spend on a physics step
 | 
				
			||||||
 | 
						private maxFPS: number;
 | 
				
			||||||
 | 
						private simulationTimestep: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The time when the last frame was drawn
 | 
				
			||||||
 | 
						private lastFrameTime: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The current frame of the game
 | 
				
			||||||
 | 
						private frame: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Keeping track of the fps
 | 
				
			||||||
 | 
						private runningFrameSum: number;
 | 
				
			||||||
 | 
						private numFramesInSum: number;
 | 
				
			||||||
 | 
						private maxFramesInSum: number;
 | 
				
			||||||
 | 
						private fps: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private started: boolean;
 | 
				
			||||||
 | 
						private running: boolean;
 | 
				
			||||||
 | 
						private frameDelta: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readonly GAME_CANVAS: HTMLCanvasElement;
 | 
				
			||||||
 | 
						readonly WIDTH: number;
 | 
				
			||||||
 | 
						readonly HEIGHT: number;
 | 
				
			||||||
 | 
						private ctx: CanvasRenderingContext2D;
 | 
				
			||||||
 | 
						private eventQueue: EventQueue;
 | 
				
			||||||
 | 
						private inputHandler: InputHandler;
 | 
				
			||||||
 | 
						private inputReceiver: InputReceiver;
 | 
				
			||||||
 | 
						private recorder: Recorder;
 | 
				
			||||||
 | 
						private gameState: GameState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(){
 | 
				
			||||||
 | 
					        this.maxFPS = 60;
 | 
				
			||||||
 | 
					        this.simulationTimestep = Math.floor(1000/this.maxFPS);
 | 
				
			||||||
 | 
					        this.frame = 0;
 | 
				
			||||||
 | 
					        this.runningFrameSum = 0;
 | 
				
			||||||
 | 
					        this.numFramesInSum = 0;
 | 
				
			||||||
 | 
					        this.maxFramesInSum = 30;
 | 
				
			||||||
 | 
					        this.fps = this.maxFPS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.started = false;
 | 
				
			||||||
 | 
					        this.running = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.GAME_CANVAS = document.getElementById("game-canvas") as HTMLCanvasElement;
 | 
				
			||||||
 | 
					        this.GAME_CANVAS.style.setProperty("background-color", "whitesmoke");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        this.WIDTH = 800;
 | 
				
			||||||
 | 
					        this.HEIGHT = 500;
 | 
				
			||||||
 | 
					        this.ctx = this.initializeCanvas(this.GAME_CANVAS, this.WIDTH, this.HEIGHT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
 | 
					        this.inputHandler = new InputHandler(this.GAME_CANVAS);
 | 
				
			||||||
 | 
					        this.inputReceiver = InputReceiver.getInstance();
 | 
				
			||||||
 | 
					        this.recorder = new Recorder();
 | 
				
			||||||
 | 
					        this.gameState = new GameState();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
 | 
				
			||||||
 | 
					        canvas.width = width;
 | 
				
			||||||
 | 
					        canvas.height = height;
 | 
				
			||||||
 | 
					        return canvas.getContext("2d");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setMaxFPS(initMax: number): void {
 | 
				
			||||||
 | 
					        this.maxFPS = initMax;
 | 
				
			||||||
 | 
					        this.simulationTimestep = Math.floor(1000/this.maxFPS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getGameState(): GameState {
 | 
				
			||||||
 | 
					        return this.gameState;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private renderFPS(ctx: CanvasRenderingContext2D): void {
 | 
				
			||||||
 | 
					        ctx.fillStyle = "black";
 | 
				
			||||||
 | 
							ctx.font = "30px Arial"
 | 
				
			||||||
 | 
							ctx.fillText(this.fps.toFixed(1), 5, 5 + 30);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private updateFrameCount(timestep: number): void {
 | 
				
			||||||
 | 
					        this.frame += 1;
 | 
				
			||||||
 | 
					        this.numFramesInSum += 1;
 | 
				
			||||||
 | 
					        this.runningFrameSum += timestep;
 | 
				
			||||||
 | 
					        if(this.numFramesInSum >= this.maxFramesInSum){
 | 
				
			||||||
 | 
					            this.fps = 1000 * this.numFramesInSum / this.runningFrameSum;
 | 
				
			||||||
 | 
					            this.numFramesInSum = 0;
 | 
				
			||||||
 | 
					            this.runningFrameSum = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    start(): void {
 | 
				
			||||||
 | 
					        if(!this.started){
 | 
				
			||||||
 | 
					            this.started = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            window.requestAnimationFrame(this.startFrame);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    startFrame = (timestamp: number): void => {
 | 
				
			||||||
 | 
					        this.running = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.lastFrameTime = timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        window.requestAnimationFrame(this.doFrame);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    doFrame = (timestamp: number): void => {
 | 
				
			||||||
 | 
					        // Request animation frame to prepare for another update or render
 | 
				
			||||||
 | 
					        window.requestAnimationFrame(this.doFrame);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If we are trying to update too soon, return and do nothing
 | 
				
			||||||
 | 
					        if(timestamp < this.lastFrameTime + this.simulationTimestep){
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Currently, update and draw are synced - eventually it would probably be good to desync these
 | 
				
			||||||
 | 
					        this.frameDelta = timestamp - this.lastFrameTime;
 | 
				
			||||||
 | 
					        this.lastFrameTime = timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Update while we can (This will present problems if we leave the window)
 | 
				
			||||||
 | 
					        let i = 0;
 | 
				
			||||||
 | 
					        while(this.frameDelta >= this.simulationTimestep){
 | 
				
			||||||
 | 
					            this.update(this.simulationTimestep/1000);
 | 
				
			||||||
 | 
					            this.frameDelta -= this.simulationTimestep;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Update the frame of the game
 | 
				
			||||||
 | 
					            this.updateFrameCount(this.simulationTimestep);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Updates are done, draw
 | 
				
			||||||
 | 
					        this.render();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
 | 
					        this.eventQueue.update(deltaT);
 | 
				
			||||||
 | 
					        this.inputReceiver.update(deltaT);
 | 
				
			||||||
 | 
					        this.recorder.update(deltaT);
 | 
				
			||||||
 | 
					        this.gameState.update(deltaT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render(): void {
 | 
				
			||||||
 | 
					        this.ctx.clearRect(0, 0, this.WIDTH, this.HEIGHT);
 | 
				
			||||||
 | 
					        this.gameState.render(this.ctx);
 | 
				
			||||||
 | 
					        this.renderFPS(this.ctx);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/Nodes/ColoredCircle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Nodes/ColoredCircle.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					import GameNode from "./GameNode";
 | 
				
			||||||
 | 
					import Color from "../Utils/Color";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import RandUtils from "../Utils/RandUtils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class ColoredCircle extends GameNode{
 | 
				
			||||||
 | 
						private color: Color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(){
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.position = new Vec2(RandUtils.randInt(0, 1000), RandUtils.randInt(0, 1000));
 | 
				
			||||||
 | 
					        this.color = RandUtils.randColor();
 | 
				
			||||||
 | 
					        console.log(this.color.toStringRGB());
 | 
				
			||||||
 | 
					        this.size = new Vec2(50, 50);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render(ctx: CanvasRenderingContext2D, viewportOrigin: Vec2, viewportSize: Vec2){
 | 
				
			||||||
 | 
					        ctx.fillStyle = this.color.toStringRGB();
 | 
				
			||||||
 | 
					        ctx.beginPath();
 | 
				
			||||||
 | 
					        ctx.arc(this.position.x + this.size.x/2 - viewportOrigin.x, this.position.y + this.size.y/2 - viewportOrigin.y, this.size.x/2, 0, Math.PI*2, false);
 | 
				
			||||||
 | 
					        ctx.fill();
 | 
				
			||||||
 | 
					        ctx.closePath();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/Nodes/GameNode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/Nodes/GameNode.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					import EventQueue from "../Events/EventQueue";
 | 
				
			||||||
 | 
					import InputReceiver from "../Input/InputReceiver";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class GameNode{
 | 
				
			||||||
 | 
						protected eventQueue: EventQueue;
 | 
				
			||||||
 | 
						protected input: InputReceiver;
 | 
				
			||||||
 | 
						protected position: Vec2;
 | 
				
			||||||
 | 
						protected size: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							this.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
 | 
							this.input = InputReceiver.getInstance();
 | 
				
			||||||
 | 
							this.position = new Vec2(0, 0);
 | 
				
			||||||
 | 
							this.size = new Vec2(0, 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						getPosition(): Vec2 {
 | 
				
			||||||
 | 
							return this.position;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getSize(): Vec2 {
 | 
				
			||||||
 | 
							return this.size;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contains(x: number, y: number): boolean {
 | 
				
			||||||
 | 
							if(x > this.position.x && x < this.position.x + this.size.x){
 | 
				
			||||||
 | 
								if(y > this.position.y && y < this.position.y + this.size.y){
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						abstract update(deltaT: number): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						abstract render(ctx: CanvasRenderingContext2D, viewportOrigin: Vec2, viewportSize: Vec2): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/Nodes/Player.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Nodes/Player.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					import GameNode from "./GameNode";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Player extends GameNode{
 | 
				
			||||||
 | 
						velocity: Vec2;
 | 
				
			||||||
 | 
						speed: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
							this.velocity = new Vec2(0, 0);
 | 
				
			||||||
 | 
							this.speed = 300;
 | 
				
			||||||
 | 
							this.size = new Vec2(50, 50);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						update(deltaT: number): void {
 | 
				
			||||||
 | 
							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('s') ? 1 : 0;
 | 
				
			||||||
 | 
							dir.y += this.input.isPressed('w') ? -1 : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dir.normalize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.velocity = dir.scale(this.speed);
 | 
				
			||||||
 | 
							this.position = this.position.add(this.velocity.scale(deltaT));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						render(ctx: CanvasRenderingContext2D, viewportOrigin: Vec2, viewportSize: Vec2){
 | 
				
			||||||
 | 
							ctx.fillStyle = "#FF0000";
 | 
				
			||||||
 | 
							ctx.fillRect(this.position.x - viewportOrigin.x, this.position.y - viewportOrigin.y, this.size.x, this.size.y);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										89
									
								
								src/Nodes/UIElement.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Nodes/UIElement.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					import GameNode from "./GameNode";
 | 
				
			||||||
 | 
					import Color from "../Utils/Color";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import GameEvent from "../Events/GameEvent";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class UIElement extends GameNode{
 | 
				
			||||||
 | 
						parent: GameNode;
 | 
				
			||||||
 | 
						children: Array<GameNode>;
 | 
				
			||||||
 | 
						text: string;
 | 
				
			||||||
 | 
						backgroundColor: Color;
 | 
				
			||||||
 | 
						textColor: Color;
 | 
				
			||||||
 | 
						onPress: Function;
 | 
				
			||||||
 | 
						onPressSignal: string;
 | 
				
			||||||
 | 
						onHover: Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							super();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							this.parent = null;
 | 
				
			||||||
 | 
							this.children = [];
 | 
				
			||||||
 | 
							this.text = "";
 | 
				
			||||||
 | 
							this.backgroundColor = new Color(0, 0, 0, 0);
 | 
				
			||||||
 | 
							this.textColor = new Color(0, 0, 0, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.onPress = null;
 | 
				
			||||||
 | 
							this.onPressSignal = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.onHover = null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setPosition(vecOrX: Vec2 | number, y: number = null): void {
 | 
				
			||||||
 | 
							if(vecOrX instanceof Vec2){
 | 
				
			||||||
 | 
								this.position.set(vecOrX.x, vecOrX.y);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								this.position.set(vecOrX, y);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setText(text: string): void {
 | 
				
			||||||
 | 
							this.text = text;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setBackgroundColor(color: Color): void {
 | 
				
			||||||
 | 
							this.backgroundColor = color;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setTextColor(color: Color): void {
 | 
				
			||||||
 | 
							this.textColor = color;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						update(deltaT: number): void {
 | 
				
			||||||
 | 
							if(this.input.isMouseJustPressed()){
 | 
				
			||||||
 | 
								let mousePos =  this.input.getMousePressPosition();
 | 
				
			||||||
 | 
								if(mousePos.x >= this.position.x && mousePos.x <= this.position.x + this.size.x){
 | 
				
			||||||
 | 
									// Inside x bounds
 | 
				
			||||||
 | 
									if(mousePos.y >= this.position.y && mousePos.y <= this.position.y + this.size.y){
 | 
				
			||||||
 | 
										// Inside element
 | 
				
			||||||
 | 
										if(this.onHover !== null){
 | 
				
			||||||
 | 
											this.onHover();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if(this.onPress !== null){
 | 
				
			||||||
 | 
											this.onPress();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if(this.onPressSignal !== null){
 | 
				
			||||||
 | 
											let event = new GameEvent(this.onPressSignal, {});
 | 
				
			||||||
 | 
											this.eventQueue.addEvent(event);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						render(ctx: CanvasRenderingContext2D, viewportOrigin: Vec2, viewportSize: Vec2){
 | 
				
			||||||
 | 
							ctx.fillStyle = this.backgroundColor.toStringRGBA();
 | 
				
			||||||
 | 
							ctx.fillRect(this.position.x - viewportOrigin.x, this.position.y - viewportOrigin.y, this.size.x, this.size.y);
 | 
				
			||||||
 | 
							ctx.fillStyle = this.textColor.toStringRGBA();
 | 
				
			||||||
 | 
							ctx.font = "30px Arial"
 | 
				
			||||||
 | 
							ctx.fillText(this.text, this.position.x - viewportOrigin.x, this.position.y - viewportOrigin.y + 30);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								src/Playback/Recorder.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/Playback/Recorder.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,90 @@
 | 
				
			||||||
 | 
					import Queue from "../DataTypes/Queue";
 | 
				
			||||||
 | 
					import Receiver from "../Events/Receiver";
 | 
				
			||||||
 | 
					import GameEvent from "../Events/GameEvent";
 | 
				
			||||||
 | 
					import EventQueue from "../Events/EventQueue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Recorder{
 | 
				
			||||||
 | 
						private receiver: Receiver;
 | 
				
			||||||
 | 
						private log: Queue<LogItem>;
 | 
				
			||||||
 | 
						private recording: boolean;
 | 
				
			||||||
 | 
						private eventQueue: EventQueue;
 | 
				
			||||||
 | 
						private frame: number;
 | 
				
			||||||
 | 
						private playing: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(){
 | 
				
			||||||
 | 
							this.receiver = new Receiver();
 | 
				
			||||||
 | 
							this.log = new Queue(1000);
 | 
				
			||||||
 | 
							this.recording = false;
 | 
				
			||||||
 | 
							this.playing = false;
 | 
				
			||||||
 | 
							this.frame = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
 | 
							this.eventQueue.subscribe(this.receiver, "all");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						update(deltaT: number): void {
 | 
				
			||||||
 | 
							if(this.recording){
 | 
				
			||||||
 | 
								this.frame += 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(this.playing){
 | 
				
			||||||
 | 
								// If playing, ignore events, just feed the record to the event queue
 | 
				
			||||||
 | 
								this.receiver.ignoreEvents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
									While there is a next item, and while it should occur in this frame,
 | 
				
			||||||
 | 
									send the event. i.e., while current_frame * current_delta_t is greater
 | 
				
			||||||
 | 
									than recorded_frame * recorded_delta_t
 | 
				
			||||||
 | 
								*/
 | 
				
			||||||
 | 
								while(this.log.hasItems()
 | 
				
			||||||
 | 
										&& this.log.peekNext().frame * this.log.peekNext().delta < this.frame * deltaT){
 | 
				
			||||||
 | 
									let event = this.log.dequeue().event;
 | 
				
			||||||
 | 
									console.log(event);
 | 
				
			||||||
 | 
									this.eventQueue.addEvent(event);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if(!this.log.hasItems()){
 | 
				
			||||||
 | 
									this.playing = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.frame += 1;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// If not playing, handle events
 | 
				
			||||||
 | 
								while(this.receiver.hasNextEvent()){
 | 
				
			||||||
 | 
									let event = this.receiver.getNextEvent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(event.type === "stop_button_press"){
 | 
				
			||||||
 | 
										this.recording = false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(this.recording){
 | 
				
			||||||
 | 
										this.log.enqueue(new LogItem(this.frame, deltaT, event));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(event.type === "record_button_press"){
 | 
				
			||||||
 | 
										this.log.clear();
 | 
				
			||||||
 | 
										this.recording = true;
 | 
				
			||||||
 | 
										this.frame = 0
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(event.type === "play_button_press"){
 | 
				
			||||||
 | 
										this.frame = 0;
 | 
				
			||||||
 | 
										this.recording = false;
 | 
				
			||||||
 | 
										this.playing = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LogItem {
 | 
				
			||||||
 | 
						frame: number;
 | 
				
			||||||
 | 
						delta: number;
 | 
				
			||||||
 | 
						event: GameEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(frame: number, deltaT: number, event: GameEvent){
 | 
				
			||||||
 | 
							this.frame = frame;
 | 
				
			||||||
 | 
							this.delta = deltaT;
 | 
				
			||||||
 | 
							this.event = event;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										55
									
								
								src/SceneGraph/SceneGraph.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/SceneGraph/SceneGraph.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					import Viewport from "./Viewport";
 | 
				
			||||||
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
 | 
					import Map from "../DataTypes/Map";
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class SceneGraph{
 | 
				
			||||||
 | 
						protected viewport: Viewport;
 | 
				
			||||||
 | 
						protected nodeMap: Map<GameNode>;
 | 
				
			||||||
 | 
						protected idCounter: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(viewport: Viewport){
 | 
				
			||||||
 | 
							this.viewport = viewport;
 | 
				
			||||||
 | 
							this.nodeMap = new Map<GameNode>();
 | 
				
			||||||
 | 
							this.idCounter = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addNode(node: GameNode): number {
 | 
				
			||||||
 | 
							this.nodeMap.add(this.idCounter.toString(), node);
 | 
				
			||||||
 | 
							this.addNodeSpecific(node, this.idCounter.toString());
 | 
				
			||||||
 | 
							this.idCounter += 1;
 | 
				
			||||||
 | 
							return this.idCounter - 1;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected abstract addNodeSpecific(node: GameNode, id: string): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    removeNode(node: GameNode): void {
 | 
				
			||||||
 | 
							// Find and remove node in O(n)
 | 
				
			||||||
 | 
							// TODO: Can this be better?
 | 
				
			||||||
 | 
							let id = this.nodeMap.keys().filter((key: string) => this.nodeMap.get(key) === node)[0];
 | 
				
			||||||
 | 
							if(id !== undefined){
 | 
				
			||||||
 | 
								this.nodeMap.set(id, undefined);
 | 
				
			||||||
 | 
								this.removeNodeSpecific(node, id);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected abstract removeNodeSpecific(node: GameNode, id: string): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getNode(id: string): GameNode{
 | 
				
			||||||
 | 
							return this.nodeMap.get(id);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getNodeAt(vecOrX: Vec2 | number, y: number = null): GameNode{
 | 
				
			||||||
 | 
							if(vecOrX instanceof Vec2){
 | 
				
			||||||
 | 
								return this.getNodeAtCoords(vecOrX.x, vecOrX.y);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return this.getNodeAtCoords(vecOrX, y);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    protected abstract getNodeAtCoords(x: number, y: number): GameNode;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    abstract update(deltaT: number): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract getVisibleSet(): Array<GameNode>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/SceneGraph/SceneGraphArray.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/SceneGraph/SceneGraphArray.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					import SceneGraph from "./SceneGraph";
 | 
				
			||||||
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
 | 
					import Viewport from "./Viewport";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class SceneGraphArray extends SceneGraph{
 | 
				
			||||||
 | 
						private nodeList: Array<GameNode>;
 | 
				
			||||||
 | 
						private turnOffViewportCulling_demoTool: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(viewport: Viewport){
 | 
				
			||||||
 | 
					        super(viewport);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.nodeList = new Array<GameNode>();
 | 
				
			||||||
 | 
					        this.turnOffViewportCulling_demoTool = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setViewportCulling_demoTool(bool: boolean): void {
 | 
				
			||||||
 | 
					        this.turnOffViewportCulling_demoTool = bool;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addNodeSpecific(node: GameNode, id: string): void {
 | 
				
			||||||
 | 
					        this.nodeList.push(node);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    removeNodeSpecific(node: GameNode, id: string): void {
 | 
				
			||||||
 | 
					        let index = this.nodeList.indexOf(node);
 | 
				
			||||||
 | 
					        if(index > -1){
 | 
				
			||||||
 | 
					            this.nodeList.splice(index, 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getNodeAtCoords(x: number, y: number): GameNode {
 | 
				
			||||||
 | 
					        // TODO: This only returns the first node found. There is no notion of z coordinates
 | 
				
			||||||
 | 
					        for(let node of this.nodeList){
 | 
				
			||||||
 | 
					            if(node.contains(x, y)){
 | 
				
			||||||
 | 
					                return node;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
 | 
					        for(let node of this.nodeList){
 | 
				
			||||||
 | 
					            node.update(deltaT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getVisibleSet(): Array<GameNode> {
 | 
				
			||||||
 | 
					        // If viewport culling is turned off for demonstration
 | 
				
			||||||
 | 
					        if(this.turnOffViewportCulling_demoTool){
 | 
				
			||||||
 | 
					            let visibleSet = new Array<GameNode>();
 | 
				
			||||||
 | 
					            for(let node of this.nodeList){
 | 
				
			||||||
 | 
					                visibleSet.push(node);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return visibleSet;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let visibleSet = new Array<GameNode>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for(let node of this.nodeList){
 | 
				
			||||||
 | 
					            if(this.viewport.includes(node)){
 | 
				
			||||||
 | 
					                visibleSet.push(node);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return visibleSet;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/SceneGraph/Viewport.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/SceneGraph/Viewport.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					import Vec2 from "../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import Vec4 from "../DataTypes/Vec4";
 | 
				
			||||||
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
 | 
					import MathUtils from "../Utils/MathUtils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Viewport{
 | 
				
			||||||
 | 
						private position: Vec2;
 | 
				
			||||||
 | 
						private size: Vec2;
 | 
				
			||||||
 | 
						private bounds: Vec4;
 | 
				
			||||||
 | 
						private following: GameNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(){
 | 
				
			||||||
 | 
					        this.position = new Vec2(0, 0);
 | 
				
			||||||
 | 
					        this.size = new Vec2(0, 0);
 | 
				
			||||||
 | 
					        this.bounds = new Vec4(0, 0, 0, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPosition(): Vec2 {
 | 
				
			||||||
 | 
					        return this.position;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setPosition(vecOrX: Vec2 | number, y: number = null): void {
 | 
				
			||||||
 | 
							if(vecOrX instanceof Vec2){
 | 
				
			||||||
 | 
								this.position.set(vecOrX.x, vecOrX.y);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								this.position.set(vecOrX, y);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getSize(): Vec2{
 | 
				
			||||||
 | 
					        return this.size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    includes(node: GameNode): boolean {
 | 
				
			||||||
 | 
					        let nodePos = node.getPosition();
 | 
				
			||||||
 | 
					        let nodeSize = node.getSize();
 | 
				
			||||||
 | 
					        if(nodePos.x + nodeSize.x > this.position.x && nodePos.x < this.position.x + this.size.x){
 | 
				
			||||||
 | 
					            if(nodePos.y + nodeSize.y > this.position.y && nodePos.y < this.position.y + this.size.y){
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: Put some error handling on this for trying to make the bounds too small for the viewport
 | 
				
			||||||
 | 
						// TODO: This should probably be done automatically, or should consider the aspect ratio or something
 | 
				
			||||||
 | 
					    setBounds(lowerX: number, lowerY: number, upperX: number, upperY: number): void {
 | 
				
			||||||
 | 
					        this.bounds = new Vec4(lowerX, lowerY, upperX, upperY);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    follow(node: GameNode): void {
 | 
				
			||||||
 | 
					        this.following = node;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
 | 
					        if(this.following){
 | 
				
			||||||
 | 
					            this.position.x = this.following.getPosition().x - this.size.x/2;
 | 
				
			||||||
 | 
					            this.position.y = this.following.getPosition().y - this.size.y/2;
 | 
				
			||||||
 | 
					            let [min, max] = this.bounds.split();
 | 
				
			||||||
 | 
					            this.position.x = MathUtils.clamp(this.position.x, min.x, max.x - this.size.x/2);
 | 
				
			||||||
 | 
					            this.position.y = MathUtils.clamp(this.position.y, min.y, max.y - this.size.y/2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/Utils/Color.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Utils/Color.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					import MathUtils from "./MathUtils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Color{
 | 
				
			||||||
 | 
						public r: number;
 | 
				
			||||||
 | 
						public g: number;
 | 
				
			||||||
 | 
						public b: number;
 | 
				
			||||||
 | 
						public a: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(r: number = 0, g: number = 0, b: number = 0, a: number = null){
 | 
				
			||||||
 | 
					        this.r = r;
 | 
				
			||||||
 | 
					        this.g = g;
 | 
				
			||||||
 | 
					        this.b = b;
 | 
				
			||||||
 | 
					        this.a = a;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						toString(): string{
 | 
				
			||||||
 | 
							return "#" + MathUtils.toHex(this.r, 2) + MathUtils.toHex(this.g, 2) + MathUtils.toHex(this.b, 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						toStringRGB(){
 | 
				
			||||||
 | 
							return "rgb(" + this.r.toString() + ", " + this.g.toString() + ", " + this.b.toString() + ")";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						toStringRGBA(){
 | 
				
			||||||
 | 
							if(this.a === null){
 | 
				
			||||||
 | 
								throw "No alpha value assigned to color";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "rgb(" + this.r.toString() + ", " + this.g.toString() + ", " + this.b.toString() + ", " + this.a.toString() +")"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/Utils/MathUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/Utils/MathUtils.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					export default class MathUtils{
 | 
				
			||||||
 | 
					    static clamp(x: number, min: number, max: number): number{
 | 
				
			||||||
 | 
					        if(x < min) return min;
 | 
				
			||||||
 | 
					        if(x > max) return max;
 | 
				
			||||||
 | 
					        return x;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static toHex(num: number, minLength: number = null): string{
 | 
				
			||||||
 | 
					        let factor = 1;
 | 
				
			||||||
 | 
					        while(factor*16 < num){
 | 
				
			||||||
 | 
					            factor *= 16;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let hexStr = "";
 | 
				
			||||||
 | 
					        while(num > 0){
 | 
				
			||||||
 | 
					            let digit = Math.floor(num/factor);
 | 
				
			||||||
 | 
					            hexStr += MathUtils.toHexDigit(digit);
 | 
				
			||||||
 | 
					            num -= digit * factor;
 | 
				
			||||||
 | 
					            factor /= 16;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if(minLength !== null){
 | 
				
			||||||
 | 
								while(hexStr.length < minLength){
 | 
				
			||||||
 | 
									hexStr = "0" + hexStr;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return hexStr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static toHexDigit(num: number): string{
 | 
				
			||||||
 | 
					        if(num < 10){
 | 
				
			||||||
 | 
					            return "" + num;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return String.fromCharCode(65 + num - 10);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/Utils/RandUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Utils/RandUtils.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					import MathUtils from "./MathUtils";
 | 
				
			||||||
 | 
					import Color from "./Color";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class RandUtils{
 | 
				
			||||||
 | 
						static randInt(min: number, max: number): number{
 | 
				
			||||||
 | 
					        return Math.floor(Math.random()*(max - min) + min);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    static randHex(min: number, max: number): string{
 | 
				
			||||||
 | 
					        return MathUtils.toHex(RandUtils.randInt(min, max));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static randColor(): Color{
 | 
				
			||||||
 | 
					        let r = RandUtils.randInt(0, 256);
 | 
				
			||||||
 | 
					        let g = RandUtils.randInt(0, 256);
 | 
				
			||||||
 | 
					        let b = RandUtils.randInt(0, 256);
 | 
				
			||||||
 | 
					        return new Color(r, g, b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
export function sayHello(name: string){
 | 
					 | 
				
			||||||
    return `Hello from ${name}`;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
    <title>Hello World!</title>
 | 
					    <title>Hello World!</title>
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
        <p id="greeting">Loading ...</p>
 | 
					    <canvas id="game-canvas"></canvas>
 | 
				
			||||||
    <script src="bundle.js"></script>
 | 
					    <script src="bundle.js"></script>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										91
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								src/main.ts
									
									
									
									
									
								
							| 
						 | 
					@ -1,8 +1,89 @@
 | 
				
			||||||
import { sayHello } from './greet';
 | 
					import GameLoop from "./Loop/GameLoop";
 | 
				
			||||||
 | 
					import Scene from "./GameState/Scene";
 | 
				
			||||||
 | 
					import Player from "./Nodes/Player";
 | 
				
			||||||
 | 
					import UIElement from "./Nodes/UIElement";
 | 
				
			||||||
 | 
					import ColoredCircle from "./Nodes/ColoredCircle";
 | 
				
			||||||
 | 
					import Color from "./Utils/Color";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showHello(divName: string, name: string) {
 | 
					function main(){
 | 
				
			||||||
    const elt = document.getElementById(divName);
 | 
					    // Create the game object
 | 
				
			||||||
    elt.innerText = sayHello(name);
 | 
					    let game = new GameLoop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mainScene = new Scene();
 | 
				
			||||||
 | 
					    let pauseMenu = new Scene();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Initialize GameObjects
 | 
				
			||||||
 | 
					    let player = new Player();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let recordButton = new UIElement();
 | 
				
			||||||
 | 
					    recordButton.setSize(100, 50);
 | 
				
			||||||
 | 
					    recordButton.setText("Record");
 | 
				
			||||||
 | 
					    recordButton.setBackgroundColor(new Color(200, 100, 0, 0.3));
 | 
				
			||||||
 | 
					    recordButton.setPosition(400, 30);
 | 
				
			||||||
 | 
					    recordButton.onPressSignal = "record_button_press";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let stopButton = new UIElement();
 | 
				
			||||||
 | 
					    stopButton.setSize(100, 50);
 | 
				
			||||||
 | 
					    stopButton.setText("Stop");
 | 
				
			||||||
 | 
					    stopButton.setBackgroundColor(new Color(200, 0, 0, 0.3));
 | 
				
			||||||
 | 
					    stopButton.setPosition(550, 30);
 | 
				
			||||||
 | 
					    stopButton.onPressSignal = "stop_button_press";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let playButton = new UIElement();
 | 
				
			||||||
 | 
					    playButton.setSize(100, 50);
 | 
				
			||||||
 | 
					    playButton.setText("Play");
 | 
				
			||||||
 | 
					    playButton.setBackgroundColor(new Color(0, 200, 0, 0.3));
 | 
				
			||||||
 | 
					    playButton.setPosition(700, 30);
 | 
				
			||||||
 | 
					    playButton.onPressSignal = "play_button_press";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let cycleFramerateButton = new UIElement();
 | 
				
			||||||
 | 
					    cycleFramerateButton.setSize(150, 50);
 | 
				
			||||||
 | 
					    cycleFramerateButton.setText("Cycle FPS");
 | 
				
			||||||
 | 
					    cycleFramerateButton.setBackgroundColor(new Color(200, 0, 200, 0.3));
 | 
				
			||||||
 | 
					    cycleFramerateButton.setPosition(5, 400);
 | 
				
			||||||
 | 
					    let i = 0;
 | 
				
			||||||
 | 
					    let fps = [15, 30, 60];
 | 
				
			||||||
 | 
					    cycleFramerateButton.onPress = () => {
 | 
				
			||||||
 | 
					        game.setMaxFPS(fps[i]);
 | 
				
			||||||
 | 
					        i = (i + 1) % 3;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
showHello('greeting', 'TypeScript');
 | 
					    let pauseButton = new UIElement();
 | 
				
			||||||
 | 
					    pauseButton.setSize(100, 50);
 | 
				
			||||||
 | 
					    pauseButton.setText("Pause");
 | 
				
			||||||
 | 
					    pauseButton.setBackgroundColor(new Color(200, 0, 200, 1));
 | 
				
			||||||
 | 
					    pauseButton.setPosition(700, 400);
 | 
				
			||||||
 | 
					    pauseButton.onPress = () => {
 | 
				
			||||||
 | 
					        game.getGameState().addScene(pauseMenu);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let modalBackground = new UIElement();
 | 
				
			||||||
 | 
					    modalBackground.setSize(400, 200);
 | 
				
			||||||
 | 
					    modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
 | 
				
			||||||
 | 
					    modalBackground.setPosition(200, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let resumeButton = new UIElement();
 | 
				
			||||||
 | 
					    resumeButton.setSize(100, 50);
 | 
				
			||||||
 | 
					    resumeButton.setText("Resume");
 | 
				
			||||||
 | 
					    resumeButton.setBackgroundColor(new Color(200, 0, 200, 1));
 | 
				
			||||||
 | 
					    resumeButton.setPosition(400, 200);
 | 
				
			||||||
 | 
					    resumeButton.onPress = () => {
 | 
				
			||||||
 | 
					        game.getGameState().removeScene();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let lotsOfCircs = [];
 | 
				
			||||||
 | 
					    for(let i = 0; i < 10; i++){
 | 
				
			||||||
 | 
					        lotsOfCircs.push(new ColoredCircle());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mainScene.add([...lotsOfCircs, player, recordButton, stopButton, playButton, cycleFramerateButton, pauseButton]);
 | 
				
			||||||
 | 
					    mainScene.getViewport().follow(player);
 | 
				
			||||||
 | 
					    pauseMenu.add([modalBackground, resumeButton]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    game.getGameState().changeScene(mainScene);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    game.start();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main();
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,40 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "files": [
 | 
					    "files": [
 | 
				
			||||||
        "src/main.ts",
 | 
					        "src/main.ts",
 | 
				
			||||||
        "src/greet.ts"
 | 
					
 | 
				
			||||||
 | 
					        "src/DataTypes/Collection.ts",
 | 
				
			||||||
 | 
					        "src/DataTypes/Map.ts",
 | 
				
			||||||
 | 
					        "src/DataTypes/Queue.ts",
 | 
				
			||||||
 | 
					        "src/DataTypes/Stack.ts",
 | 
				
			||||||
 | 
					        "src/DataTypes/Vec2.ts",
 | 
				
			||||||
 | 
					        "src/DataTypes/Vec4.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "src/Events/EventQueue.ts",
 | 
				
			||||||
 | 
					        "src/Events/GameEvent.ts",
 | 
				
			||||||
 | 
					        "src/Events/Receiver.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "src/GameState/GameState.ts",
 | 
				
			||||||
 | 
					        "src/GameState/Scene.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "src/Input/InputHandler.ts",
 | 
				
			||||||
 | 
					        "src/Input/InputReceiver.ts",
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        "src/Loop/GameLoop.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "src/Nodes/ColoredCircle.ts",
 | 
				
			||||||
 | 
					        "src/Nodes/GameNode.ts",
 | 
				
			||||||
 | 
					        "src/Nodes/Player.ts",
 | 
				
			||||||
 | 
					        "src/Nodes/UIElement.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "src/Playback/Recorder.ts",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "src/SceneGraph/SceneGraph.ts",
 | 
				
			||||||
 | 
					        "src/SceneGraph/SceneGraphArray.ts",
 | 
				
			||||||
 | 
					        "src/SceneGraph/Viewport.ts",
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        "src/Utils/Color.ts",
 | 
				
			||||||
 | 
					        "src/Utils/MathUtils.ts",
 | 
				
			||||||
 | 
					        "src/Utils/RandUtils.ts"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "compilerOptions": {
 | 
					    "compilerOptions": {
 | 
				
			||||||
        "noImplicitAny": true,
 | 
					        "noImplicitAny": true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user