added annotations to all files
This commit is contained in:
parent
3661ee3ada
commit
9e0ebae63c
|
@ -1,10 +1,15 @@
|
||||||
import { Actor, AI, Updateable } from "../DataTypes/Interfaces/Descriptors";
|
import { Actor, AI, Updateable } from "../DataTypes/Interfaces/Descriptors";
|
||||||
import Map from "../DataTypes/Map";
|
import Map from "../DataTypes/Map";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manager class for all of the AI in a scene.
|
||||||
|
* Keeps a list of registered actors and handles AI generation for actors.
|
||||||
|
*/
|
||||||
export default class AIManager implements Updateable {
|
export default class AIManager implements Updateable {
|
||||||
|
/** The array of registered actors */
|
||||||
actors: Array<Actor>;
|
actors: Array<Actor>;
|
||||||
|
/** Maps AI names to their constructors */
|
||||||
registeredAI: Map<new <T extends AI>() => T>;
|
registeredAI: Map<AIConstructor>;
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
this.actors = new Array();
|
this.actors = new Array();
|
||||||
|
@ -20,10 +25,20 @@ export default class AIManager implements Updateable {
|
||||||
this.actors.push(actor);
|
this.actors.push(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an AI with the AIManager for use later on
|
||||||
|
* @param name The name of the AI to register
|
||||||
|
* @param constr The constructor for the AI
|
||||||
|
*/
|
||||||
registerAI(name: string, constr: new <T extends AI>() => T ): void {
|
registerAI(name: string, constr: new <T extends AI>() => T ): void {
|
||||||
this.registeredAI.add(name, constr);
|
this.registeredAI.add(name, constr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an AI instance from its name
|
||||||
|
* @param name The name of the AI to add
|
||||||
|
* @returns A new AI instance
|
||||||
|
*/
|
||||||
generateAI(name: string): AI {
|
generateAI(name: string): AI {
|
||||||
if(this.registeredAI.has(name)){
|
if(this.registeredAI.has(name)){
|
||||||
return new (this.registeredAI.get(name))();
|
return new (this.registeredAI.get(name))();
|
||||||
|
@ -37,3 +52,5 @@ export default class AIManager implements Updateable {
|
||||||
this.actors.forEach(actor => { if(actor.aiActive) actor.ai.update(deltaT) });
|
this.actors.forEach(actor => { if(actor.aiActive) actor.ai.update(deltaT) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AIConstructor = new <T extends AI>() => T;
|
|
@ -2,8 +2,13 @@ import { AI } from "../DataTypes/Interfaces/Descriptors";
|
||||||
import StateMachine from "../DataTypes/State/StateMachine";
|
import StateMachine from "../DataTypes/State/StateMachine";
|
||||||
import GameNode from "../Nodes/GameNode";
|
import GameNode from "../Nodes/GameNode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of a @reference[StateMachine] that is configured to work as an AI controller for a @reference[GameNode]
|
||||||
|
*/
|
||||||
export default class StateMachineAI extends StateMachine implements AI {
|
export default class StateMachineAI extends StateMachine implements AI {
|
||||||
|
/** The GameNode that uses this StateMachine for its AI */
|
||||||
protected owner: GameNode;
|
protected owner: GameNode;
|
||||||
|
|
||||||
|
// @implemented
|
||||||
initializeAI(owner: GameNode, config: Record<string, any>): void {}
|
initializeAI(owner: GameNode, config: Record<string, any>): void {}
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
// TODO - Is there already a way to do this in js/ts?
|
|
||||||
/**
|
/**
|
||||||
* An interface for all iterable data custom data structures
|
* An interface for all iterable data custom data structures
|
||||||
*/
|
*/
|
||||||
export default interface Collection {
|
export default interface Collection {
|
||||||
/**
|
/**
|
||||||
* Iterates through all of the items in this data structure.
|
* Iterates through all of the items in this data structure.
|
||||||
* @param func
|
* @param func The function to evaluate of every item in the collection
|
||||||
*/
|
*/
|
||||||
forEach(func: Function): void;
|
forEach(func: Function): void;
|
||||||
|
|
||||||
|
|
22
src/DataTypes/Graphs/EdgeNode.ts
Normal file
22
src/DataTypes/Graphs/EdgeNode.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* A linked-list for the edges in a @reference[Graph].
|
||||||
|
*/
|
||||||
|
export default class EdgeNode {
|
||||||
|
/** The node in the Graph this edge connects to */
|
||||||
|
y: number;
|
||||||
|
/** The weight of this EdgeNode */
|
||||||
|
weight: number;
|
||||||
|
/** The next EdgeNode in the linked-list */
|
||||||
|
next: EdgeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new EdgeNode
|
||||||
|
* @param index The index of the node this edge connects to
|
||||||
|
* @param weight The weight of this edge
|
||||||
|
*/
|
||||||
|
constructor(index: number, weight?: number){
|
||||||
|
this.y = index;
|
||||||
|
this.next = null;
|
||||||
|
this.weight = weight ? weight : 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,28 @@
|
||||||
|
import EdgeNode from "./EdgeNode";
|
||||||
|
|
||||||
export const MAX_V = 100;
|
export const MAX_V = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a graph data structure using edge lists. Inspired by The Algorithm Design Manual.
|
||||||
|
*/
|
||||||
export default class Graph {
|
export default class Graph {
|
||||||
|
/** An array of edges at the node specified by the index */
|
||||||
edges: Array<EdgeNode>;
|
edges: Array<EdgeNode>;
|
||||||
|
/** An array representing the degree of the node specified by the index */
|
||||||
degree: Array<number>;
|
degree: Array<number>;
|
||||||
|
/** The number of vertices in the graph */
|
||||||
numVertices: number;
|
numVertices: number;
|
||||||
|
/** The number of edges in the graph */
|
||||||
numEdges: number;
|
numEdges: number;
|
||||||
|
/** Whether or not the graph is directed */
|
||||||
directed: boolean;
|
directed: boolean;
|
||||||
|
/** Whether or not the graph is weighted */
|
||||||
weighted: boolean;
|
weighted: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new graph
|
||||||
|
* @param directed Whether or not this graph is directed
|
||||||
|
*/
|
||||||
constructor(directed: boolean = false){
|
constructor(directed: boolean = false){
|
||||||
this.directed = directed;
|
this.directed = directed;
|
||||||
this.weighted = false;
|
this.weighted = false;
|
||||||
|
@ -19,12 +34,20 @@ export default class Graph {
|
||||||
this.degree = new Array(MAX_V);
|
this.degree = new Array(MAX_V);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Adds a node to this graph and returns the index of it
|
||||||
|
* @returns The index of the new node
|
||||||
|
*/
|
||||||
addNode(): number {
|
addNode(): number {
|
||||||
this.numVertices++;
|
this.numVertices++;
|
||||||
return this.numVertices;
|
return this.numVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
addEdge(x: number, y: number, weight?: number){
|
/** Adds an edge between node x and y, with an optional weight
|
||||||
|
* @param x The index of the start of the edge
|
||||||
|
* @param y The index of the end of the edge
|
||||||
|
* @param weight The optional weight of the new edge
|
||||||
|
*/
|
||||||
|
addEdge(x: number, y: number, weight?: number): void {
|
||||||
let edge = new EdgeNode(y, weight);
|
let edge = new EdgeNode(y, weight);
|
||||||
|
|
||||||
if(this.edges[x]){
|
if(this.edges[x]){
|
||||||
|
@ -46,18 +69,36 @@ export default class Graph {
|
||||||
this.numEdges += 1;
|
this.numEdges += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the edge list associated with node x
|
||||||
|
* @param x The index of the node
|
||||||
|
* @returns The head of a linked-list of edges
|
||||||
|
*/
|
||||||
getEdges(x: number): EdgeNode {
|
getEdges(x: number): EdgeNode {
|
||||||
return this.edges[x];
|
return this.edges[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the degree associated with node x
|
||||||
|
* @param x The index of the node
|
||||||
|
*/
|
||||||
getDegree(x: number): number {
|
getDegree(x: number): number {
|
||||||
return this.degree[x];
|
return this.degree[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the specifed node into a string
|
||||||
|
* @param index The index of the node to convert to a string
|
||||||
|
* @returns The string representation of the node: "Node x"
|
||||||
|
*/
|
||||||
protected nodeToString(index: number): string {
|
protected nodeToString(index: number): string {
|
||||||
return "Node " + index;
|
return "Node " + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the Graph into a string format
|
||||||
|
* @returns The graph as a string
|
||||||
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
let retval = "";
|
let retval = "";
|
||||||
|
|
||||||
|
@ -82,15 +123,3 @@ export default class Graph {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EdgeNode {
|
|
||||||
y: number;
|
|
||||||
next: EdgeNode;
|
|
||||||
weight: number;
|
|
||||||
|
|
||||||
constructor(index: number, weight?: number){
|
|
||||||
this.y = index;
|
|
||||||
this.next = null;
|
|
||||||
this.weight = weight ? weight : 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,27 +2,74 @@ import Graph, { MAX_V } from "./Graph";
|
||||||
import Vec2 from "../Vec2";
|
import Vec2 from "../Vec2";
|
||||||
import { DebugRenderable } from "../Interfaces/Descriptors";
|
import { DebugRenderable } from "../Interfaces/Descriptors";
|
||||||
|
|
||||||
export default class PositionGraph extends Graph implements DebugRenderable{
|
/**
|
||||||
|
* An extension of Graph that has nodes with positions in 2D space.
|
||||||
|
* This is a weighted graph (though not inherently directd)
|
||||||
|
*/
|
||||||
|
export default class PositionGraph extends Graph implements DebugRenderable {
|
||||||
|
/** An array of the positions of the nodes in this graph */
|
||||||
positions: Array<Vec2>;
|
positions: Array<Vec2>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Createes a new PositionGraph
|
||||||
|
* @param directed Whether or not this graph is directed
|
||||||
|
*/
|
||||||
constructor(directed: boolean = false){
|
constructor(directed: boolean = false){
|
||||||
super(directed);
|
super(directed);
|
||||||
this.positions = new Array(MAX_V);
|
this.positions = new Array(MAX_V);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPositionedNode(position: Vec2){
|
/**
|
||||||
|
* Adds a positioned node to this graph
|
||||||
|
* @param position The position of the node to add
|
||||||
|
* @returns The index of the added node
|
||||||
|
*/
|
||||||
|
addPositionedNode(position: Vec2): number {
|
||||||
this.positions[this.numVertices] = position;
|
this.positions[this.numVertices] = position;
|
||||||
this.addNode();
|
return this.addNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the position of a node.
|
||||||
|
* Automatically adjusts the weights of the graph tied to this node.
|
||||||
|
* As such, be warned that this function has an O(n + m) running time, and use it sparingly.
|
||||||
|
* @param index The index of the node
|
||||||
|
* @param position The new position of the node
|
||||||
|
*/
|
||||||
setNodePosition(index: number, position: Vec2): void {
|
setNodePosition(index: number, position: Vec2): void {
|
||||||
this.positions[index] = position;
|
this.positions[index] = position;
|
||||||
|
|
||||||
|
// Recalculate all weights associated with this index
|
||||||
|
for(let i = 0; i < this.numEdges; i++){
|
||||||
|
|
||||||
|
let edge = this.edges[i];
|
||||||
|
|
||||||
|
while(edge !== null){
|
||||||
|
// If this node is on either side of the edge, recalculate weight
|
||||||
|
if(i === index || edge.y === index){
|
||||||
|
edge.weight = this.positions[i].distanceTo(this.positions[edge.y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
edge = edge.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of a node
|
||||||
|
* @param index The index of the node
|
||||||
|
* @returns The position of the node
|
||||||
|
*/
|
||||||
getNodePosition(index: number): Vec2 {
|
getNodePosition(index: number): Vec2 {
|
||||||
return this.positions[index];
|
return this.positions[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an edge to this graph between node x and y.
|
||||||
|
* Automatically calculates the weight of the edge as the distance between the nodes.
|
||||||
|
* @param x The beginning of the edge
|
||||||
|
* @param y The end of the edge
|
||||||
|
*/
|
||||||
addEdge(x: number, y: number): void {
|
addEdge(x: number, y: number): void {
|
||||||
if(!this.positions[x] || !this.positions[y]){
|
if(!this.positions[x] || !this.positions[y]){
|
||||||
throw "Can't add edge to un-positioned node!";
|
throw "Can't add edge to un-positioned node!";
|
||||||
|
@ -34,6 +81,7 @@ export default class PositionGraph extends Graph implements DebugRenderable{
|
||||||
super.addEdge(x, y, weight);
|
super.addEdge(x, y, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
protected nodeToString(index: number): string {
|
protected nodeToString(index: number): string {
|
||||||
return "Node " + index + " - " + this.positions[index].toString();
|
return "Node " + index + " - " + this.positions[index].toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import Vec2 from "../Vec2";
|
||||||
import NavigationPath from "../../Pathfinding/NavigationPath";
|
import NavigationPath from "../../Pathfinding/NavigationPath";
|
||||||
import GameNode from "../../Nodes/GameNode";
|
import GameNode from "../../Nodes/GameNode";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export interface Unique {
|
export interface Unique {
|
||||||
/** The unique id of this object. */
|
/** The unique id of this object. */
|
||||||
id: number;
|
id: number;
|
||||||
|
|
|
@ -6,14 +6,15 @@ import Collection from "./Collection";
|
||||||
export default class Map<T> implements Collection {
|
export default class Map<T> implements Collection {
|
||||||
private map: Record<string, T>;
|
private map: Record<string, T>;
|
||||||
|
|
||||||
|
/** Creates a new map */
|
||||||
constructor(){
|
constructor(){
|
||||||
this.map = {};
|
this.map = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a value T stored at a key.
|
* Adds a value T stored at a key.
|
||||||
* @param key
|
* @param key The key of the item to be stored
|
||||||
* @param value
|
* @param value The item to be stored
|
||||||
*/
|
*/
|
||||||
add(key: string, value: T): void {
|
add(key: string, value: T): void {
|
||||||
this.map[key] = value;
|
this.map[key] = value;
|
||||||
|
@ -21,16 +22,17 @@ export default class Map<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value associated with a key.
|
* Get the value associated with a key.
|
||||||
* @param key
|
* @param key The key of the item
|
||||||
|
* @returns The item at the key or undefined
|
||||||
*/
|
*/
|
||||||
get(key: string): T {
|
get(key: string): T {
|
||||||
return this.map[key];
|
return this.map[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value stored at key to the new specified value
|
* An alias of add. Sets the value stored at key to the new specified value
|
||||||
* @param key
|
* @param key The key of the item to be stored
|
||||||
* @param value
|
* @param value The item to be stored
|
||||||
*/
|
*/
|
||||||
set(key: string, value: T): void {
|
set(key: string, value: T): void {
|
||||||
this.add(key, value);
|
this.add(key, value);
|
||||||
|
@ -38,7 +40,8 @@ export default class Map<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there is a value stored at the specified key, false otherwise.
|
* Returns true if there is a value stored at the specified key, false otherwise.
|
||||||
* @param key
|
* @param key The key to check
|
||||||
|
* @returns A boolean representing whether or not there is an item at the given key.
|
||||||
*/
|
*/
|
||||||
has(key: string): boolean {
|
has(key: string): boolean {
|
||||||
return this.map[key] !== undefined;
|
return this.map[key] !== undefined;
|
||||||
|
@ -46,11 +49,13 @@ export default class Map<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of all of the keys in this map.
|
* Returns an array of all of the keys in this map.
|
||||||
|
* @returns An array containing all keys in the map.
|
||||||
*/
|
*/
|
||||||
keys(): Array<string> {
|
keys(): Array<string> {
|
||||||
return Object.keys(this.map);
|
return Object.keys(this.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
forEach(func: (key: string) => void): void {
|
forEach(func: (key: string) => void): void {
|
||||||
Object.keys(this.map).forEach(key => func(key));
|
Object.keys(this.map).forEach(key => func(key));
|
||||||
}
|
}
|
||||||
|
@ -63,10 +68,15 @@ export default class Map<T> implements Collection {
|
||||||
delete this.map[key];
|
delete this.map[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.forEach(key => delete this.map[key]);
|
this.forEach(key => delete this.map[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this map to a string representation.
|
||||||
|
* @returns The string representation of this map.
|
||||||
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
let str = "";
|
let str = "";
|
||||||
|
|
||||||
|
|
21
src/DataTypes/Physics/AreaCollision.ts
Normal file
21
src/DataTypes/Physics/AreaCollision.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import AABB from "../Shapes/AABB";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that contains the area of overlap of two colliding objects to allow for sorting by the physics system.
|
||||||
|
*/
|
||||||
|
export default class AreaCollision {
|
||||||
|
/** The area of the overlap for the colliding objects */
|
||||||
|
area: number;
|
||||||
|
/** The AABB of the other collider in this collision */
|
||||||
|
collider: AABB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AreaCollision object
|
||||||
|
* @param area The area of the collision
|
||||||
|
* @param collider The other collider
|
||||||
|
*/
|
||||||
|
constructor(area: number, collider: AABB){
|
||||||
|
this.area = area;
|
||||||
|
this.collider = collider;
|
||||||
|
}
|
||||||
|
}
|
18
src/DataTypes/Physics/Hit.ts
Normal file
18
src/DataTypes/Physics/Hit.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import Vec2 from "../Vec2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing the data collected from a physics hit between two geometric objects.
|
||||||
|
* Inspired by the helpful collision documentation @link(here)(https://noonat.github.io/intersect/).
|
||||||
|
*/
|
||||||
|
export default class Hit {
|
||||||
|
/** The time of the collision. Only numbers 0 through 1 happen in this frame. */
|
||||||
|
time: number;
|
||||||
|
/** The near times of the collision */
|
||||||
|
nearTimes: Vec2 = Vec2.ZERO;
|
||||||
|
/** The position of the collision */
|
||||||
|
pos: Vec2 = Vec2.ZERO;
|
||||||
|
/** The overlap distance of the hit */
|
||||||
|
delta: Vec2 = Vec2.ZERO;
|
||||||
|
/** The normal vector of the hit */
|
||||||
|
normal: Vec2 = Vec2.ZERO;
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import { Positioned } from "./Interfaces/Descriptors";
|
||||||
|
|
||||||
// TODO - Make max depth
|
// TODO - Make max depth
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primarily used to organize the scene graph
|
* Primarily used to organize the scene graph
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,12 +4,25 @@ import Collection from "./Collection";
|
||||||
* A FIFO queue with elements of type T
|
* A FIFO queue with elements of type T
|
||||||
*/
|
*/
|
||||||
export default class Queue<T> implements Collection {
|
export default class Queue<T> implements Collection {
|
||||||
private readonly MAX_ELEMENTS: number;
|
/** The maximum number of elements in the Queue */
|
||||||
private q: Array<T>;
|
private readonly MAX_ELEMENTS: number;
|
||||||
private head: number;
|
|
||||||
|
/** The internal representation of the queue */
|
||||||
|
private q: Array<T>;
|
||||||
|
|
||||||
|
/** The head of the queue */
|
||||||
|
private head: number;
|
||||||
|
|
||||||
|
/** The tail of the queue */
|
||||||
private tail: number;
|
private tail: number;
|
||||||
|
|
||||||
|
/** The current number of items in the queue */
|
||||||
private size: number;
|
private size: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new queue
|
||||||
|
* @param maxElements The maximum size of the stack
|
||||||
|
*/
|
||||||
constructor(maxElements: number = 100){
|
constructor(maxElements: number = 100){
|
||||||
this.MAX_ELEMENTS = maxElements;
|
this.MAX_ELEMENTS = maxElements;
|
||||||
this.q = new Array(this.MAX_ELEMENTS);
|
this.q = new Array(this.MAX_ELEMENTS);
|
||||||
|
@ -20,7 +33,7 @@ export default class Queue<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an item to the back of the queue
|
* Adds an item to the back of the queue
|
||||||
* @param item
|
* @param item The item to add to the back of the queue
|
||||||
*/
|
*/
|
||||||
enqueue(item: T): void{
|
enqueue(item: T): void{
|
||||||
if((this.tail + 1) % this.MAX_ELEMENTS === this.head){
|
if((this.tail + 1) % this.MAX_ELEMENTS === this.head){
|
||||||
|
@ -34,6 +47,7 @@ export default class Queue<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an item from the front of the queue
|
* Retrieves an item from the front of the queue
|
||||||
|
* @returns The item at the front of the queue
|
||||||
*/
|
*/
|
||||||
dequeue(): T {
|
dequeue(): T {
|
||||||
if(this.head === this.tail){
|
if(this.head === this.tail){
|
||||||
|
@ -51,7 +65,8 @@ export default class Queue<T> implements Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the item at the front of the queue, but does not return it
|
* Returns the item at the front of the queue, but does not remove it
|
||||||
|
* @returns The item at the front of the queue
|
||||||
*/
|
*/
|
||||||
peekNext(): T {
|
peekNext(): T {
|
||||||
if(this.head === this.tail){
|
if(this.head === this.tail){
|
||||||
|
@ -65,6 +80,7 @@ export default class Queue<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the queue has items in it, false otherwise
|
* Returns true if the queue has items in it, false otherwise
|
||||||
|
* @returns A boolean representing whether or not this queue has items
|
||||||
*/
|
*/
|
||||||
hasItems(): boolean {
|
hasItems(): boolean {
|
||||||
return this.head !== this.tail;
|
return this.head !== this.tail;
|
||||||
|
@ -72,17 +88,20 @@ export default class Queue<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of elements in the queue.
|
* Returns the number of elements in the queue.
|
||||||
|
* @returns The size of the queue
|
||||||
*/
|
*/
|
||||||
getSize(): number {
|
getSize(): number {
|
||||||
return this.size;
|
return this.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.forEach((item, index) => delete this.q[index]);
|
this.forEach((item, index) => delete this.q[index]);
|
||||||
this.size = 0;
|
this.size = 0;
|
||||||
this.head = this.tail;
|
this.head = this.tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
forEach(func: (item: T, index?: number) => void): void {
|
forEach(func: (item: T, index?: number) => void): void {
|
||||||
let i = this.head;
|
let i = this.head;
|
||||||
while(i !== this.tail){
|
while(i !== this.tail){
|
||||||
|
|
|
@ -6,38 +6,34 @@ import Map from "./Map";
|
||||||
import Stats from "../Debug/Stats";
|
import Stats from "../Debug/Stats";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primarily used to organize the scene graph
|
* A quadtree data structure implemented to work with regions rather than points.
|
||||||
|
* Elements in this quadtree have a position and an area, and thus can span multiple
|
||||||
|
* quadtree branches.
|
||||||
*/
|
*/
|
||||||
export default class QuadTree<T extends Region & Unique> implements Collection {
|
export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
/**
|
/** The center of this quadtree */
|
||||||
* The center of this quadtree
|
|
||||||
*/
|
|
||||||
protected boundary: AABB;
|
protected boundary: AABB;
|
||||||
|
|
||||||
/**
|
/** The number of elements this quadtree root can hold before splitting */
|
||||||
* The number of elements this quadtree root can hold before splitting
|
|
||||||
*/
|
|
||||||
protected capacity: number;
|
protected capacity: number;
|
||||||
|
|
||||||
/**
|
/** The maximum height of the quadtree from this root */
|
||||||
* The maximum height of the quadtree from this root
|
|
||||||
*/
|
|
||||||
protected maxDepth: number;
|
protected maxDepth: number;
|
||||||
|
|
||||||
/**
|
/** Represents whether the quadtree is a root or a leaf */
|
||||||
* Represents whether the quadtree is a root or a leaf
|
|
||||||
*/
|
|
||||||
protected divided: boolean;
|
protected divided: boolean;
|
||||||
|
|
||||||
/**
|
/** The array of the items in this quadtree */
|
||||||
* The array of the items in this quadtree
|
|
||||||
*/
|
|
||||||
protected items: Array<T>;
|
protected items: Array<T>;
|
||||||
|
|
||||||
// The child quadtrees of this one
|
// The child quadtrees of this one
|
||||||
|
/** The top left child */
|
||||||
protected nw: QuadTree<T>;
|
protected nw: QuadTree<T>;
|
||||||
|
/** The top right child */
|
||||||
protected ne: QuadTree<T>;
|
protected ne: QuadTree<T>;
|
||||||
|
/** The bottom left child */
|
||||||
protected sw: QuadTree<T>;
|
protected sw: QuadTree<T>;
|
||||||
|
/** The bottom right child */
|
||||||
protected se: QuadTree<T>;
|
protected se: QuadTree<T>;
|
||||||
|
|
||||||
constructor(center: Vec2, size: Vec2, maxDepth?: number, capacity?: number){
|
constructor(center: Vec2, size: Vec2, maxDepth?: number, capacity?: number){
|
||||||
|
@ -92,6 +88,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
/**
|
/**
|
||||||
* Returns all items at this point.
|
* Returns all items at this point.
|
||||||
* @param point The point to query at
|
* @param point The point to query at
|
||||||
|
* @returns A list of all elements in the quadtree that contain the specified point
|
||||||
*/
|
*/
|
||||||
queryPoint(point: Vec2): Array<T> {
|
queryPoint(point: Vec2): Array<T> {
|
||||||
// A matrix to keep track of our results
|
// A matrix to keep track of our results
|
||||||
|
@ -105,6 +102,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ignoreFunction
|
||||||
/**
|
/**
|
||||||
* A recursive function called by queryPoint
|
* A recursive function called by queryPoint
|
||||||
* @param point The point being queried
|
* @param point The point being queried
|
||||||
|
@ -135,10 +133,11 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all items in this region
|
* Returns all items in this region
|
||||||
* @param boundary The region to check
|
* @param boundary The region to check
|
||||||
* @param inclusionCheck Allows for additional inclusion checks to further refine searches
|
* @param inclusionCheck Allows for additional inclusion checks to further refine searches
|
||||||
|
* @returns A list of all elements in the specified region
|
||||||
*/
|
*/
|
||||||
queryRegion(boundary: AABB): Array<T> {
|
queryRegion(boundary: AABB): Array<T> {
|
||||||
// A matrix to keep track of our results
|
// A matrix to keep track of our results
|
||||||
|
@ -152,6 +151,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ignoreFunction
|
||||||
/**
|
/**
|
||||||
* A recursive function called by queryPoint
|
* A recursive function called by queryPoint
|
||||||
* @param point The point being queried
|
* @param point The point being queried
|
||||||
|
@ -213,7 +213,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defers this insertion to the children of this quadtree
|
* Defers this insertion to the children of this quadtree
|
||||||
* @param item
|
* @param item The item to insert
|
||||||
*/
|
*/
|
||||||
protected deferInsert(item: T): void {
|
protected deferInsert(item: T): void {
|
||||||
this.nw.insert(item);
|
this.nw.insert(item);
|
||||||
|
@ -222,10 +222,6 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
this.se.insert(item);
|
this.se.insert(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the quadtree for demo purposes.
|
|
||||||
* @param ctx
|
|
||||||
*/
|
|
||||||
public render_demo(ctx: CanvasRenderingContext2D, origin: Vec2, zoom: number): void {
|
public render_demo(ctx: CanvasRenderingContext2D, origin: Vec2, zoom: number): void {
|
||||||
ctx.strokeStyle = "#0000FF";
|
ctx.strokeStyle = "#0000FF";
|
||||||
ctx.strokeRect((this.boundary.x - this.boundary.hw - origin.x)*zoom, (this.boundary.y - this.boundary.hh - origin.y)*zoom, 2*this.boundary.hw*zoom, 2*this.boundary.hh*zoom);
|
ctx.strokeRect((this.boundary.x - this.boundary.hw - origin.x)*zoom, (this.boundary.y - this.boundary.hh - origin.y)*zoom, 2*this.boundary.hw*zoom, 2*this.boundary.hh*zoom);
|
||||||
|
@ -238,6 +234,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
forEach(func: Function): void {
|
forEach(func: Function): void {
|
||||||
// If divided, send the call down
|
// If divided, send the call down
|
||||||
if(this.divided){
|
if(this.divided){
|
||||||
|
@ -253,9 +250,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// @implemented
|
||||||
* Clear the items in this quadtree
|
|
||||||
*/
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
if(this.nw){
|
if(this.nw){
|
||||||
this.nw.clear();
|
this.nw.clear();
|
||||||
|
@ -271,7 +266,6 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
this.items.length = 0;
|
this.items.length = 0;
|
||||||
|
|
||||||
this.divided = false;
|
this.divided = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,77 +2,64 @@ import Shape from "./Shape";
|
||||||
import Vec2 from "../Vec2";
|
import Vec2 from "../Vec2";
|
||||||
import MathUtils from "../../Utils/MathUtils";
|
import MathUtils from "../../Utils/MathUtils";
|
||||||
import Circle from "./Circle";
|
import Circle from "./Circle";
|
||||||
import Debug from "../../Debug/Debug";
|
import Hit from "../Physics/Hit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Axis-Aligned Bounding Box. In other words, a rectangle that is always aligned to the x-y grid.
|
||||||
|
* Inspired by the helpful collision documentation @link(here)(https://noonat.github.io/intersect/).
|
||||||
|
*/
|
||||||
export default class AABB extends Shape {
|
export default class AABB extends Shape {
|
||||||
|
|
||||||
center: Vec2;
|
center: Vec2;
|
||||||
halfSize: Vec2;
|
halfSize: Vec2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AABB
|
||||||
|
* @param center The center of the AABB
|
||||||
|
* @param halfSize The half size of the AABB - The distance from the center to an edge in x and y
|
||||||
|
*/
|
||||||
constructor(center?: Vec2, halfSize?: Vec2){
|
constructor(center?: Vec2, halfSize?: Vec2){
|
||||||
super();
|
super();
|
||||||
this.center = center ? center : new Vec2(0, 0);
|
this.center = center ? center : new Vec2(0, 0);
|
||||||
this.halfSize = halfSize ? halfSize : new Vec2(0, 0);
|
this.halfSize = halfSize ? halfSize : new Vec2(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
get x(): number {
|
// @override
|
||||||
return this.center.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
get y(): number {
|
|
||||||
return this.center.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hw(): number {
|
|
||||||
return this.halfSize.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hh(): number {
|
|
||||||
return this.halfSize.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
get top(): number {
|
|
||||||
return this.y - this.hh;
|
|
||||||
}
|
|
||||||
|
|
||||||
get bottom(): number {
|
|
||||||
return this.y + this.hh;
|
|
||||||
}
|
|
||||||
|
|
||||||
get left(): number {
|
|
||||||
return this.x - this.hw;
|
|
||||||
}
|
|
||||||
|
|
||||||
get right(): number {
|
|
||||||
return this.x + this.hw;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBoundingRect(): AABB {
|
getBoundingRect(): AABB {
|
||||||
return this.clone();
|
return this.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getBoundingCircle(): Circle {
|
getBoundingCircle(): Circle {
|
||||||
let r = Math.max(this.hw, this.hh)
|
let r = Math.max(this.hw, this.hh)
|
||||||
return new Circle(this.center.clone(), r);
|
return new Circle(this.center.clone(), r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @deprecated
|
||||||
getHalfSize(): Vec2 {
|
getHalfSize(): Vec2 {
|
||||||
return this.halfSize;
|
return this.halfSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @deprecated
|
||||||
setHalfSize(halfSize: Vec2): void {
|
setHalfSize(halfSize: Vec2): void {
|
||||||
this.halfSize = halfSize;
|
this.halfSize = halfSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - move these all to the Shape class
|
||||||
/**
|
/**
|
||||||
* A simple boolean check of whether this AABB contains a point
|
* A simple boolean check of whether this AABB contains a point
|
||||||
* @param point
|
* @param point The point to check
|
||||||
|
* @returns A boolean representing whether this AABB contains the specified point
|
||||||
*/
|
*/
|
||||||
containsPoint(point: Vec2): boolean {
|
containsPoint(point: Vec2): boolean {
|
||||||
return point.x >= this.x - this.hw && point.x <= this.x + this.hw
|
return point.x >= this.x - this.hw && point.x <= this.x + this.hw
|
||||||
&& point.y >= this.y - this.hh && point.y <= this.y + this.hh
|
&& point.y >= this.y - this.hh && point.y <= this.y + this.hh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple boolean check of whether this AABB contains a point
|
||||||
|
* @param point The point to check
|
||||||
|
* @returns A boolean representing whether this AABB contains the specified point
|
||||||
|
*/
|
||||||
intersectPoint(point: Vec2): boolean {
|
intersectPoint(point: Vec2): boolean {
|
||||||
let dx = point.x - this.x;
|
let dx = point.x - this.x;
|
||||||
let px = this.hw - Math.abs(dx);
|
let px = this.hw - Math.abs(dx);
|
||||||
|
@ -94,7 +81,8 @@ export default class AABB extends Shape {
|
||||||
/**
|
/**
|
||||||
* A boolean check of whether this AABB contains a point with soft left and top boundaries.
|
* A boolean check of whether this AABB contains a point with soft left and top boundaries.
|
||||||
* In other words, if the top left is (0, 0), the point (0, 0) is not in the AABB
|
* In other words, if the top left is (0, 0), the point (0, 0) is not in the AABB
|
||||||
* @param point
|
* @param point The point to check
|
||||||
|
* @returns A boolean representing whether this AABB contains the specified point
|
||||||
*/
|
*/
|
||||||
containsPointSoft(point: Vec2): boolean {
|
containsPointSoft(point: Vec2): boolean {
|
||||||
return point.x > this.x - this.hw && point.x <= this.x + this.hw
|
return point.x > this.x - this.hw && point.x <= this.x + this.hw
|
||||||
|
@ -105,10 +93,9 @@ export default class AABB extends Shape {
|
||||||
/**
|
/**
|
||||||
* Returns the data from the intersection of this AABB with a line segment from a point in a direction
|
* Returns the data from the intersection of this AABB with a line segment from a point in a direction
|
||||||
* @param point The point that the line segment starts from
|
* @param point The point that the line segment starts from
|
||||||
* @param direction The direction the point will go
|
* @param delta The direction and distance of the segment
|
||||||
* @param distance The length of the line segment, if the direction is a unit vector
|
* @param padding Pads the AABB to make it wider for the intersection test
|
||||||
* @param paddingX Pads the AABB in the x axis
|
* @returns The Hit object representing the intersection, or null if there was no intersection
|
||||||
* @param paddingY Pads the AABB in the y axis
|
|
||||||
*/
|
*/
|
||||||
intersectSegment(point: Vec2, delta: Vec2, padding?: Vec2): Hit {
|
intersectSegment(point: Vec2, delta: Vec2, padding?: Vec2): Hit {
|
||||||
let paddingX = padding ? padding.x : 0;
|
let paddingX = padding ? padding.x : 0;
|
||||||
|
@ -172,6 +159,7 @@ export default class AABB extends Shape {
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
overlaps(other: Shape): boolean {
|
overlaps(other: Shape): boolean {
|
||||||
if(other instanceof AABB){
|
if(other instanceof AABB){
|
||||||
return this.overlapsAABB(other);
|
return this.overlapsAABB(other);
|
||||||
|
@ -181,9 +169,10 @@ export default class AABB extends Shape {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple boolean check of whether this AABB overlaps another
|
* A simple boolean check of whether this AABB overlaps another
|
||||||
* @param other
|
* @param other The other AABB to check against
|
||||||
|
* @returns True if this AABB overlaps the other, false otherwise
|
||||||
*/
|
*/
|
||||||
overlapsAABB(other: AABB): boolean {
|
protected overlapsAABB(other: AABB): boolean {
|
||||||
let dx = other.x - this.x;
|
let dx = other.x - this.x;
|
||||||
let px = this.hw + other.hw - Math.abs(dx);
|
let px = this.hw + other.hw - Math.abs(dx);
|
||||||
|
|
||||||
|
@ -201,7 +190,11 @@ export default class AABB extends Shape {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - Implement this generally and use it in the tilemap
|
/**
|
||||||
|
* Calculates the area of the overlap between this AABB and another
|
||||||
|
* @param other The other AABB
|
||||||
|
* @returns The area of the overlap between the AABBs
|
||||||
|
*/
|
||||||
overlapArea(other: AABB): number {
|
overlapArea(other: AABB): number {
|
||||||
let leftx = Math.max(this.x - this.hw, other.x - other.hw);
|
let leftx = Math.max(this.x - this.hw, other.x - other.hw);
|
||||||
let rightx = Math.min(this.x + this.hw, other.x + other.hw);
|
let rightx = Math.min(this.x + this.hw, other.x + other.hw);
|
||||||
|
@ -241,19 +234,16 @@ export default class AABB extends Shape {
|
||||||
this.halfSize.set(centerX - minX, centerY - minY);
|
this.halfSize.set(centerX - minX, centerY - minY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
clone(): AABB {
|
clone(): AABB {
|
||||||
return new AABB(this.center.clone(), this.halfSize.clone());
|
return new AABB(this.center.clone(), this.halfSize.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this AABB to a string format
|
||||||
|
* @returns (center: (x, y), halfSize: (x, y))
|
||||||
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return "(center: " + this.center.toString() + ", half-size: " + this.halfSize.toString() + ")"
|
return "(center: " + this.center.toString() + ", half-size: " + this.halfSize.toString() + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Hit {
|
|
||||||
time: number;
|
|
||||||
nearTimes: Vec2 = Vec2.ZERO;
|
|
||||||
pos: Vec2 = Vec2.ZERO;
|
|
||||||
delta: Vec2 = Vec2.ZERO;
|
|
||||||
normal: Vec2 = Vec2.ZERO;
|
|
||||||
}
|
|
|
@ -2,10 +2,18 @@ import Vec2 from "../Vec2";
|
||||||
import AABB from "./AABB";
|
import AABB from "./AABB";
|
||||||
import Shape from "./Shape";
|
import Shape from "./Shape";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Circle
|
||||||
|
*/
|
||||||
export default class Circle extends Shape {
|
export default class Circle extends Shape {
|
||||||
private _center: Vec2;
|
private _center: Vec2;
|
||||||
private radius: number;
|
private radius: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Circle
|
||||||
|
* @param center The center of the circle
|
||||||
|
* @param radius The radius of the circle
|
||||||
|
*/
|
||||||
constructor(center: Vec2, radius: number) {
|
constructor(center: Vec2, radius: number) {
|
||||||
super();
|
super();
|
||||||
this._center = center ? center : new Vec2(0, 0);
|
this._center = center ? center : new Vec2(0, 0);
|
||||||
|
@ -24,18 +32,22 @@ export default class Circle extends Shape {
|
||||||
return new Vec2(this.radius, this.radius);
|
return new Vec2(this.radius, this.radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getBoundingRect(): AABB {
|
getBoundingRect(): AABB {
|
||||||
return new AABB(this._center.clone(), new Vec2(this.radius, this.radius));
|
return new AABB(this._center.clone(), new Vec2(this.radius, this.radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getBoundingCircle(): Circle {
|
getBoundingCircle(): Circle {
|
||||||
return this.clone();
|
return this.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
overlaps(other: Shape): boolean {
|
overlaps(other: Shape): boolean {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
clone(): Circle {
|
clone(): Circle {
|
||||||
return new Circle(this._center.clone(), this.radius);
|
return new Circle(this._center.clone(), this.radius);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ import Vec2 from "../Vec2";
|
||||||
import AABB from "./AABB";
|
import AABB from "./AABB";
|
||||||
import Circle from "./Circle";
|
import Circle from "./Circle";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract Shape class that acts as an interface for better interactions with subclasses.
|
||||||
|
*/
|
||||||
export default abstract class Shape {
|
export default abstract class Shape {
|
||||||
abstract get center(): Vec2;
|
abstract get center(): Vec2;
|
||||||
|
|
||||||
|
@ -9,16 +12,63 @@ export default abstract class Shape {
|
||||||
|
|
||||||
abstract get halfSize(): Vec2;
|
abstract get halfSize(): Vec2;
|
||||||
|
|
||||||
/** Gets a bounding rectangle for this shape */
|
get x(): number {
|
||||||
|
return this.center.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
get y(): number {
|
||||||
|
return this.center.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hw(): number {
|
||||||
|
return this.halfSize.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hh(): number {
|
||||||
|
return this.halfSize.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
get top(): number {
|
||||||
|
return this.y - this.hh;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bottom(): number {
|
||||||
|
return this.y + this.hh;
|
||||||
|
}
|
||||||
|
|
||||||
|
get left(): number {
|
||||||
|
return this.x - this.hw;
|
||||||
|
}
|
||||||
|
|
||||||
|
get right(): number {
|
||||||
|
return this.x + this.hw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a bounding rectangle for this shape. Warning - may be the same as this Shape.
|
||||||
|
* For instance, the bounding circle of an AABB is itself. Use clone() if you need a new shape.
|
||||||
|
* @returns An AABB that bounds this shape
|
||||||
|
*/
|
||||||
abstract getBoundingRect(): AABB;
|
abstract getBoundingRect(): AABB;
|
||||||
|
|
||||||
/** Gets a bounding circle for this shape */
|
/**
|
||||||
|
* Gets a bounding circle for this shape. Warning - may be the same as this Shape.
|
||||||
|
* For instance, the bounding circle of a Circle is itself. Use clone() if you need a new shape.
|
||||||
|
* @returns A Circle that bounds this shape
|
||||||
|
*/
|
||||||
abstract getBoundingCircle(): Circle;
|
abstract getBoundingCircle(): Circle;
|
||||||
|
|
||||||
/** Returns a copy of this Shape */
|
/**
|
||||||
|
* Returns a copy of this Shape
|
||||||
|
* @returns A new copy of this shape
|
||||||
|
*/
|
||||||
abstract clone(): Shape;
|
abstract clone(): Shape;
|
||||||
|
|
||||||
/** Checks if this shape overlaps another */
|
/**
|
||||||
|
* Checks if this shape overlaps another
|
||||||
|
* @param other The other shape to check against
|
||||||
|
* @returns a boolean that represents whether this Shape overlaps the other one
|
||||||
|
*/
|
||||||
abstract overlaps(other: Shape): boolean;
|
abstract overlaps(other: Shape): boolean;
|
||||||
|
|
||||||
static getTimeOfCollision(A: Shape, velA: Vec2, B: Shape, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
|
static getTimeOfCollision(A: Shape, velA: Vec2, B: Shape, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
import { AnimationData } from "../Rendering/Animations/AnimationTypes";
|
import { AnimationData } from "../Rendering/Animations/AnimationTypes";
|
||||||
|
|
||||||
|
/** A class representing data contained in a spritesheet.
|
||||||
|
* Spritesheets are the images associated with sprites, and contain images indexed in a grid, which
|
||||||
|
* correspond to animations.
|
||||||
|
*/
|
||||||
export default class Spritesheet {
|
export default class Spritesheet {
|
||||||
|
/** The name of the spritesheet */
|
||||||
name: string;
|
name: string;
|
||||||
|
/** The image key of the spritesheet */
|
||||||
spriteSheetImage: string;
|
spriteSheetImage: string;
|
||||||
|
/** The width of the sprite */
|
||||||
spriteWidth: number;
|
spriteWidth: number;
|
||||||
|
/** The height of the sprite */
|
||||||
spriteHeight: number;
|
spriteHeight: number;
|
||||||
|
/** The number of columns in the spritesheet */
|
||||||
columns: number;
|
columns: number;
|
||||||
|
/** The number of rows in the spritesheet */
|
||||||
rows: number;
|
rows: number;
|
||||||
|
/** An array of the animations associated with this spritesheet */
|
||||||
animations: Array<AnimationData>;
|
animations: Array<AnimationData>;
|
||||||
}
|
}
|
|
@ -4,10 +4,19 @@ import Collection from "./Collection";
|
||||||
* A LIFO stack with items of type T
|
* A LIFO stack with items of type T
|
||||||
*/
|
*/
|
||||||
export default class Stack<T> implements Collection {
|
export default class Stack<T> implements Collection {
|
||||||
readonly MAX_ELEMENTS: number;
|
/** The maximum number of elements in the Stack */
|
||||||
private stack: Array<T>;
|
private readonly MAX_ELEMENTS: number;
|
||||||
|
|
||||||
|
/** The internal representation of the stack */
|
||||||
|
private stack: Array<T>;
|
||||||
|
|
||||||
|
/** The head of the stack */
|
||||||
private head: number;
|
private head: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new stack
|
||||||
|
* @param maxElements The maximum size of the stack
|
||||||
|
*/
|
||||||
constructor(maxElements: number = 100){
|
constructor(maxElements: number = 100){
|
||||||
this.MAX_ELEMENTS = maxElements;
|
this.MAX_ELEMENTS = maxElements;
|
||||||
this.stack = new Array<T>(this.MAX_ELEMENTS);
|
this.stack = new Array<T>(this.MAX_ELEMENTS);
|
||||||
|
@ -28,6 +37,7 @@ export default class Stack<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an item from the top of the stack
|
* Removes an item from the top of the stack
|
||||||
|
* @returns The item at the top of the stack
|
||||||
*/
|
*/
|
||||||
pop(): T {
|
pop(): T {
|
||||||
if(this.head === -1){
|
if(this.head === -1){
|
||||||
|
@ -39,6 +49,7 @@ export default class Stack<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the element currently at the top of the stack
|
* Returns the element currently at the top of the stack
|
||||||
|
* @returns The item at the top of the stack
|
||||||
*/
|
*/
|
||||||
peek(): T {
|
peek(): T {
|
||||||
if(this.head === -1){
|
if(this.head === -1){
|
||||||
|
@ -47,11 +58,14 @@ export default class Stack<T> implements Collection {
|
||||||
return this.stack[this.head];
|
return this.stack[this.head];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if this stack is empty */
|
/** Returns true if this stack is empty
|
||||||
|
* @returns A boolean that represents whether or not the stack is empty
|
||||||
|
*/
|
||||||
isEmpty(): boolean {
|
isEmpty(): boolean {
|
||||||
return this.head === -1;
|
return this.head === -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.forEach((item, index) => delete this.stack[index]);
|
this.forEach((item, index) => delete this.stack[index]);
|
||||||
this.head = -1;
|
this.head = -1;
|
||||||
|
@ -59,11 +73,13 @@ export default class Stack<T> implements Collection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of items currently in the stack
|
* Returns the number of items currently in the stack
|
||||||
|
* @returns The number of items in the stack
|
||||||
*/
|
*/
|
||||||
size(): number {
|
size(): number {
|
||||||
return this.head + 1;
|
return this.head + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
forEach(func: (item: T, index?: number) => void): void{
|
forEach(func: (item: T, index?: number) => void): void{
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while(i <= this.head){
|
while(i <= this.head){
|
||||||
|
@ -72,6 +88,10 @@ export default class Stack<T> implements Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this stack into a string format
|
||||||
|
* @returns A string representing this stack
|
||||||
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
let retval = "";
|
let retval = "";
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,21 @@ import GameEvent from "../../Events/GameEvent";
|
||||||
import { Updateable } from "../Interfaces/Descriptors";
|
import { Updateable } from "../Interfaces/Descriptors";
|
||||||
import StateMachine from "./StateMachine";
|
import StateMachine from "./StateMachine";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract implementation of a state for a @reference[StateMachine].
|
||||||
|
* This class should be extended to allow for custom state behaviors.
|
||||||
|
*/
|
||||||
export default abstract class State implements Updateable {
|
export default abstract class State implements Updateable {
|
||||||
|
/** The StateMachine that uses this State */
|
||||||
protected parent: StateMachine;
|
protected parent: StateMachine;
|
||||||
|
|
||||||
|
/** An event emitter */
|
||||||
protected emitter: Emitter;
|
protected emitter: Emitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new State
|
||||||
|
* @param parent The parent StateMachine of this state
|
||||||
|
*/
|
||||||
constructor(parent: StateMachine) {
|
constructor(parent: StateMachine) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.emitter = new Emitter();
|
this.emitter = new Emitter();
|
||||||
|
@ -18,11 +29,12 @@ export default abstract class State implements Updateable {
|
||||||
abstract onEnter(): void;
|
abstract onEnter(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an input event, such as taking damage.
|
* A lifecycle method that handles an input event, such as taking damage.
|
||||||
* @param event
|
* @param event The GameEvent to process
|
||||||
*/
|
*/
|
||||||
abstract handleInput(event: GameEvent): void;
|
abstract handleInput(event: GameEvent): void;
|
||||||
|
|
||||||
|
// @implemented
|
||||||
abstract update(deltaT: number): void;
|
abstract update(deltaT: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +46,7 @@ export default abstract class State implements Updateable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the state is ending.
|
* A lifecycle method is called when the state is ending.
|
||||||
*/
|
*/
|
||||||
abstract onExit(): void;
|
abstract onExit(): void;
|
||||||
}
|
}
|
|
@ -8,18 +8,29 @@ import { Updateable } from "../Interfaces/Descriptors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of a Push Down Automata State machine. States can also be hierarchical
|
* An implementation of a Push Down Automata State machine. States can also be hierarchical
|
||||||
* for more flexibility, as described in Game Programming Principles.
|
* for more flexibility, as described in @link(Game Programming Patterns)(https://gameprogrammingpatterns.com/state.html).
|
||||||
*/
|
*/
|
||||||
export default class StateMachine implements Updateable {
|
export default class StateMachine implements Updateable {
|
||||||
|
/** A stack of the current states */
|
||||||
protected stack: Stack<State>;
|
protected stack: Stack<State>;
|
||||||
|
/** A mape of state keys to actual state instances */
|
||||||
protected stateMap: Map<State>;
|
protected stateMap: Map<State>;
|
||||||
|
/** The current state */
|
||||||
protected currentState: State;
|
protected currentState: State;
|
||||||
|
/** An event receiver */
|
||||||
protected receiver: Receiver;
|
protected receiver: Receiver;
|
||||||
|
/** An event emitter */
|
||||||
protected emitter: Emitter;
|
protected emitter: Emitter;
|
||||||
|
/** A boolean representing whether or not this StateMachine is currently active */
|
||||||
protected active: boolean;
|
protected active: boolean;
|
||||||
|
/** A boolean representing whether or not this StateMachine should emit an event on state change */
|
||||||
protected emitEventOnStateChange: boolean;
|
protected emitEventOnStateChange: boolean;
|
||||||
|
/** The name of the event to be emitted on state change */
|
||||||
protected stateChangeEventName: string;
|
protected stateChangeEventName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new StateMachine
|
||||||
|
*/
|
||||||
constructor(){
|
constructor(){
|
||||||
this.stack = new Stack();
|
this.stack = new Stack();
|
||||||
this.stateMap = new Map();
|
this.stateMap = new Map();
|
||||||
|
@ -56,7 +67,7 @@ export default class StateMachine implements Updateable {
|
||||||
* Initializes this state machine with an initial state and sets it running
|
* Initializes this state machine with an initial state and sets it running
|
||||||
* @param initialState The name of initial state of the state machine
|
* @param initialState The name of initial state of the state machine
|
||||||
*/
|
*/
|
||||||
initialize(initialState: string){
|
initialize(initialState: string): void {
|
||||||
this.stack.push(this.stateMap.get(initialState));
|
this.stack.push(this.stateMap.get(initialState));
|
||||||
this.currentState = this.stack.peek();
|
this.currentState = this.stack.peek();
|
||||||
this.setActive(true);
|
this.setActive(true);
|
||||||
|
@ -109,6 +120,7 @@ export default class StateMachine implements Updateable {
|
||||||
this.currentState.handleInput(event);
|
this.currentState.handleInput(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
// If the state machine isn't currently active, ignore all events and don't update
|
// If the state machine isn't currently active, ignore all events and don't update
|
||||||
if(!this.active){
|
if(!this.active){
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ignorePage
|
||||||
/**
|
/**
|
||||||
* a representation of Tiled's tilemap data
|
* a representation of Tiled's tilemap data
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,12 +7,19 @@ import { TiledTilesetData } from "./TiledData";
|
||||||
* with a startIndex if required (as it is with Tiled using two images in one tilset).
|
* with a startIndex if required (as it is with Tiled using two images in one tilset).
|
||||||
*/
|
*/
|
||||||
export default class Tileset {
|
export default class Tileset {
|
||||||
|
/** The key of the image used by this tileset */
|
||||||
protected imageKey: string;
|
protected imageKey: string;
|
||||||
|
/** The size of the tileset image */
|
||||||
protected imageSize: Vec2;
|
protected imageSize: Vec2;
|
||||||
|
/** The index of 0th image of this tileset */
|
||||||
protected startIndex: number;
|
protected startIndex: number;
|
||||||
|
/** The index of the last image of this tilset */
|
||||||
protected endIndex: number;
|
protected endIndex: number;
|
||||||
|
/** The size of the tiles in this tileset */
|
||||||
protected tileSize: Vec2;
|
protected tileSize: Vec2;
|
||||||
|
/** The number of rows in this tileset */
|
||||||
protected numRows: number;
|
protected numRows: number;
|
||||||
|
/** The number of columns in this tileset */
|
||||||
protected numCols: number;
|
protected numCols: number;
|
||||||
|
|
||||||
// TODO: Change this to be more general and work with other tileset formats
|
// TODO: Change this to be more general and work with other tileset formats
|
||||||
|
@ -35,6 +42,10 @@ export default class Tileset {
|
||||||
this.imageSize = new Vec2(tiledData.imagewidth, tiledData.imageheight);
|
this.imageSize = new Vec2(tiledData.imagewidth, tiledData.imageheight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the image key associated with this tilemap
|
||||||
|
* @returns The image key of this tilemap
|
||||||
|
*/
|
||||||
getImageKey(): string {
|
getImageKey(): string {
|
||||||
return this.imageKey;
|
return this.imageKey;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +53,7 @@ export default class Tileset {
|
||||||
/**
|
/**
|
||||||
* Returns a Vec2 containing the left and top offset from the image origin for this tile.
|
* Returns a Vec2 containing the left and top offset from the image origin for this tile.
|
||||||
* @param tileIndex The index of the tile from startIndex to endIndex of this tileset
|
* @param tileIndex The index of the tile from startIndex to endIndex of this tileset
|
||||||
|
* @returns A Vec2 containing the offset for the specified tile.
|
||||||
*/
|
*/
|
||||||
getImageOffsetForTile(tileIndex: number): Vec2 {
|
getImageOffsetForTile(tileIndex: number): Vec2 {
|
||||||
// Get the true index
|
// Get the true index
|
||||||
|
@ -58,18 +70,34 @@ export default class Tileset {
|
||||||
return new Vec2(left, top);
|
return new Vec2(left, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the start index
|
||||||
|
* @returns The start index
|
||||||
|
*/
|
||||||
getStartIndex(): number {
|
getStartIndex(): number {
|
||||||
return this.startIndex;
|
return this.startIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the tile set
|
||||||
|
* @returns A Vec2 containing the tile size
|
||||||
|
*/
|
||||||
getTileSize(): Vec2 {
|
getTileSize(): Vec2 {
|
||||||
return this.tileSize;
|
return this.tileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of rows in the tileset
|
||||||
|
* @returns The number of rows
|
||||||
|
*/
|
||||||
getNumRows(): number {
|
getNumRows(): number {
|
||||||
return this.numRows;
|
return this.numRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of columns in the tilset
|
||||||
|
* @returns The number of columns
|
||||||
|
*/
|
||||||
getNumCols(): number {
|
getNumCols(): number {
|
||||||
return this.numCols;
|
return this.numCols;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +106,11 @@ export default class Tileset {
|
||||||
return this.endIndex - this.startIndex + 1;
|
return this.endIndex - this.startIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether or not this tilset contains the specified tile index. This is used for rendering.
|
||||||
|
* @param tileIndex The index of the tile to check
|
||||||
|
* @returns A boolean representing whether or not this tilset uses the specified index
|
||||||
|
*/
|
||||||
hasTile(tileIndex: number): boolean {
|
hasTile(tileIndex: number): boolean {
|
||||||
return tileIndex >= this.startIndex && tileIndex <= this.endIndex;
|
return tileIndex >= this.startIndex && tileIndex <= this.endIndex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import MathUtils from "../Utils/MathUtils";
|
||||||
export default class Vec2 {
|
export default class Vec2 {
|
||||||
|
|
||||||
// Store x and y in an array
|
// Store x and y in an array
|
||||||
/** The array that stores the actual vector values */
|
/** The array that stores the actual vector values x and y */
|
||||||
private vec: Float32Array;
|
private vec: Float32Array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,21 +77,25 @@ export default class Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The squared magnitude of the vector
|
* The squared magnitude of the vector. This tends to be faster, so use it in situations where taking the
|
||||||
|
* square root doesn't matter, like for comparing distances.
|
||||||
|
* @returns The squared magnitude of the vector
|
||||||
*/
|
*/
|
||||||
magSq(): number {
|
magSq(): number {
|
||||||
return this.x*this.x + this.y*this.y;
|
return this.x*this.x + this.y*this.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The magnitude of the vector
|
* The magnitude of the vector.
|
||||||
|
* @returns The magnitude of the vector.
|
||||||
*/
|
*/
|
||||||
mag(): number {
|
mag(): number {
|
||||||
return Math.sqrt(this.magSq());
|
return Math.sqrt(this.magSq());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this vector as a unit vector - Equivalent to dividing x and y by the magnitude
|
* Divdes x and y by the magnitude to obtain the unit vector in the direction of this vector.
|
||||||
|
* @returns This vector as a unit vector.
|
||||||
*/
|
*/
|
||||||
normalize(): Vec2 {
|
normalize(): Vec2 {
|
||||||
if(this.x === 0 && this.y === 0) return this;
|
if(this.x === 0 && this.y === 0) return this;
|
||||||
|
@ -102,7 +106,8 @@ export default class Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new vector that is the normalized version of this one
|
* Works like normalize(), but returns a new Vec2
|
||||||
|
* @returns A new vector that is the unit vector for this one
|
||||||
*/
|
*/
|
||||||
normalized(): Vec2 {
|
normalized(): Vec2 {
|
||||||
let mag = this.mag();
|
let mag = this.mag();
|
||||||
|
@ -110,7 +115,8 @@ export default class Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the x and y elements of this vector to zero
|
* Sets the x and y elements of this vector to zero.
|
||||||
|
* @returns This vector, with x and y set to zero.
|
||||||
*/
|
*/
|
||||||
zero(): Vec2 {
|
zero(): Vec2 {
|
||||||
return this.set(0, 0);
|
return this.set(0, 0);
|
||||||
|
@ -120,6 +126,7 @@ export default class Vec2 {
|
||||||
* Sets the vector's x and y based on the angle provided. Goes counter clockwise.
|
* Sets the vector's x and y based on the angle provided. Goes counter clockwise.
|
||||||
* @param angle The angle in radians
|
* @param angle The angle in radians
|
||||||
* @param radius The magnitude of the vector at the specified angle
|
* @param radius The magnitude of the vector at the specified angle
|
||||||
|
* @returns This vector.
|
||||||
*/
|
*/
|
||||||
setToAngle(angle: number, radius: number = 1): Vec2 {
|
setToAngle(angle: number, radius: number = 1): Vec2 {
|
||||||
this.x = MathUtils.floorToPlace(Math.cos(angle)*radius, 5);
|
this.x = MathUtils.floorToPlace(Math.cos(angle)*radius, 5);
|
||||||
|
@ -129,7 +136,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a vector that point from this vector to another one
|
* Returns a vector that point from this vector to another one
|
||||||
* @param other
|
* @param other The vector to point to
|
||||||
|
* @returns A new Vec2 that points from this vector to the one provided
|
||||||
*/
|
*/
|
||||||
vecTo(other: Vec2): Vec2 {
|
vecTo(other: Vec2): Vec2 {
|
||||||
return new Vec2(other.x - this.x, other.y - this.y);
|
return new Vec2(other.x - this.x, other.y - this.y);
|
||||||
|
@ -137,7 +145,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a vector containing the direction from this vector to another
|
* Returns a vector containing the direction from this vector to another
|
||||||
* @param other
|
* @param other The vector to point to
|
||||||
|
* @returns A new Vec2 that points from this vector to the one provided. This new Vec2 will be a unit vector.
|
||||||
*/
|
*/
|
||||||
dirTo(other: Vec2): Vec2 {
|
dirTo(other: Vec2): Vec2 {
|
||||||
return this.vecTo(other).normalize();
|
return this.vecTo(other).normalize();
|
||||||
|
@ -145,7 +154,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps the vector's direction, but sets its magnitude to be the provided magnitude
|
* Keeps the vector's direction, but sets its magnitude to be the provided magnitude
|
||||||
* @param magnitude
|
* @param magnitude The magnitude the vector should be
|
||||||
|
* @returns This vector with its magnitude set to the new magnitude
|
||||||
*/
|
*/
|
||||||
scaleTo(magnitude: number): Vec2 {
|
scaleTo(magnitude: number): Vec2 {
|
||||||
return this.normalize().scale(magnitude);
|
return this.normalize().scale(magnitude);
|
||||||
|
@ -153,8 +163,9 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scales x and y by the number provided, or if two number are provided, scales them individually.
|
* Scales x and y by the number provided, or if two number are provided, scales them individually.
|
||||||
* @param factor
|
* @param factor The scaling factor for the vector, or for only the x-component if yFactor is provided
|
||||||
* @param yFactor
|
* @param yFactor The scaling factor for the y-component of the vector
|
||||||
|
* @returns This vector after scaling
|
||||||
*/
|
*/
|
||||||
scale(factor: number, yFactor: number = null): Vec2 {
|
scale(factor: number, yFactor: number = null): Vec2 {
|
||||||
if(yFactor !== null){
|
if(yFactor !== null){
|
||||||
|
@ -169,8 +180,9 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a scaled version of this vector without modifying it.
|
* Returns a scaled version of this vector without modifying it.
|
||||||
* @param factor
|
* @param factor The scaling factor for the vector, or for only the x-component if yFactor is provided
|
||||||
* @param yFactor
|
* @param yFactor The scaling factor for the y-component of the vector
|
||||||
|
* @returns A new vector that has the values of this vector after scaling
|
||||||
*/
|
*/
|
||||||
scaled(factor: number, yFactor: number = null): Vec2 {
|
scaled(factor: number, yFactor: number = null): Vec2 {
|
||||||
return this.clone().scale(factor, yFactor);
|
return this.clone().scale(factor, yFactor);
|
||||||
|
@ -179,6 +191,7 @@ export default class Vec2 {
|
||||||
/**
|
/**
|
||||||
* Rotates the vector counter-clockwise by the angle amount specified
|
* Rotates the vector counter-clockwise by the angle amount specified
|
||||||
* @param angle The angle to rotate by in radians
|
* @param angle The angle to rotate by in radians
|
||||||
|
* @returns This vector after rotation.
|
||||||
*/
|
*/
|
||||||
rotateCCW(angle: number): Vec2 {
|
rotateCCW(angle: number): Vec2 {
|
||||||
let cs = Math.cos(angle);
|
let cs = Math.cos(angle);
|
||||||
|
@ -192,8 +205,9 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the vectors coordinates to be the ones provided
|
* Sets the vectors coordinates to be the ones provided
|
||||||
* @param x
|
* @param x The new x value for this vector
|
||||||
* @param y
|
* @param y The new y value for this vector
|
||||||
|
* @returns This vector
|
||||||
*/
|
*/
|
||||||
set(x: number, y: number): Vec2 {
|
set(x: number, y: number): Vec2 {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
|
@ -204,6 +218,7 @@ export default class Vec2 {
|
||||||
/**
|
/**
|
||||||
* Copies the values of the other Vec2 into this one.
|
* Copies the values of the other Vec2 into this one.
|
||||||
* @param other The Vec2 to copy
|
* @param other The Vec2 to copy
|
||||||
|
* @returns This vector with its values set to the vector provided
|
||||||
*/
|
*/
|
||||||
copy(other: Vec2): Vec2 {
|
copy(other: Vec2): Vec2 {
|
||||||
return this.set(other.x, other.y);
|
return this.set(other.x, other.y);
|
||||||
|
@ -211,7 +226,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds this vector the another vector
|
* Adds this vector the another vector
|
||||||
* @param other
|
* @param other The Vec2 to add to this one
|
||||||
|
* @returns This vector after adding the one provided
|
||||||
*/
|
*/
|
||||||
add(other: Vec2): Vec2 {
|
add(other: Vec2): Vec2 {
|
||||||
this.x += other.x;
|
this.x += other.x;
|
||||||
|
@ -221,7 +237,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtracts another vector from this vector
|
* Subtracts another vector from this vector
|
||||||
* @param other
|
* @param other The Vec2 to subtract from this one
|
||||||
|
* @returns This vector after subtracting the one provided
|
||||||
*/
|
*/
|
||||||
sub(other: Vec2): Vec2 {
|
sub(other: Vec2): Vec2 {
|
||||||
this.x -= other.x;
|
this.x -= other.x;
|
||||||
|
@ -230,8 +247,9 @@ export default class Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiplies this vector with another vector element-wise
|
* Multiplies this vector with another vector element-wise. In other words, this.x *= other.x and this.y *= other.y
|
||||||
* @param other
|
* @param other The Vec2 to multiply this one by
|
||||||
|
* @returns This vector after multiplying its components by this one
|
||||||
*/
|
*/
|
||||||
mult(other: Vec2): Vec2 {
|
mult(other: Vec2): Vec2 {
|
||||||
this.x *= other.x;
|
this.x *= other.x;
|
||||||
|
@ -240,8 +258,9 @@ export default class Vec2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divides this vector with another vector element-wise
|
* Divides this vector with another vector element-wise. In other words, this.x /= other.x and this.y /= other.y
|
||||||
* @param other
|
* @param other The vector to divide this one by
|
||||||
|
* @returns This vector after division
|
||||||
*/
|
*/
|
||||||
div(other: Vec2): Vec2 {
|
div(other: Vec2): Vec2 {
|
||||||
if(other.x === 0 || other.y === 0) throw "Divide by zero error";
|
if(other.x === 0 || other.y === 0) throw "Divide by zero error";
|
||||||
|
@ -252,7 +271,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the squared distance between this vector and another vector
|
* Returns the squared distance between this vector and another vector
|
||||||
* @param other
|
* @param other The vector to compute distance squared to
|
||||||
|
* @returns The squared distance between this vector and the one provided
|
||||||
*/
|
*/
|
||||||
distanceSqTo(other: Vec2): number {
|
distanceSqTo(other: Vec2): number {
|
||||||
return (this.x - other.x)*(this.x - other.x) + (this.y - other.y)*(this.y - other.y);
|
return (this.x - other.x)*(this.x - other.x) + (this.y - other.y)*(this.y - other.y);
|
||||||
|
@ -260,7 +280,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the distance between this vector and another vector
|
* Returns the distance between this vector and another vector
|
||||||
* @param other
|
* @param other The vector to compute distance to
|
||||||
|
* @returns The distance between this vector and the one provided
|
||||||
*/
|
*/
|
||||||
distanceTo(other: Vec2): number {
|
distanceTo(other: Vec2): number {
|
||||||
return Math.sqrt(this.distanceSqTo(other));
|
return Math.sqrt(this.distanceSqTo(other));
|
||||||
|
@ -268,7 +289,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the dot product of this vector and another
|
* Returns the dot product of this vector and another
|
||||||
* @param other
|
* @param other The vector to compute the dot product with
|
||||||
|
* @returns The dot product of this vector and the one provided.
|
||||||
*/
|
*/
|
||||||
dot(other: Vec2): number {
|
dot(other: Vec2): number {
|
||||||
return this.x*other.x + this.y*other.y;
|
return this.x*other.x + this.y*other.y;
|
||||||
|
@ -276,7 +298,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the angle counter-clockwise in radians from this vector to another vector
|
* Returns the angle counter-clockwise in radians from this vector to another vector
|
||||||
* @param other
|
* @param other The vector to compute the angle to
|
||||||
|
* @returns The angle, rotating CCW, from this vector to the other vector
|
||||||
*/
|
*/
|
||||||
angleToCCW(other: Vec2): number {
|
angleToCCW(other: Vec2): number {
|
||||||
let dot = this.dot(other);
|
let dot = this.dot(other);
|
||||||
|
@ -292,6 +315,7 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string representation of this vector rounded to 1 decimal point
|
* Returns a string representation of this vector rounded to 1 decimal point
|
||||||
|
* @returns This vector as a string
|
||||||
*/
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return this.toFixed();
|
return this.toFixed();
|
||||||
|
@ -299,7 +323,8 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string representation of this vector rounded to the specified number of decimal points
|
* Returns a string representation of this vector rounded to the specified number of decimal points
|
||||||
* @param numDecimalPoints
|
* @param numDecimalPoints The number of decimal points to create a string to
|
||||||
|
* @returns This vector as a string
|
||||||
*/
|
*/
|
||||||
toFixed(numDecimalPoints: number = 1): string {
|
toFixed(numDecimalPoints: number = 1): string {
|
||||||
return "(" + this.x.toFixed(numDecimalPoints) + ", " + this.y.toFixed(numDecimalPoints) + ")";
|
return "(" + this.x.toFixed(numDecimalPoints) + ", " + this.y.toFixed(numDecimalPoints) + ")";
|
||||||
|
@ -307,6 +332,7 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new vector with the same coordinates as this one.
|
* Returns a new vector with the same coordinates as this one.
|
||||||
|
* @returns A new Vec2 with the same values as this one
|
||||||
*/
|
*/
|
||||||
clone(): Vec2 {
|
clone(): Vec2 {
|
||||||
return new Vec2(this.x, this.y);
|
return new Vec2(this.x, this.y);
|
||||||
|
@ -315,6 +341,7 @@ export default class Vec2 {
|
||||||
/**
|
/**
|
||||||
* Returns true if this vector and other have the EXACT same x and y (not assured to be safe for floats)
|
* Returns true if this vector and other have the EXACT same x and y (not assured to be safe for floats)
|
||||||
* @param other The vector to check against
|
* @param other The vector to check against
|
||||||
|
* @returns A boolean representing the equality of the two vectors
|
||||||
*/
|
*/
|
||||||
strictEquals(other: Vec2): boolean {
|
strictEquals(other: Vec2): boolean {
|
||||||
return this.x === other.x && this.y === other.y;
|
return this.x === other.x && this.y === other.y;
|
||||||
|
@ -323,6 +350,7 @@ export default class Vec2 {
|
||||||
/**
|
/**
|
||||||
* Returns true if this vector and other have the same x and y
|
* Returns true if this vector and other have the same x and y
|
||||||
* @param other The vector to check against
|
* @param other The vector to check against
|
||||||
|
* @returns A boolean representing the equality of the two vectors
|
||||||
*/
|
*/
|
||||||
equals(other: Vec2): boolean {
|
equals(other: Vec2): boolean {
|
||||||
let xEq = Math.abs(this.x - other.x) < 0.0000001;
|
let xEq = Math.abs(this.x - other.x) < 0.0000001;
|
||||||
|
@ -333,6 +361,7 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this vector is the zero vector exactly (not assured to be safe for floats).
|
* Returns true if this vector is the zero vector exactly (not assured to be safe for floats).
|
||||||
|
* @returns A boolean representing the equality of this vector and the zero vector
|
||||||
*/
|
*/
|
||||||
strictIsZero(): boolean {
|
strictIsZero(): boolean {
|
||||||
return this.x === 0 && this.y === 0;
|
return this.x === 0 && this.y === 0;
|
||||||
|
@ -340,6 +369,7 @@ export default class Vec2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this x and y for this vector are both zero.
|
* Returns true if this x and y for this vector are both zero.
|
||||||
|
* @returns A boolean representing the equality of this vector and the zero vector
|
||||||
*/
|
*/
|
||||||
isZero(): boolean {
|
isZero(): boolean {
|
||||||
return Math.abs(this.x) < 0.0000001 && Math.abs(this.y) < 0.0000001;
|
return Math.abs(this.x) < 0.0000001 && Math.abs(this.y) < 0.0000001;
|
||||||
|
@ -353,18 +383,12 @@ export default class Vec2 {
|
||||||
this.onChange = f;
|
this.onChange = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the function that is called whenever this vector is changed
|
|
||||||
*/
|
|
||||||
getOnChange(): string {
|
|
||||||
return this.onChange.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs linear interpolation between two vectors
|
* Performs linear interpolation between two vectors
|
||||||
* @param a The first vector
|
* @param a The first vector
|
||||||
* @param b The second vector
|
* @param b The second vector
|
||||||
* @param t The time of the lerp, with 0 being vector A, and 1 being vector B
|
* @param t The time of the lerp, with 0 being vector A, and 1 being vector B
|
||||||
|
* @returns A new Vec2 representing the lerp between vector a and b.
|
||||||
*/
|
*/
|
||||||
static lerp(a: Vec2, b: Vec2, t: number): Vec2 {
|
static lerp(a: Vec2, b: Vec2, t: number): Vec2 {
|
||||||
return new Vec2(MathUtils.lerp(a.x, b.x, t), MathUtils.lerp(a.y, b.y, t));
|
return new Vec2(MathUtils.lerp(a.x, b.x, t), MathUtils.lerp(a.y, b.y, t));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Vec2 from "./Vec2";
|
import Vec2 from "./Vec2";
|
||||||
|
|
||||||
export default class Vec4{
|
// @ignorePage
|
||||||
|
export default class Vec4 {
|
||||||
|
|
||||||
public vec: Float32Array;
|
public vec: Float32Array;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import Map from "../DataTypes/Map";
|
import Map from "../DataTypes/Map";
|
||||||
import Vec2 from "../DataTypes/Vec2";
|
import Vec2 from "../DataTypes/Vec2";
|
||||||
import InputHandler from "../Input/InputHandler";
|
|
||||||
import GameNode from "../Nodes/GameNode";
|
import GameNode from "../Nodes/GameNode";
|
||||||
import Color from "../Utils/Color";
|
import Color from "../Utils/Color";
|
||||||
|
|
||||||
type DebugRenderFunction = (ctx: CanvasRenderingContext2D) => void;
|
/**
|
||||||
|
* A util class for rendering Debug messages to the canvas.
|
||||||
|
*/
|
||||||
export default class Debug {
|
export default class Debug {
|
||||||
|
|
||||||
/** A map of log messages to display on the screen */
|
/** A map of log messages to display on the screen */
|
||||||
|
@ -40,7 +40,7 @@ export default class Debug {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a a key from the log and stops it from keeping up space on the screen
|
* Deletes a a key from the log and stops it from keeping up space on the screen
|
||||||
* @param id
|
* @param id The id of the log item to clear
|
||||||
*/
|
*/
|
||||||
static clearLogItem(id: string): void {
|
static clearLogItem(id: string): void {
|
||||||
this.logMessages.delete(id);
|
this.logMessages.delete(id);
|
||||||
|
@ -59,6 +59,7 @@ export default class Debug {
|
||||||
* @param center The center of the box
|
* @param center The center of the box
|
||||||
* @param halfSize The dimensions of the box
|
* @param halfSize The dimensions of the box
|
||||||
* @param filled A boolean for whether or not the box is filled
|
* @param filled A boolean for whether or not the box is filled
|
||||||
|
* @param color The color of the box to draw
|
||||||
*/
|
*/
|
||||||
static drawBox(center: Vec2, halfSize: Vec2, filled: boolean, color: Color): void {
|
static drawBox(center: Vec2, halfSize: Vec2, filled: boolean, color: Color): void {
|
||||||
if(filled){
|
if(filled){
|
||||||
|
@ -72,6 +73,12 @@ export default class Debug {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a ray at the specified position
|
||||||
|
* @param from The starting position of the ray
|
||||||
|
* @param to The ending position of the ray
|
||||||
|
* @param color The color of the ray
|
||||||
|
*/
|
||||||
static drawRay(from: Vec2, to: Vec2, color: Color): void {
|
static drawRay(from: Vec2, to: Vec2, color: Color): void {
|
||||||
this.debugRenderingContext.lineWidth = 2;
|
this.debugRenderingContext.lineWidth = 2;
|
||||||
this.debugRenderingContext.strokeStyle = color.toString();
|
this.debugRenderingContext.strokeStyle = color.toString();
|
||||||
|
@ -83,16 +90,32 @@ export default class Debug {
|
||||||
this.debugRenderingContext.stroke();
|
this.debugRenderingContext.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a point at the specified position
|
||||||
|
* @param pos The position of the point
|
||||||
|
* @param color The color of the point
|
||||||
|
*/
|
||||||
static drawPoint(pos: Vec2, color: Color): void {
|
static drawPoint(pos: Vec2, color: Color): void {
|
||||||
let pointSize = 6;
|
let pointSize = 6;
|
||||||
this.debugRenderingContext.fillStyle = color.toString();
|
this.debugRenderingContext.fillStyle = color.toString();
|
||||||
this.debugRenderingContext.fillRect(pos.x - pointSize/2, pos.y - pointSize/2, pointSize, pointSize);
|
this.debugRenderingContext.fillRect(pos.x - pointSize/2, pos.y - pointSize/2, pointSize, pointSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default rendering color for text for the debugger
|
||||||
|
* @param color The color to render the text
|
||||||
|
*/
|
||||||
static setDefaultTextColor(color: Color): void {
|
static setDefaultTextColor(color: Color): void {
|
||||||
this.defaultTextColor = color;
|
this.defaultTextColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs any necessary setup operations on the Debug canvas
|
||||||
|
* @param canvas The debug canvas
|
||||||
|
* @param width The desired width of the canvas
|
||||||
|
* @param height The desired height of the canvas
|
||||||
|
* @returns The rendering context extracted from the canvas
|
||||||
|
*/
|
||||||
static initializeDebugCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
static initializeDebugCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
|
@ -104,15 +127,18 @@ export default class Debug {
|
||||||
return this.debugRenderingContext;
|
return this.debugRenderingContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Clears the debug canvas */
|
||||||
static clearCanvas(): void {
|
static clearCanvas(): void {
|
||||||
this.debugRenderingContext.clearRect(0, 0, this.debugCanvasSize.x, this.debugCanvasSize.y);
|
this.debugRenderingContext.clearRect(0, 0, this.debugCanvasSize.x, this.debugCanvasSize.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Renders the text and nodes sent to the Debug system */
|
||||||
static render(): void {
|
static render(): void {
|
||||||
this.renderText();
|
this.renderText();
|
||||||
this.renderNodes();
|
this.renderNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Renders the text sent to the Debug canvas */
|
||||||
static renderText(): void {
|
static renderText(): void {
|
||||||
let y = 20;
|
let y = 20;
|
||||||
this.debugRenderingContext.font = "20px Arial";
|
this.debugRenderingContext.font = "20px Arial";
|
||||||
|
@ -125,6 +151,7 @@ export default class Debug {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Renders the nodes registered with the debug canvas */
|
||||||
static renderNodes(): void {
|
static renderNodes(): void {
|
||||||
if(this.nodes){
|
if(this.nodes){
|
||||||
this.nodes.forEach(node => {
|
this.nodes.forEach(node => {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Color from "../Utils/Color";
|
import Color from "../Utils/Color";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
export default class Stats extends Object {
|
export default class Stats extends Object {
|
||||||
/** The fps of the game. */
|
// The fps of the game.
|
||||||
private static prevfps: Array<number>;
|
private static prevfps: Array<number>;
|
||||||
private static readonly NUM_POINTS: number = 60;
|
private static readonly NUM_POINTS: number = 60;
|
||||||
private static ctx: CanvasRenderingContext2D;
|
private static ctx: CanvasRenderingContext2D;
|
||||||
|
|
|
@ -2,17 +2,23 @@ import Map from "../DataTypes/Map";
|
||||||
import EventQueue from "./EventQueue";
|
import EventQueue from "./EventQueue";
|
||||||
import GameEvent from "./GameEvent";
|
import GameEvent from "./GameEvent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event emitter object other systems can use to hook into the EventQueue.
|
||||||
|
* Provides an easy interface for firing off events.
|
||||||
|
*/
|
||||||
export default class Emitter {
|
export default class Emitter {
|
||||||
|
/** A reference to the EventQueue */
|
||||||
private eventQueue: EventQueue;
|
private eventQueue: EventQueue;
|
||||||
|
|
||||||
|
/** Creates a new Emitter */
|
||||||
constructor(){
|
constructor(){
|
||||||
this.eventQueue = EventQueue.getInstance();
|
this.eventQueue = EventQueue.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit and event of type eventType with the data packet data
|
* Emit and event of type eventType with the data packet data
|
||||||
* @param eventType
|
* @param eventType The name of the event to fire off
|
||||||
* @param data
|
* @param data A @reference[Map] or record containing any data about the event
|
||||||
*/
|
*/
|
||||||
fireEvent(eventType: string, data: Map<any> | Record<string, any> = null): void {
|
fireEvent(eventType: string, data: Map<any> | Record<string, any> = null): void {
|
||||||
this.eventQueue.addEvent(new GameEvent(eventType, data));
|
this.eventQueue.addEvent(new GameEvent(eventType, data));
|
||||||
|
|
|
@ -4,11 +4,35 @@ import GameEvent from "./GameEvent";
|
||||||
import Receiver from "./Receiver";
|
import Receiver from "./Receiver";
|
||||||
import { GameEventType } from "./GameEventType";
|
import { GameEventType } from "./GameEventType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main event system of the game engine.
|
||||||
|
* Events are sent to the EventQueue, which handles distribution to any systems that are listening for those events.
|
||||||
|
* This allows for handling of input without having classes directly hook into javascript event handles,
|
||||||
|
* and allows otherwise separate classes to communicate with each other cleanly, such as a Player object
|
||||||
|
* requesting a sound be played by the audio system.
|
||||||
|
*
|
||||||
|
* The distribution of @reference[GameEvent]s happens as follows:
|
||||||
|
*
|
||||||
|
* Events are recieved throughout a frame and are queued up by the EventQueue.
|
||||||
|
* At the beginning of the next frame, events are sent out to any receivers that are hooked into the event type.
|
||||||
|
* @reference[Receiver]s are then free to process events as they see fit.
|
||||||
|
*
|
||||||
|
* Overall, the EventQueue can be considered as something similar to an email server,
|
||||||
|
* and the @reference[Receiver]s can be considered as the client inboxes.
|
||||||
|
*
|
||||||
|
* See @link(Game Programming Patterns)(https://gameprogrammingpatterns.com/event-queue.html) for more discussion on EventQueues
|
||||||
|
*/
|
||||||
export default class EventQueue {
|
export default class EventQueue {
|
||||||
private static instance: EventQueue = null;
|
private static instance: EventQueue = null;
|
||||||
private readonly MAX_SIZE: number;
|
|
||||||
private q: Queue<GameEvent>;
|
/** The maximum number of events visible */
|
||||||
private receivers: Map<Array<Receiver>>
|
private readonly MAX_SIZE: number;
|
||||||
|
|
||||||
|
/** The actual queue of events */
|
||||||
|
private q: Queue<GameEvent>;
|
||||||
|
|
||||||
|
/** The map of receivers registered for an event name */
|
||||||
|
private receivers: Map<Array<Receiver>>;
|
||||||
|
|
||||||
private constructor(){
|
private constructor(){
|
||||||
this.MAX_SIZE = 100;
|
this.MAX_SIZE = 100;
|
||||||
|
@ -16,6 +40,7 @@ export default class EventQueue {
|
||||||
this.receivers = new Map<Array<Receiver>>();
|
this.receivers = new Map<Array<Receiver>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Retrieves the instance of the Singleton EventQueue */
|
||||||
static getInstance(): EventQueue {
|
static getInstance(): EventQueue {
|
||||||
if(this.instance === null){
|
if(this.instance === null){
|
||||||
this.instance = new EventQueue();
|
this.instance = new EventQueue();
|
||||||
|
@ -24,14 +49,18 @@ export default class EventQueue {
|
||||||
return this.instance;
|
return this.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Adds an event to the EventQueue.
|
||||||
|
* This is exposed to the rest of the game engine through the @reference[Emitter] class */
|
||||||
addEvent(event: GameEvent): void {
|
addEvent(event: GameEvent): void {
|
||||||
this.q.enqueue(event);
|
this.q.enqueue(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associates a receiver with a type of event. Every time this event appears in the future, it will be given to the receiver (and any others watching that type)
|
* Associates a receiver with a type of event. Every time this event appears in the future,
|
||||||
* @param receiver
|
* it will be given to the receiver (and any others watching that type).
|
||||||
* @param type
|
* This is exposed to the rest of the game engine through the @reference[Receiver] class
|
||||||
|
* @param receiver The event receiver
|
||||||
|
* @param type The type or types of events to subscribe to
|
||||||
*/
|
*/
|
||||||
subscribe(receiver: Receiver, type: string | Array<string>): void {
|
subscribe(receiver: Receiver, type: string | Array<string>): void {
|
||||||
if(type instanceof Array){
|
if(type instanceof Array){
|
||||||
|
@ -53,7 +82,7 @@ export default class EventQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaT: number): void{
|
update(deltaT: number): void {
|
||||||
while(this.q.hasItems()){
|
while(this.q.hasItems()){
|
||||||
// Retrieve each event
|
// Retrieve each event
|
||||||
let event = this.q.dequeue();
|
let event = this.q.dequeue();
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
import Map from "../DataTypes/Map"
|
import Map from "../DataTypes/Map"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of an in-game event
|
* A representation of an in-game event that is passed through the @reference[EventQueue]
|
||||||
*/
|
*/
|
||||||
export default class GameEvent {
|
export default class GameEvent {
|
||||||
public type: string;
|
/** The type of the event */
|
||||||
public data: Map<any>;
|
public type: string;
|
||||||
|
/** The data contained by the event */
|
||||||
|
public data: Map<any>;
|
||||||
|
/** The time of the event in ms */
|
||||||
public time: number;
|
public time: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new GameEvent.
|
||||||
|
* This is handled implicitly through the @reference[Emitter] class
|
||||||
|
* @param type The type of the GameEvent
|
||||||
|
* @param data The data contained by the GameEvent
|
||||||
|
*/
|
||||||
constructor(type: string, data: Map<any> | Record<string, any> = null) {
|
constructor(type: string, data: Map<any> | Record<string, any> = null) {
|
||||||
// Parse the game event data
|
// Parse the game event data
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
|
@ -26,10 +35,19 @@ export default class GameEvent {
|
||||||
this.time = Date.now();
|
this.time = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the type of the GameEvent
|
||||||
|
* @param type The type to check
|
||||||
|
* @returns True if the GameEvent is the specified type, false otherwise.
|
||||||
|
*/
|
||||||
isType(type: string): boolean {
|
isType(type: string): boolean {
|
||||||
return this.type === type;
|
return this.type === type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this GameEvent as a string
|
||||||
|
* @returns The string representation of the GameEvent
|
||||||
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return this.type + ": @" + this.time;
|
return this.type + ": @" + this.time;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export enum GameEventType {
|
export enum GameEventType {
|
||||||
/**
|
/**
|
||||||
* Mouse Down event. Has data: {position: Vec2 - Mouse Position}
|
* Mouse Down event. Has data: {position: Vec2 - Mouse Position}
|
||||||
|
|
|
@ -3,12 +3,16 @@ import EventQueue from "./EventQueue";
|
||||||
import GameEvent from "./GameEvent";
|
import GameEvent from "./GameEvent";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives subscribed events from the EventQueue
|
* Receives subscribed events from the EventQueue.
|
||||||
*/
|
*/
|
||||||
export default class Receiver{
|
export default class Receiver {
|
||||||
|
/** The maximum number of events this Receiver can hold at one time */
|
||||||
readonly MAX_SIZE: number;
|
readonly MAX_SIZE: number;
|
||||||
|
|
||||||
|
/** The inbox of the Receiver */
|
||||||
private q: Queue<GameEvent>;
|
private q: Queue<GameEvent>;
|
||||||
|
|
||||||
|
/** Creates a new Receiver */
|
||||||
constructor(){
|
constructor(){
|
||||||
this.MAX_SIZE = 100;
|
this.MAX_SIZE = 100;
|
||||||
this.q = new Queue(this.MAX_SIZE);
|
this.q = new Queue(this.MAX_SIZE);
|
||||||
|
@ -23,7 +27,8 @@ export default class Receiver{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an event to the queue of this reciever
|
* Adds an event to the queue of this reciever. This is used by the @reference[EventQueue] to distribute events
|
||||||
|
* @param event The event to receive
|
||||||
*/
|
*/
|
||||||
receive(event: GameEvent): void {
|
receive(event: GameEvent): void {
|
||||||
this.q.enqueue(event);
|
this.q.enqueue(event);
|
||||||
|
@ -31,13 +36,15 @@ export default class Receiver{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the next event from the receiver's queue
|
* Retrieves the next event from the receiver's queue
|
||||||
|
* @returns The next GameEvent
|
||||||
*/
|
*/
|
||||||
getNextEvent(): GameEvent {
|
getNextEvent(): GameEvent {
|
||||||
return this.q.dequeue();
|
return this.q.dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks at the next event in the receiver's queue
|
* Looks at the next event in the receiver's queue, but doesn't remove it from the queue
|
||||||
|
* @returns The next GameEvent
|
||||||
*/
|
*/
|
||||||
peekNextEvent(): GameEvent {
|
peekNextEvent(): GameEvent {
|
||||||
return this.q.peekNext()
|
return this.q.peekNext()
|
||||||
|
@ -45,6 +52,7 @@ export default class Receiver{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the receiver has any events in its queue
|
* Returns true if the receiver has any events in its queue
|
||||||
|
* @returns True if the receiver has another event, false otherwise
|
||||||
*/
|
*/
|
||||||
hasNextEvent(): boolean {
|
hasNextEvent(): boolean {
|
||||||
return this.q.hasItems();
|
return this.q.hasItems();
|
||||||
|
|
|
@ -4,11 +4,15 @@ import GameEvent from "../Events/GameEvent";
|
||||||
import { GameEventType } from "../Events/GameEventType";
|
import { GameEventType } from "../Events/GameEventType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles communication with the web browser to receive asynchronous events and send them to the event queue
|
* Handles communication with the web browser to receive asynchronous events and send them to the @reference[EventQueue]
|
||||||
*/
|
*/
|
||||||
export default class InputHandler{
|
export default class InputHandler {
|
||||||
private eventQueue: EventQueue;
|
private eventQueue: EventQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new InputHandler
|
||||||
|
* @param canvas The game canvas
|
||||||
|
*/
|
||||||
constructor(canvas: HTMLCanvasElement){
|
constructor(canvas: HTMLCanvasElement){
|
||||||
this.eventQueue = EventQueue.getInstance();
|
this.eventQueue = EventQueue.getInstance();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import GameEvent from "../Events/GameEvent";
|
||||||
import { GameEventType } from "../Events/GameEventType";
|
import { GameEventType } from "../Events/GameEventType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives input events from the event queue and allows for easy access of information about input
|
* Receives input events from the @reference[EventQueue] and allows for easy access of information about input by other systems
|
||||||
*/
|
*/
|
||||||
export default class InputReceiver{
|
export default class InputReceiver{
|
||||||
private static instance: InputReceiver = null;
|
private static instance: InputReceiver = null;
|
||||||
|
@ -45,6 +45,10 @@ export default class InputReceiver{
|
||||||
GameEventType.KEY_DOWN, GameEventType.KEY_UP, GameEventType.CANVAS_BLUR, GameEventType.WHEEL_UP, GameEventType.WHEEL_DOWN]);
|
GameEventType.KEY_DOWN, GameEventType.KEY_UP, GameEventType.CANVAS_BLUR, GameEventType.WHEEL_UP, GameEventType.WHEEL_DOWN]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the statc instance of the Singleton InputReceiver
|
||||||
|
* @returns The InputReceiver instance
|
||||||
|
*/
|
||||||
static getInstance(): InputReceiver{
|
static getInstance(): InputReceiver{
|
||||||
if(this.instance === null){
|
if(this.instance === null){
|
||||||
this.instance = new InputReceiver();
|
this.instance = new InputReceiver();
|
||||||
|
@ -117,6 +121,12 @@ export default class InputReceiver{
|
||||||
this.keyPressed.forEach((key: string) => this.keyPressed.set(key, false));
|
this.keyPressed.forEach((key: string) => this.keyPressed.set(key, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not a key was newly pressed this frame.
|
||||||
|
* If the key is still pressed from last frame and wasn't re-pressed, this will return false.
|
||||||
|
* @param key The key
|
||||||
|
* @returns True if the key was just pressed, false otherwise
|
||||||
|
*/
|
||||||
isJustPressed(key: string): boolean {
|
isJustPressed(key: string): boolean {
|
||||||
if(this.keyJustPressed.has(key)){
|
if(this.keyJustPressed.has(key)){
|
||||||
return this.keyJustPressed.get(key)
|
return this.keyJustPressed.get(key)
|
||||||
|
@ -125,6 +135,11 @@ export default class InputReceiver{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all of the keys that are newly pressed this frame.
|
||||||
|
* If a key is still pressed from last frame and wasn't re-pressed, it will not be in this list.
|
||||||
|
* @returns An array of all of the newly pressed keys.
|
||||||
|
*/
|
||||||
getKeysJustPressed(): Array<string> {
|
getKeysJustPressed(): Array<string> {
|
||||||
let keys = Array<string>();
|
let keys = Array<string>();
|
||||||
this.keyJustPressed.forEach(key => {
|
this.keyJustPressed.forEach(key => {
|
||||||
|
@ -135,6 +150,11 @@ export default class InputReceiver{
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not a key is being pressed.
|
||||||
|
* @param key The key
|
||||||
|
* @returns True if the key is currently pressed, false otherwise
|
||||||
|
*/
|
||||||
isPressed(key: string): boolean {
|
isPressed(key: string): boolean {
|
||||||
if(this.keyPressed.has(key)){
|
if(this.keyPressed.has(key)){
|
||||||
return this.keyPressed.get(key)
|
return this.keyPressed.get(key)
|
||||||
|
@ -143,38 +163,76 @@ export default class InputReceiver{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the mouse was newly pressed this frame
|
||||||
|
* @returns True if the mouse was just pressed, false otherwise
|
||||||
|
*/
|
||||||
isMouseJustPressed(): boolean {
|
isMouseJustPressed(): boolean {
|
||||||
return this.mouseJustPressed;
|
return this.mouseJustPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the mouse is currently pressed
|
||||||
|
* @returns True if the mouse is currently pressed, false otherwise
|
||||||
|
*/
|
||||||
isMousePressed(): boolean {
|
isMousePressed(): boolean {
|
||||||
return this.mousePressed;
|
return this.mousePressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the user scrolled or not
|
||||||
|
* @returns True if the user just scrolled this frame, false otherwise
|
||||||
|
*/
|
||||||
didJustScroll(): boolean {
|
didJustScroll(): boolean {
|
||||||
return this.justScrolled;
|
return this.justScrolled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the direction of the scroll
|
||||||
|
* @returns -1 if the user scrolled up, 1 if they scrolled down
|
||||||
|
*/
|
||||||
getScrollDirection(): number {
|
getScrollDirection(): number {
|
||||||
return this.scrollDirection;
|
return this.scrollDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of the player's mouse
|
||||||
|
* @returns The mouse position stored as a Vec2
|
||||||
|
*/
|
||||||
getMousePosition(): Vec2 {
|
getMousePosition(): Vec2 {
|
||||||
return this.mousePosition;
|
return this.mousePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of the player's mouse in the game world,
|
||||||
|
* taking into consideration the scrolling of the viewport
|
||||||
|
* @returns The mouse position stored as a Vec2
|
||||||
|
*/
|
||||||
getGlobalMousePosition(): Vec2 {
|
getGlobalMousePosition(): Vec2 {
|
||||||
return this.mousePosition.clone().add(this.viewport.getOrigin());
|
return this.mousePosition.clone().add(this.viewport.getOrigin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of the last mouse press
|
||||||
|
* @returns The mouse position stored as a Vec2
|
||||||
|
*/
|
||||||
getMousePressPosition(): Vec2 {
|
getMousePressPosition(): Vec2 {
|
||||||
return this.mousePressPosition;
|
return this.mousePressPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of the last mouse press in the game world,
|
||||||
|
* taking into consideration the scrolling of the viewport
|
||||||
|
* @returns The mouse position stored as a Vec2
|
||||||
|
*/
|
||||||
getGlobalMousePressPosition(): Vec2 {
|
getGlobalMousePressPosition(): Vec2 {
|
||||||
return this.mousePressPosition.clone().add(this.viewport.getOrigin());
|
return this.mousePressPosition.clone().add(this.viewport.getOrigin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the input receiver a reference to the viewport
|
||||||
|
* @param viewport The viewport
|
||||||
|
*/
|
||||||
setViewport(viewport: Viewport): void {
|
setViewport(viewport: Viewport): void {
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,16 @@ import Viewport from "../SceneGraph/Viewport";
|
||||||
import SceneManager from "../Scene/SceneManager";
|
import SceneManager from "../Scene/SceneManager";
|
||||||
import AudioManager from "../Sound/AudioManager";
|
import AudioManager from "../Sound/AudioManager";
|
||||||
import Stats from "../Debug/Stats";
|
import Stats from "../Debug/Stats";
|
||||||
import ArrayUtils from "../Utils/ArrayUtils";
|
|
||||||
import RenderingManager from "../Rendering/RenderingManager";
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
import CanvasRenderer from "../Rendering/CanvasRenderer";
|
import CanvasRenderer from "../Rendering/CanvasRenderer";
|
||||||
import Color from "../Utils/Color";
|
import Color from "../Utils/Color";
|
||||||
|
import GameOptions from "./GameOptions";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main loop of the game engine.
|
||||||
|
* Handles the update order, and initializes all subsystems.
|
||||||
|
* The GameLoop manages the update cycle, and requests animation frames to render to the browser.
|
||||||
|
*/
|
||||||
export default class GameLoop {
|
export default class GameLoop {
|
||||||
gameOptions: GameOptions;
|
gameOptions: GameOptions;
|
||||||
|
|
||||||
|
@ -77,6 +82,10 @@ export default class GameLoop {
|
||||||
private audioManager: AudioManager;
|
private audioManager: AudioManager;
|
||||||
private renderingManager: RenderingManager;
|
private renderingManager: RenderingManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new GameLoop
|
||||||
|
* @param options The options for GameLoop initialization
|
||||||
|
*/
|
||||||
constructor(options?: Record<string, any>){
|
constructor(options?: Record<string, any>){
|
||||||
// Typecast the config object to a GameConfig object
|
// Typecast the config object to a GameConfig object
|
||||||
this.gameOptions = GameOptions.parse(options);
|
this.gameOptions = GameOptions.parse(options);
|
||||||
|
@ -148,24 +157,32 @@ export default class GameLoop {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the maximum allowed physics framerate of the game
|
* Changes the maximum allowed physics framerate of the game
|
||||||
* @param initMax
|
* @param initMax The max framerate
|
||||||
*/
|
*/
|
||||||
setMaxUpdateFPS(initMax: number): void {
|
setMaxUpdateFPS(initMax: number): void {
|
||||||
this.maxUpdateFPS = initMax;
|
this.maxUpdateFPS = initMax;
|
||||||
this.simulationTimestep = Math.floor(1000/this.maxUpdateFPS);
|
this.simulationTimestep = Math.floor(1000/this.maxUpdateFPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum rendering framerate
|
||||||
|
* @param maxFPS The max framerate
|
||||||
|
*/
|
||||||
setMaxFPS(maxFPS: number): void {
|
setMaxFPS(maxFPS: number): void {
|
||||||
this.minFrameDelay = 1000/maxFPS;
|
this.minFrameDelay = 1000/maxFPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the SceneManager from the GameLoop
|
||||||
|
* @returns The SceneManager
|
||||||
|
*/
|
||||||
getSceneManager(): SceneManager {
|
getSceneManager(): SceneManager {
|
||||||
return this.sceneManager;
|
return this.sceneManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the frame count and sum of time for the framerate of the game
|
* Updates the frame count and sum of time for the framerate of the game
|
||||||
* @param timestep
|
* @param timestep The current time in ms
|
||||||
*/
|
*/
|
||||||
private updateFPS(timestamp: number): void {
|
private updateFPS(timestamp: number): void {
|
||||||
this.fps = 0.9 * this.framesSinceLastFpsUpdate * 1000 / (timestamp - this.lastFpsUpdate) +(1 - 0.9) * this.fps;
|
this.fps = 0.9 * this.framesSinceLastFpsUpdate * 1000 / (timestamp - this.lastFpsUpdate) +(1 - 0.9) * this.fps;
|
||||||
|
@ -189,9 +206,9 @@ export default class GameLoop {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first game frame - initializes the first frame time and begins the render
|
* The first game frame - initializes the first frame time and begins the render
|
||||||
* @param timestamp
|
* @param timestamp The current time in ms
|
||||||
*/
|
*/
|
||||||
startFrame = (timestamp: number): void => {
|
startFrame(timestamp: number): void {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
@ -207,7 +224,7 @@ export default class GameLoop {
|
||||||
* The main loop of the game. Updates and renders every frame
|
* The main loop of the game. Updates and renders every frame
|
||||||
* @param timestamp
|
* @param timestamp
|
||||||
*/
|
*/
|
||||||
doFrame = (timestamp: number): void => {
|
doFrame(timestamp: number): void {
|
||||||
// Request animation frame to prepare for another update or render
|
// Request animation frame to prepare for another update or render
|
||||||
window.requestAnimationFrame(this.doFrame);
|
window.requestAnimationFrame(this.doFrame);
|
||||||
|
|
||||||
|
@ -250,6 +267,9 @@ export default class GameLoop {
|
||||||
this.panic = false;
|
this.panic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the game loop
|
||||||
|
*/
|
||||||
end(){
|
end(){
|
||||||
if(this.panic) {
|
if(this.panic) {
|
||||||
var discardedTime = Math.round(this.resetFrameDelta());
|
var discardedTime = Math.round(this.resetFrameDelta());
|
||||||
|
@ -265,7 +285,7 @@ export default class GameLoop {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates all necessary subsystems of the game. Defers scene updates to the sceneManager
|
* Updates all necessary subsystems of the game. Defers scene updates to the sceneManager
|
||||||
* @param deltaT
|
* @param deltaT The time sine the last update
|
||||||
*/
|
*/
|
||||||
update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
// Handle all events that happened since the start of the last loop
|
// Handle all events that happened since the start of the last loop
|
||||||
|
@ -288,7 +308,7 @@ export default class GameLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the canvas and defers scene rendering to the sceneManager. Renders the debug
|
* Clears the canvas and defers scene rendering to the sceneManager. Renders the debug canvas
|
||||||
*/
|
*/
|
||||||
render(): void {
|
render(): void {
|
||||||
// Clear the canvases
|
// Clear the canvases
|
||||||
|
@ -305,17 +325,3 @@ export default class GameLoop {
|
||||||
Stats.render();
|
Stats.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameOptions {
|
|
||||||
viewportSize: {x: number, y: number};
|
|
||||||
clearColor: {r: number, g: number, b: number}
|
|
||||||
|
|
||||||
static parse(options: Record<string, any>): GameOptions {
|
|
||||||
let gOpt = new GameOptions();
|
|
||||||
|
|
||||||
gOpt.viewportSize = options.viewportSize ? options.viewportSize : {x: 800, y: 600};
|
|
||||||
gOpt.clearColor = options.clearColor ? options.clearColor : {r: 255, g: 255, b: 255};
|
|
||||||
|
|
||||||
return gOpt;
|
|
||||||
}
|
|
||||||
}
|
|
24
src/Loop/GameOptions.ts
Normal file
24
src/Loop/GameOptions.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
|
/** The options for initializing the @reference[GameLoop] */
|
||||||
|
export default class GameOptions {
|
||||||
|
/** The size of the viewport */
|
||||||
|
viewportSize: {x: number, y: number};
|
||||||
|
|
||||||
|
/** The color to clear the canvas to each frame */
|
||||||
|
clearColor: {r: number, g: number, b: number}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the data in the raw options object
|
||||||
|
* @param options The game options as a Record
|
||||||
|
* @returns A version of the options converted to a GameOptions object
|
||||||
|
*/
|
||||||
|
static parse(options: Record<string, any>): GameOptions {
|
||||||
|
let gOpt = new GameOptions();
|
||||||
|
|
||||||
|
gOpt.viewportSize = options.viewportSize ? options.viewportSize : {x: 800, y: 600};
|
||||||
|
gOpt.clearColor = options.clearColor ? options.clearColor : {r: 255, g: 255, b: 255};
|
||||||
|
|
||||||
|
return gOpt;
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,7 +73,7 @@ export default abstract class Tilemap extends CanvasNode {
|
||||||
/**
|
/**
|
||||||
* Adds this tilemap to the physics system
|
* Adds this tilemap to the physics system
|
||||||
*/
|
*/
|
||||||
addPhysics = (): void => {
|
addPhysics(): void {
|
||||||
this.scene.getPhysicsManager().registerTilemap(this);
|
this.scene.getPhysicsManager().registerTilemap(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,9 +99,9 @@ export default abstract class Tilemap extends CanvasNode {
|
||||||
abstract getTile(index: number): number;
|
abstract getTile(index: number): number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value of the tile at the specified index
|
* Sets the tile at the specified index
|
||||||
* @param index
|
* @param index The index of the tile
|
||||||
* @param type
|
* @param type The new data value of the tile
|
||||||
*/
|
*/
|
||||||
abstract setTile(index: number, type: number): void;
|
abstract setTile(index: number, type: number): void;
|
||||||
|
|
||||||
|
|
|
@ -101,11 +101,7 @@ export default class OrthogonalTilemap extends Tilemap {
|
||||||
return this.data[index];
|
return this.data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// @override
|
||||||
* Sets the tile at the specified index
|
|
||||||
* @param index The index of the tile
|
|
||||||
* @param type The new data value of the tile
|
|
||||||
*/
|
|
||||||
setTile(index: number, type: number): void {
|
setTile(index: number, type: number): void {
|
||||||
this.data[index] = type;
|
this.data[index] = type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ export default class Label extends UIElement{
|
||||||
* Gets a string containg the font details for rendering
|
* Gets a string containg the font details for rendering
|
||||||
* @returns A string containing the font details
|
* @returns A string containing the font details
|
||||||
*/
|
*/
|
||||||
protected getFontString(): string {
|
getFontString(): string {
|
||||||
return this.fontSize + "px " + this.font;
|
return this.fontSize + "px " + this.font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default class Slider extends UIElement {
|
||||||
/** The color of the slider track */
|
/** The color of the slider track */
|
||||||
public sliderColor: Color;
|
public sliderColor: Color;
|
||||||
/** The reaction of this UIElement to a value change */
|
/** The reaction of this UIElement to a value change */
|
||||||
public onValueChange: (value: number) => void;
|
public onValueChange: Function;
|
||||||
/** The event propagated by this UIElement when value changes */
|
/** The event propagated by this UIElement when value changes */
|
||||||
public onValueChangeEventId: string;
|
public onValueChangeEventId: string;
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,35 @@ import Map from "../DataTypes/Map";
|
||||||
import Vec2 from "../DataTypes/Vec2";
|
import Vec2 from "../DataTypes/Vec2";
|
||||||
import NavigationPath from "./NavigationPath";
|
import NavigationPath from "./NavigationPath";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The manager class for navigation.
|
||||||
|
* Handles all navigable entities, such and allows them to be accessed by outside systems by requesting a path
|
||||||
|
* from one position to another.
|
||||||
|
*/
|
||||||
export default class NavigationManager {
|
export default class NavigationManager {
|
||||||
|
/** The list of all navigable entities */
|
||||||
protected navigableEntities: Map<Navigable>;
|
protected navigableEntities: Map<Navigable>;
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
this.navigableEntities = new Map();
|
this.navigableEntities = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
addNavigableEntity(navName: string, nav: Navigable){
|
/**
|
||||||
|
* Adds a navigable entity to the NavigationManager
|
||||||
|
* @param navName The name of the navigable entitry
|
||||||
|
* @param nav The actual Navigable instance
|
||||||
|
*/
|
||||||
|
addNavigableEntity(navName: string, nav: Navigable): void {
|
||||||
this.navigableEntities.add(navName, nav);
|
this.navigableEntities.add(navName, nav);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a path frome one point to another using a specified Navigable object
|
||||||
|
* @param navName The name of the registered Navigable object
|
||||||
|
* @param fromPosition The starting position of navigation
|
||||||
|
* @param toPosition The ending position of Navigation
|
||||||
|
* @returns A NavigationPath containing the route to take over the Navigable entity to get between the provided positions.
|
||||||
|
*/
|
||||||
getPath(navName: string, fromPosition: Vec2, toPosition: Vec2): NavigationPath {
|
getPath(navName: string, fromPosition: Vec2, toPosition: Vec2): NavigationPath {
|
||||||
let nav = this.navigableEntities.get(navName);
|
let nav = this.navigableEntities.get(navName);
|
||||||
return nav.getNavigationPath(fromPosition.clone(), toPosition.clone());
|
return nav.getNavigationPath(fromPosition.clone(), toPosition.clone());
|
||||||
|
|
|
@ -6,10 +6,17 @@ import GameNode from "../Nodes/GameNode";
|
||||||
* A path that AIs can follow. Uses finishMove() in Physical to determine progress on the route
|
* A path that AIs can follow. Uses finishMove() in Physical to determine progress on the route
|
||||||
*/
|
*/
|
||||||
export default class NavigationPath {
|
export default class NavigationPath {
|
||||||
protected path: Stack<Vec2>
|
/** The navigation path, stored as a stack of next positions */
|
||||||
|
protected path: Stack<Vec2>;
|
||||||
|
/** The current direction of movement */
|
||||||
protected currentMoveDirection: Vec2;
|
protected currentMoveDirection: Vec2;
|
||||||
|
/** The distance a node must be to a point to consider it as having arrived */
|
||||||
protected distanceThreshold: number;
|
protected distanceThreshold: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new NavigationPath
|
||||||
|
* @param path The path of nodes to take
|
||||||
|
*/
|
||||||
constructor(path: Stack<Vec2>){
|
constructor(path: Stack<Vec2>){
|
||||||
this.path = path;
|
this.path = path;
|
||||||
console.log(path.toString())
|
console.log(path.toString())
|
||||||
|
@ -17,15 +24,28 @@ export default class NavigationPath {
|
||||||
this.distanceThreshold = 20;
|
this.distanceThreshold = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the status of navigation along this NavigationPath
|
||||||
|
* @returns True if the node has reached the end of the path, false otherwise
|
||||||
|
*/
|
||||||
isDone(): boolean {
|
isDone(): boolean {
|
||||||
return this.path.isEmpty();
|
return this.path.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the movement direction in the current position along the path
|
||||||
|
* @param node The node to move along the path
|
||||||
|
* @returns The movement direction as a Vec2
|
||||||
|
*/
|
||||||
getMoveDirection(node: GameNode): Vec2 {
|
getMoveDirection(node: GameNode): Vec2 {
|
||||||
// Return direction to next point in the nav
|
// Return direction to next point in the nav
|
||||||
return node.position.dirTo(this.path.peek());
|
return node.position.dirTo(this.path.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates this NavigationPath to the current state of the GameNode
|
||||||
|
* @param node The node moving along the path
|
||||||
|
*/
|
||||||
handlePathProgress(node: GameNode): void {
|
handlePathProgress(node: GameNode): void {
|
||||||
if(node.position.distanceSqTo(this.path.peek()) < this.distanceThreshold*this.distanceThreshold){
|
if(node.position.distanceSqTo(this.path.peek()) < this.distanceThreshold*this.distanceThreshold){
|
||||||
// We've reached our node, move on to the next destination
|
// We've reached our node, move on to the next destination
|
||||||
|
|
|
@ -5,13 +5,22 @@ import Vec2 from "../DataTypes/Vec2";
|
||||||
import GraphUtils from "../Utils/GraphUtils";
|
import GraphUtils from "../Utils/GraphUtils";
|
||||||
import NavigationPath from "./NavigationPath";
|
import NavigationPath from "./NavigationPath";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a Navmesh. Navmeshes are graphs in the game world along which nodes can move.
|
||||||
|
*/
|
||||||
export default class Navmesh implements Navigable {
|
export default class Navmesh implements Navigable {
|
||||||
|
/** The graph of points in the NavMesh */
|
||||||
protected graph: PositionGraph;
|
protected graph: PositionGraph;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Navmesh from the points in the speecified graph
|
||||||
|
* @param graph The graph to construct a navmesh from
|
||||||
|
*/
|
||||||
constructor(graph: PositionGraph){
|
constructor(graph: PositionGraph){
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
getNavigationPath(fromPosition: Vec2, toPosition: Vec2): NavigationPath {
|
getNavigationPath(fromPosition: Vec2, toPosition: Vec2): NavigationPath {
|
||||||
let start = this.getClosestNode(fromPosition);
|
let start = this.getClosestNode(fromPosition);
|
||||||
let end = this.getClosestNode(toPosition);
|
let end = this.getClosestNode(toPosition);
|
||||||
|
@ -34,7 +43,12 @@ export default class Navmesh implements Navigable {
|
||||||
return new NavigationPath(pathStack);
|
return new NavigationPath(pathStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosestNode(position: Vec2): number {
|
/**
|
||||||
|
* Gets the closest node in this Navmesh to the specified position
|
||||||
|
* @param position The position to query
|
||||||
|
* @returns The index of the closest node in the Navmesh to the position
|
||||||
|
*/
|
||||||
|
protected getClosestNode(position: Vec2): number {
|
||||||
let n = this.graph.numVertices;
|
let n = this.graph.numVertices;
|
||||||
let i = 1;
|
let i = 1;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
|
@ -11,6 +11,8 @@ import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import Debug from "../Debug/Debug";
|
import Debug from "../Debug/Debug";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export default class BasicPhysicsManager extends PhysicsManager {
|
export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
|
|
||||||
/** The array of static nodes */
|
/** The array of static nodes */
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Physical } from "../../DataTypes/Interfaces/Descriptors";
|
import { Physical } from "../../DataTypes/Interfaces/Descriptors";
|
||||||
import GameNode from "../../Nodes/GameNode";
|
import GameNode from "../../Nodes/GameNode";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export default abstract class BroadPhase {
|
export default abstract class BroadPhase {
|
||||||
/**
|
/**
|
||||||
* Runs the algorithm and returns an array of possible collision pairs.
|
* Runs the algorithm and returns an array of possible collision pairs.
|
||||||
|
|
|
@ -3,6 +3,8 @@ import PhysicsUtils from "../../Utils/PhysicsUtils";
|
||||||
import SortingUtils from "../../Utils/SortingUtils";
|
import SortingUtils from "../../Utils/SortingUtils";
|
||||||
import BroadPhase from "./BroadPhase";
|
import BroadPhase from "./BroadPhase";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export default class SweepAndPrune extends BroadPhase {
|
export default class SweepAndPrune extends BroadPhase {
|
||||||
protected xList: Array<Physical>;
|
protected xList: Array<Physical>;
|
||||||
protected yList: Array<Physical>;
|
protected yList: Array<Physical>;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Physical } from "../DataTypes/Interfaces/Descriptors";
|
import { Physical } from "../DataTypes/Interfaces/Descriptors";
|
||||||
import Vec2 from "../DataTypes/Vec2";
|
import Vec2 from "../DataTypes/Vec2";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export class Collision {
|
export class Collision {
|
||||||
firstContact: Vec2;
|
firstContact: Vec2;
|
||||||
lastContact: Vec2;
|
lastContact: Vec2;
|
||||||
|
|
|
@ -6,14 +6,20 @@ import Receiver from "../Events/Receiver";
|
||||||
import Emitter from "../Events/Emitter";
|
import Emitter from "../Events/Emitter";
|
||||||
import Map from "../DataTypes/Map";
|
import Map from "../DataTypes/Map";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract physics manager.
|
||||||
|
* This class exposes functions for subclasses to implement that should allow for a working physics system to be created.
|
||||||
|
*/
|
||||||
export default abstract class PhysicsManager implements Updateable {
|
export default abstract class PhysicsManager implements Updateable {
|
||||||
|
/** The event receiver for the physics system */
|
||||||
protected receiver: Receiver;
|
protected receiver: Receiver;
|
||||||
|
/** The event emitter for the physics system */
|
||||||
protected emitter: Emitter;
|
protected emitter: Emitter;
|
||||||
|
|
||||||
/** Layer names to numbers */
|
/** Maps layer names to numbers */
|
||||||
protected layerMap: Map<number>;
|
protected layerMap: Map<number>;
|
||||||
|
|
||||||
/** Layer numbers to names */
|
/** Maps layer numbers to names */
|
||||||
protected layerNames: Array<string>;
|
protected layerNames: Array<string>;
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
|
@ -27,22 +33,23 @@ export default abstract class PhysicsManager implements Updateable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a gamenode with this physics manager
|
* Registers a gamenode with this physics manager
|
||||||
* @param object
|
* @param object The object to register
|
||||||
*/
|
*/
|
||||||
abstract registerObject(object: GameNode): void;
|
abstract registerObject(object: GameNode): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a tilemap with this physics manager
|
* Registers a tilemap with this physics manager
|
||||||
* @param tilemap
|
* @param tilemap The tilemap to register
|
||||||
*/
|
*/
|
||||||
abstract registerTilemap(tilemap: Tilemap): void;
|
abstract registerTilemap(tilemap: Tilemap): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the physics
|
|
||||||
* @param deltaT
|
|
||||||
*/
|
|
||||||
abstract update(deltaT: number): void;
|
abstract update(deltaT: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the physics layer of the GameNode
|
||||||
|
* @param node The GameNode
|
||||||
|
* @param layer The layer that the GameNode should be on
|
||||||
|
*/
|
||||||
setLayer(node: GameNode, layer: string): void {
|
setLayer(node: GameNode, layer: string): void {
|
||||||
node.physicsLayer = this.layerMap.get(layer);
|
node.physicsLayer = this.layerMap.get(layer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,46 @@
|
||||||
import GameNode from "../Nodes/GameNode";
|
import GameNode from "../Nodes/GameNode";
|
||||||
import { Physical, Updateable } from "../DataTypes/Interfaces/Descriptors";
|
import { Physical } from "../DataTypes/Interfaces/Descriptors";
|
||||||
import Tilemap from "../Nodes/Tilemap";
|
import Tilemap from "../Nodes/Tilemap";
|
||||||
import PhysicsManager from "./PhysicsManager";
|
import PhysicsManager from "./PhysicsManager";
|
||||||
import Vec2 from "../DataTypes/Vec2";
|
import Vec2 from "../DataTypes/Vec2";
|
||||||
import Debug from "../Debug/Debug";
|
|
||||||
import Color from "../Utils/Color";
|
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
|
import AreaCollision from "../DataTypes/Physics/AreaCollision";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALGORITHM:
|
||||||
|
* In an effort to keep things simple and working effectively, each dynamic node will resolve its
|
||||||
|
* collisions considering the rest of the world as static.
|
||||||
|
*
|
||||||
|
* Collision detecting will happen first. This can be considered a broad phase, but it is not especially
|
||||||
|
* efficient, as it does not need to be for this game engine. Every dynamic node is checked against every
|
||||||
|
* other node for collision area. If collision area is non-zero (meaning the current node sweeps into another),
|
||||||
|
* it is added to a list of hits.
|
||||||
|
*
|
||||||
|
* INITIALIZATION:
|
||||||
|
* - Physics constants are reset
|
||||||
|
* - Swept shapes are recalculated. If a node isn't moving, it is skipped.
|
||||||
|
*
|
||||||
|
* COLLISION DETECTION:
|
||||||
|
* - For a node, collision area will be calculated using the swept AABB of the node against every other AABB in a static state
|
||||||
|
* - These collisions will be sorted by area in descending order
|
||||||
|
*
|
||||||
|
* COLLISION RESOLUTION:
|
||||||
|
* - For each hit, time of collision is calculated using a swept line through the AABB of the static node expanded
|
||||||
|
* with minkowski sums (discretely, but the concept is there)
|
||||||
|
* - The collision is resolved based on the near time of the collision (from method of separated axes)
|
||||||
|
* - X is resolved by near x, Y by near y.
|
||||||
|
* - There is some fudging to allow for sliding along walls of separate colliders. Sorting by area also helps with this.
|
||||||
|
* - Corner to corner collisions are resolve to favor x-movement. This is in consideration of platformers, to give
|
||||||
|
* the player some help with jumps
|
||||||
|
*
|
||||||
|
* Pros:
|
||||||
|
* - Everything happens with a consistent time. There is a distinct before and after for each resolution.
|
||||||
|
* - No back-tracking needs to be done. Once we resolve a node, it is definitively resolved.
|
||||||
|
*
|
||||||
|
* Cons:
|
||||||
|
* - Nodes that are processed early have movement priority over other nodes. This can lead to some undesirable interactions.
|
||||||
|
*/
|
||||||
export default class TestPhysicsManager extends PhysicsManager {
|
export default class TestPhysicsManager extends PhysicsManager {
|
||||||
|
|
||||||
/** The array of static nodes */
|
/** The array of static nodes */
|
||||||
|
@ -26,10 +59,7 @@ export default class TestPhysicsManager extends PhysicsManager {
|
||||||
this.tilemaps = new Array();
|
this.tilemaps = new Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// @override
|
||||||
* Add a new physics object to be updated with the physics system
|
|
||||||
* @param node The node to be added to the physics system
|
|
||||||
*/
|
|
||||||
registerObject(node: GameNode): void {
|
registerObject(node: GameNode): void {
|
||||||
if(node.isStatic){
|
if(node.isStatic){
|
||||||
// Static and not collidable
|
// Static and not collidable
|
||||||
|
@ -40,56 +70,18 @@ export default class TestPhysicsManager extends PhysicsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// @override
|
||||||
* Registers a tilemap with this physics manager
|
|
||||||
* @param tilemap
|
|
||||||
*/
|
|
||||||
registerTilemap(tilemap: Tilemap): void {
|
registerTilemap(tilemap: Tilemap): void {
|
||||||
this.tilemaps.push(tilemap);
|
this.tilemaps.push(tilemap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
setLayer(node: GameNode, layer: string): void {
|
setLayer(node: GameNode, layer: string): void {
|
||||||
node.physicsLayer = this.layerMap.get(layer);
|
node.physicsLayer = this.layerMap.get(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// @override
|
||||||
* Updates the physics
|
|
||||||
* @param deltaT
|
|
||||||
*/
|
|
||||||
update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
/* ALGORITHM:
|
|
||||||
In an effort to keep things simple and working effectively, each dynamic node will resolve its
|
|
||||||
collisions considering the rest of the world as static.
|
|
||||||
|
|
||||||
Collision detecting will happen first. This can be considered a broad phase, but it is not especially
|
|
||||||
efficient, as it does not need to be for this game engine. Every dynamic node is checked against every
|
|
||||||
other node for collision area. If collision area is non-zero (meaning the current node sweeps into another),
|
|
||||||
it is added to a list of hits.
|
|
||||||
|
|
||||||
INITIALIZATION:
|
|
||||||
- Physics constants are reset
|
|
||||||
- Swept shapes are recalculated. If a node isn't moving, it is skipped.
|
|
||||||
|
|
||||||
COLLISION DETECTION:
|
|
||||||
- For a node, collision area will be calculated using the swept AABB of the node against every other AABB in a static state
|
|
||||||
- These collisions will be sorted by area in descending order
|
|
||||||
|
|
||||||
COLLISION RESOLUTION:
|
|
||||||
- For each hit, time of collision is calculated using a swept line through the AABB of the static node expanded
|
|
||||||
with minkowski sums (discretely, but the concept is there)
|
|
||||||
- The collision is resolved based on the near time of the collision (from method of separated axes)
|
|
||||||
- X is resolved by near x, Y by near y.
|
|
||||||
- There is some fudging to allow for sliding along walls of separate colliders. Sorting by area also helps with this.
|
|
||||||
- Corner to corner collisions are resolve to favor x-movement. This is in consideration of platformers, to give
|
|
||||||
the player some help with jumps
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
- Everything happens with a consistent time. There is a distinct before and after for each resolution.
|
|
||||||
- No back-tracking needs to be done. Once we resolve a node, it is definitively resolved.
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
- Nodes that are processed early have movement priority over other nodes. This can lead to some undesirable interactions.
|
|
||||||
*/
|
|
||||||
for(let node of this.dynamicNodes){
|
for(let node of this.dynamicNodes){
|
||||||
/*---------- INITIALIZATION PHASE ----------*/
|
/*---------- INITIALIZATION PHASE ----------*/
|
||||||
// Clear frame dependent boolean values for each node
|
// Clear frame dependent boolean values for each node
|
||||||
|
@ -186,7 +178,13 @@ export default class TestPhysicsManager extends PhysicsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collideWithOrthogonalTilemap(node: Physical, tilemap: OrthogonalTilemap, overlaps: Array<AreaCollision>): void {
|
/**
|
||||||
|
* Handles a collision between this node and an orthogonal tilemap
|
||||||
|
* @param node The node
|
||||||
|
* @param tilemap The tilemap the node may be colliding with
|
||||||
|
* @param overlaps The list of overlaps
|
||||||
|
*/
|
||||||
|
protected collideWithOrthogonalTilemap(node: Physical, tilemap: OrthogonalTilemap, overlaps: Array<AreaCollision>): void {
|
||||||
// Get the min and max x and y coordinates of the moving node
|
// Get the min and max x and y coordinates of the moving node
|
||||||
let min = new Vec2(node.sweptRect.left, node.sweptRect.top);
|
let min = new Vec2(node.sweptRect.left, node.sweptRect.top);
|
||||||
let max = new Vec2(node.sweptRect.right, node.sweptRect.bottom);
|
let max = new Vec2(node.sweptRect.right, node.sweptRect.bottom);
|
||||||
|
@ -218,12 +216,3 @@ export default class TestPhysicsManager extends PhysicsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AreaCollision {
|
|
||||||
area: number;
|
|
||||||
collider: AABB;
|
|
||||||
constructor(area: number, collider: AABB){
|
|
||||||
this.area = area;
|
|
||||||
this.collider = collider;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,9 @@ import GameEvent from "../Events/GameEvent";
|
||||||
import EventQueue from "../Events/EventQueue";
|
import EventQueue from "../Events/EventQueue";
|
||||||
import { GameEventType } from "../Events/GameEventType";
|
import { GameEventType } from "../Events/GameEventType";
|
||||||
|
|
||||||
export default class Recorder{
|
// @ignorePage
|
||||||
|
|
||||||
|
export default class Recorder {
|
||||||
private receiver: Receiver;
|
private receiver: Receiver;
|
||||||
private log: Queue<LogItem>;
|
private log: Queue<LogItem>;
|
||||||
private recording: boolean;
|
private recording: boolean;
|
||||||
|
|
|
@ -3,6 +3,12 @@ import Emitter from "../../Events/Emitter";
|
||||||
import CanvasNode from "../../Nodes/CanvasNode";
|
import CanvasNode from "../../Nodes/CanvasNode";
|
||||||
import { AnimationData, AnimationState } from "./AnimationTypes";
|
import { AnimationData, AnimationState } from "./AnimationTypes";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An animation manager class for an animated CanvasNode.
|
||||||
|
* This class keeps track of the possible animations, as well as the current animation state,
|
||||||
|
* and abstracts all interactions with playing, pausing, and stopping animations as well as
|
||||||
|
* creating new animations from the CanvasNode.
|
||||||
|
*/
|
||||||
export default class AnimationManager {
|
export default class AnimationManager {
|
||||||
/** The owner of this animation manager */
|
/** The owner of this animation manager */
|
||||||
protected owner: CanvasNode;
|
protected owner: CanvasNode;
|
||||||
|
@ -40,6 +46,10 @@ export default class AnimationManager {
|
||||||
/** The onEnd event of a pending animation */
|
/** The onEnd event of a pending animation */
|
||||||
protected pendingOnEnd: string;
|
protected pendingOnEnd: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AnimationManager
|
||||||
|
* @param owner The owner of the AnimationManager
|
||||||
|
*/
|
||||||
constructor(owner: CanvasNode){
|
constructor(owner: CanvasNode){
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.animationState = AnimationState.STOPPED;
|
this.animationState = AnimationState.STOPPED;
|
||||||
|
@ -61,7 +71,10 @@ export default class AnimationManager {
|
||||||
this.animations.add(key, animation);
|
this.animations.add(key, animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the index specified by the current animation and current frame */
|
/**
|
||||||
|
* Gets the index specified by the current animation and current frame
|
||||||
|
* @returns The index in the current animation
|
||||||
|
*/
|
||||||
getIndex(): number {
|
getIndex(): number {
|
||||||
if(this.animations.has(this.currentAnimation)){
|
if(this.animations.has(this.currentAnimation)){
|
||||||
return this.animations.get(this.currentAnimation).frames[this.currentFrame].index;
|
return this.animations.get(this.currentAnimation).frames[this.currentFrame].index;
|
||||||
|
@ -72,6 +85,10 @@ export default class AnimationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current animation index and advances the animation frame
|
||||||
|
* @returns The index of the animation frame
|
||||||
|
*/
|
||||||
getIndexAndAdvanceAnimation(): number {
|
getIndexAndAdvanceAnimation(): number {
|
||||||
// If we aren't playing, we won't be advancing the animation
|
// If we aren't playing, we won't be advancing the animation
|
||||||
if(!(this.animationState === AnimationState.PLAYING)){
|
if(!(this.animationState === AnimationState.PLAYING)){
|
||||||
|
@ -109,6 +126,7 @@ export default class AnimationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ends the current animation and fires any necessary events, as well as starting any new animations */
|
||||||
protected endCurrentAnimation(): void {
|
protected endCurrentAnimation(): void {
|
||||||
this.currentFrame = 0;
|
this.currentFrame = 0;
|
||||||
this.animationState = AnimationState.STOPPED;
|
this.animationState = AnimationState.STOPPED;
|
||||||
|
@ -144,7 +162,13 @@ export default class AnimationManager {
|
||||||
this.pendingAnimation = null;
|
this.pendingAnimation = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Queues a single animation to be played after the current one. Does NOT stack */
|
/**
|
||||||
|
* Queues a single animation to be played after the current one. Does NOT stack.
|
||||||
|
* Queueing additional animations past 1 will just replace the queued animation
|
||||||
|
* @param animation The animation to queue
|
||||||
|
* @param loop Whether or not the loop the queued animation
|
||||||
|
* @param onEnd The event to fire when the queued animation ends
|
||||||
|
*/
|
||||||
queue(animation: string, loop: boolean = false, onEnd?: string): void {
|
queue(animation: string, loop: boolean = false, onEnd?: string): void {
|
||||||
this.pendingAnimation = animation;
|
this.pendingAnimation = animation;
|
||||||
this.pendingLoop = loop;
|
this.pendingLoop = loop;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { TweenableProperties } from "../../Nodes/GameNode";
|
import { TweenableProperties } from "../../Nodes/GameNode";
|
||||||
import { EaseFunctionType } from "../../Utils/EaseFunctions";
|
import { EaseFunctionType } from "../../Utils/EaseFunctions";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export enum AnimationState {
|
export enum AnimationState {
|
||||||
STOPPED = 0,
|
STOPPED = 0,
|
||||||
PAUSED = 1,
|
PAUSED = 1,
|
||||||
|
|
|
@ -4,11 +4,24 @@ import { AnimationState, TweenData } from "./AnimationTypes";
|
||||||
import EaseFunctions from "../../Utils/EaseFunctions";
|
import EaseFunctions from "../../Utils/EaseFunctions";
|
||||||
import MathUtils from "../../Utils/MathUtils";
|
import MathUtils from "../../Utils/MathUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manager for the tweens of a GameNode.
|
||||||
|
* Tweens are short animations played by interpolating between two properties using an easing function.
|
||||||
|
* For a good visual representation of easing functions, check out @link(https://easings.net/)(https://easings.net/).
|
||||||
|
* Multiple tween can be played at the same time, as long as they don't change the same property.
|
||||||
|
* This allows for some interesting polishes or animations that may be very difficult to do with sprite work alone
|
||||||
|
* - especially pixel art (such as rotations or scaling).
|
||||||
|
*/
|
||||||
export default class TweenManager {
|
export default class TweenManager {
|
||||||
|
/** The GameNode this TweenManager acts upon */
|
||||||
protected owner: GameNode;
|
protected owner: GameNode;
|
||||||
|
/** The list of created tweens */
|
||||||
protected tweens: Map<TweenData>;
|
protected tweens: Map<TweenData>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new TweenManager
|
||||||
|
* @param owner The owner of the TweenManager
|
||||||
|
*/
|
||||||
constructor(owner: GameNode){
|
constructor(owner: GameNode){
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.tweens = new Map();
|
this.tweens = new Map();
|
||||||
|
@ -83,7 +96,7 @@ export default class TweenManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops a currently playing tween
|
* Stops a currently playing tween
|
||||||
* @param key
|
* @param key The key of the tween
|
||||||
*/
|
*/
|
||||||
stop(key: string): void {
|
stop(key: string): void {
|
||||||
if(this.tweens.has(key)){
|
if(this.tweens.has(key)){
|
||||||
|
|
|
@ -20,6 +20,9 @@ import TextInput from "../Nodes/UIElements/TextInput";
|
||||||
import AnimatedSprite from "../Nodes/Sprites/AnimatedSprite";
|
import AnimatedSprite from "../Nodes/Sprites/AnimatedSprite";
|
||||||
import Vec2 from "../DataTypes/Vec2";
|
import Vec2 from "../DataTypes/Vec2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the RenderingManager class using CanvasRenderingContext2D.
|
||||||
|
*/
|
||||||
export default class CanvasRenderer extends RenderingManager {
|
export default class CanvasRenderer extends RenderingManager {
|
||||||
protected ctx: CanvasRenderingContext2D;
|
protected ctx: CanvasRenderingContext2D;
|
||||||
protected graphicRenderer: GraphicRenderer;
|
protected graphicRenderer: GraphicRenderer;
|
||||||
|
@ -33,6 +36,7 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
setScene(scene: Scene){
|
setScene(scene: Scene){
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.graphicRenderer.setScene(scene);
|
this.graphicRenderer.setScene(scene);
|
||||||
|
@ -40,6 +44,7 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
this.uiElementRenderer.setScene(scene);
|
this.uiElementRenderer.setScene(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
|
@ -56,6 +61,7 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
return this.ctx;
|
return this.ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
render(visibleSet: CanvasNode[], tilemaps: Tilemap[], uiLayers: Map<UILayer>): void {
|
render(visibleSet: CanvasNode[], tilemaps: Tilemap[], uiLayers: Map<UILayer>): void {
|
||||||
// Sort by depth, then by visible set by y-value
|
// Sort by depth, then by visible set by y-value
|
||||||
visibleSet.sort((a, b) => {
|
visibleSet.sort((a, b) => {
|
||||||
|
@ -104,6 +110,10 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
uiLayers.forEach(key => uiLayers.get(key).getItems().forEach(node => this.renderNode(<CanvasNode>node)));
|
uiLayers.forEach(key => uiLayers.get(key).getItems().forEach(node => this.renderNode(<CanvasNode>node)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a specified CanvasNode
|
||||||
|
* @param node The CanvasNode to render
|
||||||
|
*/
|
||||||
protected renderNode(node: CanvasNode): void {
|
protected renderNode(node: CanvasNode): void {
|
||||||
// Calculate the origin of the viewport according to this sprite
|
// Calculate the origin of the viewport according to this sprite
|
||||||
this.origin = this.scene.getViewTranslation(node);
|
this.origin = this.scene.getViewTranslation(node);
|
||||||
|
@ -139,6 +149,7 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
protected renderSprite(sprite: Sprite): void {
|
protected renderSprite(sprite: Sprite): void {
|
||||||
// Get the image from the resource manager
|
// Get the image from the resource manager
|
||||||
let image = this.resourceManager.getImage(sprite.imageId);
|
let image = this.resourceManager.getImage(sprite.imageId);
|
||||||
|
@ -156,16 +167,9 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
sprite.size.x, sprite.size.y,
|
sprite.size.x, sprite.size.y,
|
||||||
(-sprite.size.x*sprite.scale.x/2)*this.zoom, (-sprite.size.y*sprite.scale.y/2)*this.zoom,
|
(-sprite.size.x*sprite.scale.x/2)*this.zoom, (-sprite.size.y*sprite.scale.y/2)*this.zoom,
|
||||||
sprite.size.x * sprite.scale.x*this.zoom, sprite.size.y * sprite.scale.y*this.zoom);
|
sprite.size.x * sprite.scale.x*this.zoom, sprite.size.y * sprite.scale.y*this.zoom);
|
||||||
|
|
||||||
// Debug mode
|
|
||||||
if(this.debug){
|
|
||||||
this.ctx.lineWidth = 4;
|
|
||||||
this.ctx.strokeStyle = "#00FF00"
|
|
||||||
let b = sprite.boundary;
|
|
||||||
this.ctx.strokeRect(-b.hw*this.zoom, -b.hh*this.zoom, b.hw*2*this.zoom, b.hh*2*this.zoom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
protected renderAnimatedSprite(sprite: AnimatedSprite): void {
|
protected renderAnimatedSprite(sprite: AnimatedSprite): void {
|
||||||
// Get the image from the resource manager
|
// Get the image from the resource manager
|
||||||
let image = this.resourceManager.getImage(sprite.imageId);
|
let image = this.resourceManager.getImage(sprite.imageId);
|
||||||
|
@ -187,16 +191,9 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
sprite.size.x, sprite.size.y,
|
sprite.size.x, sprite.size.y,
|
||||||
(-sprite.size.x*sprite.scale.x/2)*this.zoom, (-sprite.size.y*sprite.scale.y/2)*this.zoom,
|
(-sprite.size.x*sprite.scale.x/2)*this.zoom, (-sprite.size.y*sprite.scale.y/2)*this.zoom,
|
||||||
sprite.size.x * sprite.scale.x*this.zoom, sprite.size.y * sprite.scale.y*this.zoom);
|
sprite.size.x * sprite.scale.x*this.zoom, sprite.size.y * sprite.scale.y*this.zoom);
|
||||||
|
|
||||||
// Debug mode
|
|
||||||
if(this.debug){
|
|
||||||
this.ctx.lineWidth = 4;
|
|
||||||
this.ctx.strokeStyle = "#00FF00"
|
|
||||||
let b = sprite.boundary;
|
|
||||||
this.ctx.strokeRect(-b.hw*this.zoom, -b.hh*this.zoom, b.hw*2*this.zoom, b.hh*2*this.zoom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
protected renderGraphic(graphic: Graphic): void {
|
protected renderGraphic(graphic: Graphic): void {
|
||||||
if(graphic instanceof Point){
|
if(graphic instanceof Point){
|
||||||
this.graphicRenderer.renderPoint(<Point>graphic, this.zoom);
|
this.graphicRenderer.renderPoint(<Point>graphic, this.zoom);
|
||||||
|
@ -205,12 +202,14 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
protected renderTilemap(tilemap: Tilemap): void {
|
protected renderTilemap(tilemap: Tilemap): void {
|
||||||
if(tilemap instanceof OrthogonalTilemap){
|
if(tilemap instanceof OrthogonalTilemap){
|
||||||
this.tilemapRenderer.renderOrthogonalTilemap(<OrthogonalTilemap>tilemap);
|
this.tilemapRenderer.renderOrthogonalTilemap(<OrthogonalTilemap>tilemap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
protected renderUIElement(uiElement: UIElement): void {
|
protected renderUIElement(uiElement: UIElement): void {
|
||||||
if(uiElement instanceof Label){
|
if(uiElement instanceof Label){
|
||||||
this.uiElementRenderer.renderLabel(uiElement);
|
this.uiElementRenderer.renderLabel(uiElement);
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
import Vec2 from "../../DataTypes/Vec2";
|
|
||||||
import Point from "../../Nodes/Graphics/Point";
|
import Point from "../../Nodes/Graphics/Point";
|
||||||
import Rect from "../../Nodes/Graphics/Rect";
|
import Rect from "../../Nodes/Graphics/Rect";
|
||||||
import ResourceManager from "../../ResourceManager/ResourceManager";
|
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||||
import Scene from "../../Scene/Scene";
|
import Scene from "../../Scene/Scene";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class to help the @reference[CanvasRenderer] render @reference[Graphic]s
|
||||||
|
*/
|
||||||
export default class GraphicRenderer {
|
export default class GraphicRenderer {
|
||||||
|
/** The resource manager of the game engine */
|
||||||
protected resourceManager: ResourceManager;
|
protected resourceManager: ResourceManager;
|
||||||
|
/** The current scene */
|
||||||
protected scene: Scene;
|
protected scene: Scene;
|
||||||
|
/** The rendering context */
|
||||||
protected ctx: CanvasRenderingContext2D;
|
protected ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
constructor(ctx: CanvasRenderingContext2D){
|
constructor(ctx: CanvasRenderingContext2D){
|
||||||
|
@ -14,16 +19,30 @@ export default class GraphicRenderer {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scene of this GraphicRenderer
|
||||||
|
* @param scene The current scene
|
||||||
|
*/
|
||||||
setScene(scene: Scene): void {
|
setScene(scene: Scene): void {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a point
|
||||||
|
* @param point The point to render
|
||||||
|
* @param zoom The zoom level
|
||||||
|
*/
|
||||||
renderPoint(point: Point, zoom: number): void {
|
renderPoint(point: Point, zoom: number): void {
|
||||||
this.ctx.fillStyle = point.color.toStringRGBA();
|
this.ctx.fillStyle = point.color.toStringRGBA();
|
||||||
this.ctx.fillRect((-point.size.x/2)*zoom, (-point.size.y/2)*zoom,
|
this.ctx.fillRect((-point.size.x/2)*zoom, (-point.size.y/2)*zoom,
|
||||||
point.size.x*zoom, point.size.y*zoom);
|
point.size.x*zoom, point.size.y*zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a rect
|
||||||
|
* @param rect The rect to render
|
||||||
|
* @param zoom The zoom level
|
||||||
|
*/
|
||||||
renderRect(rect: Rect, zoom: number): void {
|
renderRect(rect: Rect, zoom: number): void {
|
||||||
// Draw the interior of the rect
|
// Draw the interior of the rect
|
||||||
if(rect.color.a !== 0){
|
if(rect.color.a !== 0){
|
||||||
|
|
|
@ -4,6 +4,9 @@ import OrthogonalTilemap from "../../Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
import Vec2 from "../../DataTypes/Vec2";
|
import Vec2 from "../../DataTypes/Vec2";
|
||||||
import Tileset from "../../DataTypes/Tilesets/Tileset";
|
import Tileset from "../../DataTypes/Tilesets/Tileset";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for the @reference[CanvasRenderer] to render @reference[Tilemap]s
|
||||||
|
*/
|
||||||
export default class TilemapRenderer {
|
export default class TilemapRenderer {
|
||||||
protected resourceManager: ResourceManager;
|
protected resourceManager: ResourceManager;
|
||||||
protected scene: Scene;
|
protected scene: Scene;
|
||||||
|
@ -14,10 +17,18 @@ export default class TilemapRenderer {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scene of this TilemapRenderer
|
||||||
|
* @param scene The current scene
|
||||||
|
*/
|
||||||
setScene(scene: Scene): void {
|
setScene(scene: Scene): void {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders an orthogonal tilemap
|
||||||
|
* @param tilemap The tilemap to render
|
||||||
|
*/
|
||||||
renderOrthogonalTilemap(tilemap: OrthogonalTilemap): void {
|
renderOrthogonalTilemap(tilemap: OrthogonalTilemap): void {
|
||||||
let previousAlpha = this.ctx.globalAlpha;
|
let previousAlpha = this.ctx.globalAlpha;
|
||||||
this.ctx.globalAlpha = tilemap.getLayer().getAlpha();
|
this.ctx.globalAlpha = tilemap.getLayer().getAlpha();
|
||||||
|
@ -49,6 +60,16 @@ export default class TilemapRenderer {
|
||||||
this.ctx.globalAlpha = previousAlpha;
|
this.ctx.globalAlpha = previousAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a tile
|
||||||
|
* @param tileset The tileset this tile belongs to
|
||||||
|
* @param tileIndex The index of the tile
|
||||||
|
* @param tilemapRow The row of the tile in the tilemap
|
||||||
|
* @param tilemapCol The column of the tile in the tilemap
|
||||||
|
* @param origin The origin of the viewport
|
||||||
|
* @param scale The scale of the tilemap
|
||||||
|
* @param zoom The zoom level of the viewport
|
||||||
|
*/
|
||||||
protected renderTile(tileset: Tileset, tileIndex: number, tilemapRow: number, tilemapCol: number, origin: Vec2, scale: Vec2, zoom: number): void {
|
protected renderTile(tileset: Tileset, tileIndex: number, tilemapRow: number, tilemapCol: number, origin: Vec2, scale: Vec2, zoom: number): void {
|
||||||
let image = this.resourceManager.getImage(tileset.getImageKey());
|
let image = this.resourceManager.getImage(tileset.getImageKey());
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||||
import Scene from "../../Scene/Scene";
|
import Scene from "../../Scene/Scene";
|
||||||
import MathUtils from "../../Utils/MathUtils";
|
import MathUtils from "../../Utils/MathUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class to help the @reference[CanvasRenderer] render @reference[UIElement]s
|
||||||
|
*/
|
||||||
export default class UIElementRenderer {
|
export default class UIElementRenderer {
|
||||||
protected resourceManager: ResourceManager;
|
protected resourceManager: ResourceManager;
|
||||||
protected scene: Scene;
|
protected scene: Scene;
|
||||||
|
@ -17,10 +20,18 @@ export default class UIElementRenderer {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scene of this UIElementRenderer
|
||||||
|
* @param scene The current scene
|
||||||
|
*/
|
||||||
setScene(scene: Scene): void {
|
setScene(scene: Scene): void {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a label
|
||||||
|
* @param label The label to render
|
||||||
|
*/
|
||||||
renderLabel(label: Label): void {
|
renderLabel(label: Label): void {
|
||||||
// If the size is unassigned (by the user or automatically) assign it
|
// If the size is unassigned (by the user or automatically) assign it
|
||||||
label.handleInitialSizing(this.ctx);
|
label.handleInitialSizing(this.ctx);
|
||||||
|
@ -51,10 +62,18 @@ export default class UIElementRenderer {
|
||||||
this.ctx.globalAlpha = previousAlpha;
|
this.ctx.globalAlpha = previousAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a button
|
||||||
|
* @param button The button to render
|
||||||
|
*/
|
||||||
renderButton(button: Button): void {
|
renderButton(button: Button): void {
|
||||||
this.renderLabel(button);
|
this.renderLabel(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a slider
|
||||||
|
* @param slider The slider to render
|
||||||
|
*/
|
||||||
renderSlider(slider: Slider): void {
|
renderSlider(slider: Slider): void {
|
||||||
// Grab the global alpha so we can adjust it for this render
|
// Grab the global alpha so we can adjust it for this render
|
||||||
let previousAlpha = this.ctx.globalAlpha;
|
let previousAlpha = this.ctx.globalAlpha;
|
||||||
|
@ -82,6 +101,10 @@ export default class UIElementRenderer {
|
||||||
this.ctx.globalAlpha = previousAlpha;
|
this.ctx.globalAlpha = previousAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a textInput
|
||||||
|
* @param textInput The textInput to render
|
||||||
|
*/
|
||||||
renderTextInput(textInput: TextInput): void {
|
renderTextInput(textInput: TextInput): void {
|
||||||
// Show a cursor sometimes
|
// Show a cursor sometimes
|
||||||
if(textInput.focused && textInput.cursorCounter % 60 > 30){
|
if(textInput.focused && textInput.cursorCounter % 60 > 30){
|
||||||
|
|
|
@ -9,32 +9,73 @@ import ResourceManager from "../ResourceManager/ResourceManager";
|
||||||
import UILayer from "../Scene/Layers/UILayer";
|
import UILayer from "../Scene/Layers/UILayer";
|
||||||
import Scene from "../Scene/Scene";
|
import Scene from "../Scene/Scene";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract framework to put all rendering in once place in the application
|
||||||
|
*/
|
||||||
export default abstract class RenderingManager {
|
export default abstract class RenderingManager {
|
||||||
// Give the renderer access to the resource manager
|
/** The ResourceManager */
|
||||||
protected resourceManager: ResourceManager;
|
protected resourceManager: ResourceManager;
|
||||||
|
|
||||||
|
/** The scene currently being rendered */
|
||||||
protected scene: Scene;
|
protected scene: Scene;
|
||||||
debug: boolean;
|
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
this.resourceManager = ResourceManager.getInstance();
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
this.debug = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scene currently being rendered
|
||||||
|
* @param scene The current Scene
|
||||||
|
*/
|
||||||
setScene(scene: Scene): void {
|
setScene(scene: Scene): void {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the canvas for the game
|
||||||
|
* @param canvas The canvas element
|
||||||
|
* @param width The desired width of the canvas
|
||||||
|
* @param height The desired height of the canvas
|
||||||
|
* @returns The rendering context of the canvas
|
||||||
|
*/
|
||||||
abstract initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): any;
|
abstract initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the visible set of CanvasNodes and visible portions of tilemaps, as well as any UIElement in UILayers
|
||||||
|
* @param visibleSet The visible set of CanvasNodes
|
||||||
|
* @param tilemaps The tilemaps used in the application
|
||||||
|
* @param uiLayers The user interface layers
|
||||||
|
*/
|
||||||
abstract render(visibleSet: Array<CanvasNode>, tilemaps: Array<Tilemap>, uiLayers: Map<UILayer>): void;
|
abstract render(visibleSet: Array<CanvasNode>, tilemaps: Array<Tilemap>, uiLayers: Map<UILayer>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a sprite
|
||||||
|
* @param sprite The sprite to render
|
||||||
|
*/
|
||||||
protected abstract renderSprite(sprite: Sprite): void;
|
protected abstract renderSprite(sprite: Sprite): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders an animated sprite
|
||||||
|
* @param sprite The animated sprite to render
|
||||||
|
*/
|
||||||
protected abstract renderAnimatedSprite(sprite: AnimatedSprite): void;
|
protected abstract renderAnimatedSprite(sprite: AnimatedSprite): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a graphic
|
||||||
|
* @param graphic The graphic to render
|
||||||
|
*/
|
||||||
protected abstract renderGraphic(graphic: Graphic): void;
|
protected abstract renderGraphic(graphic: Graphic): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a tilemap
|
||||||
|
* @param tilemap The tilemap to render
|
||||||
|
*/
|
||||||
protected abstract renderTilemap(tilemap: Tilemap): void;
|
protected abstract renderTilemap(tilemap: Tilemap): void;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a UIElement
|
||||||
|
* @param uiElement The UIElement to render
|
||||||
|
*/
|
||||||
protected abstract renderUIElement(uiElement: UIElement): void;
|
protected abstract renderUIElement(uiElement: UIElement): void;
|
||||||
}
|
}
|
|
@ -5,16 +5,27 @@ import StringUtils from "../Utils/StringUtils";
|
||||||
import AudioManager from "../Sound/AudioManager";
|
import AudioManager from "../Sound/AudioManager";
|
||||||
import Spritesheet from "../DataTypes/Spritesheet";
|
import Spritesheet from "../DataTypes/Spritesheet";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource manager for the game engine.
|
||||||
|
* The resource manager interfaces with the loadable assets of a game such as images, data files,
|
||||||
|
* and sounds, which are all found in the dist folder.
|
||||||
|
* This class controls loading and updates the @reference[Scene] with the loading progress, so that the scene does
|
||||||
|
* not start before all necessary assets are loaded.
|
||||||
|
*/
|
||||||
export default class ResourceManager {
|
export default class ResourceManager {
|
||||||
// Instance for the singleton class
|
// Instance for the singleton class
|
||||||
private static instance: ResourceManager;
|
private static instance: ResourceManager;
|
||||||
|
|
||||||
// Booleans to keep track of whether or not the ResourceManager is currently loading something
|
// Booleans to keep track of whether or not the ResourceManager is currently loading something
|
||||||
|
/** Whether or not any resources are loading */
|
||||||
private loading: boolean;
|
private loading: boolean;
|
||||||
|
/** A boolean to indicate that the assets just finished loading */
|
||||||
private justLoaded: boolean;
|
private justLoaded: boolean;
|
||||||
|
|
||||||
// Functions to do something when loading progresses or is completed such as render a loading screen
|
// Functions to do something when loading progresses or is completed such as render a loading screen
|
||||||
|
/** A function that is called when loading progresses */
|
||||||
public onLoadProgress: Function;
|
public onLoadProgress: Function;
|
||||||
|
/** A function that is called when loading completes */
|
||||||
public onLoadComplete: Function;
|
public onLoadComplete: Function;
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +34,7 @@ export default class ResourceManager {
|
||||||
/** Number to keep track of how many images are loaded */
|
/** Number to keep track of how many images are loaded */
|
||||||
private loadonly_imagesToLoad: number;
|
private loadonly_imagesToLoad: number;
|
||||||
/** The queue of images we must load */
|
/** The queue of images we must load */
|
||||||
private loadonly_imageLoadingQueue: Queue<{key: string, path: string}>;
|
private loadonly_imageLoadingQueue: Queue<KeyPathPair>;
|
||||||
/** A map of the images that are currently loaded and (presumably) being used by the scene */
|
/** A map of the images that are currently loaded and (presumably) being used by the scene */
|
||||||
private images: Map<HTMLImageElement>;
|
private images: Map<HTMLImageElement>;
|
||||||
|
|
||||||
|
@ -32,7 +43,7 @@ export default class ResourceManager {
|
||||||
/** Number to keep track of how many tilemaps are loaded */
|
/** Number to keep track of how many tilemaps are loaded */
|
||||||
private loadonly_spritesheetsToLoad: number;
|
private loadonly_spritesheetsToLoad: number;
|
||||||
/** The queue of tilemaps we must load */
|
/** The queue of tilemaps we must load */
|
||||||
private loadonly_spritesheetLoadingQueue: Queue<{key: string, path: string}>;
|
private loadonly_spritesheetLoadingQueue: Queue<KeyPathPair>;
|
||||||
/** A map of the tilemaps that are currently loaded and (presumably) being used by the scene */
|
/** A map of the tilemaps that are currently loaded and (presumably) being used by the scene */
|
||||||
private spritesheets: Map<Spritesheet>;
|
private spritesheets: Map<Spritesheet>;
|
||||||
|
|
||||||
|
@ -41,7 +52,7 @@ export default class ResourceManager {
|
||||||
/** Number to keep track of how many tilemaps are loaded */
|
/** Number to keep track of how many tilemaps are loaded */
|
||||||
private loadonly_tilemapsToLoad: number;
|
private loadonly_tilemapsToLoad: number;
|
||||||
/** The queue of tilemaps we must load */
|
/** The queue of tilemaps we must load */
|
||||||
private loadonly_tilemapLoadingQueue: Queue<{key: string, path: string}>;
|
private loadonly_tilemapLoadingQueue: Queue<KeyPathPair>;
|
||||||
/** A map of the tilemaps that are currently loaded and (presumably) being used by the scene */
|
/** A map of the tilemaps that are currently loaded and (presumably) being used by the scene */
|
||||||
private tilemaps: Map<TiledTilemapData>;
|
private tilemaps: Map<TiledTilemapData>;
|
||||||
|
|
||||||
|
@ -50,7 +61,7 @@ export default class ResourceManager {
|
||||||
/** Number to keep track of how many sounds are loaded */
|
/** Number to keep track of how many sounds are loaded */
|
||||||
private loadonly_audioToLoad: number;
|
private loadonly_audioToLoad: number;
|
||||||
/** The queue of sounds we must load */
|
/** The queue of sounds we must load */
|
||||||
private loadonly_audioLoadingQueue: Queue<{key: string, path: string}>;
|
private loadonly_audioLoadingQueue: Queue<KeyPathPair>;
|
||||||
/** A map of the sounds that are currently loaded and (presumably) being used by the scene */
|
/** A map of the sounds that are currently loaded and (presumably) being used by the scene */
|
||||||
private audioBuffers: Map<AudioBuffer>;
|
private audioBuffers: Map<AudioBuffer>;
|
||||||
|
|
||||||
|
@ -84,6 +95,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current instance of this class or a new instance if none exist
|
* Returns the current instance of this class or a new instance if none exist
|
||||||
|
* @returns The resource manager
|
||||||
*/
|
*/
|
||||||
static getInstance(): ResourceManager {
|
static getInstance(): ResourceManager {
|
||||||
if(!this.instance){
|
if(!this.instance){
|
||||||
|
@ -105,6 +117,7 @@ export default class ResourceManager {
|
||||||
/**
|
/**
|
||||||
* Retrieves a loaded image
|
* Retrieves a loaded image
|
||||||
* @param key The key of the loaded image
|
* @param key The key of the loaded image
|
||||||
|
* @returns The image element associated with this key
|
||||||
*/
|
*/
|
||||||
public getImage(key: string): HTMLImageElement {
|
public getImage(key: string): HTMLImageElement {
|
||||||
let image = this.images.get(key);
|
let image = this.images.get(key);
|
||||||
|
@ -114,18 +127,28 @@ export default class ResourceManager {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a spritesheet from file
|
||||||
|
* @param key The key to associate the loaded spritesheet with
|
||||||
|
* @param path The path to the spritesheet to load
|
||||||
|
*/
|
||||||
public spritesheet(key: string, path: string): void {
|
public spritesheet(key: string, path: string): void {
|
||||||
this.loadonly_spritesheetLoadingQueue.enqueue({key: key, path: path});
|
this.loadonly_spritesheetLoadingQueue.enqueue({key: key, path: path});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a loaded spritesheet
|
||||||
|
* @param key The key of the spritesheet to load
|
||||||
|
* @returns The loaded Spritesheet
|
||||||
|
*/
|
||||||
public getSpritesheet(key: string): Spritesheet {
|
public getSpritesheet(key: string): Spritesheet {
|
||||||
return this.spritesheets.get(key);
|
return this.spritesheets.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load an audio file
|
* Loads an audio file
|
||||||
* @param key
|
* @param key The key to associate with the loaded audio file
|
||||||
* @param path
|
* @param path The path to the audio file to load
|
||||||
*/
|
*/
|
||||||
public audio(key: string, path: string): void {
|
public audio(key: string, path: string): void {
|
||||||
this.loadonly_audioLoadingQueue.enqueue({key: key, path: path});
|
this.loadonly_audioLoadingQueue.enqueue({key: key, path: path});
|
||||||
|
@ -133,7 +156,8 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a loaded audio file
|
* Retrieves a loaded audio file
|
||||||
* @param key
|
* @param key The key of the audio file to load
|
||||||
|
* @returns The AudioBuffer created from the loaded audio fle
|
||||||
*/
|
*/
|
||||||
public getAudio(key: string): AudioBuffer {
|
public getAudio(key: string): AudioBuffer {
|
||||||
return this.audioBuffers.get(key);
|
return this.audioBuffers.get(key);
|
||||||
|
@ -141,8 +165,8 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a tilemap from a json file. Automatically loads related images
|
* Load a tilemap from a json file. Automatically loads related images
|
||||||
* @param key
|
* @param key The key to associate with the loaded tilemap
|
||||||
* @param path
|
* @param path The path to the tilemap to load
|
||||||
*/
|
*/
|
||||||
public tilemap(key: string, path: string): void {
|
public tilemap(key: string, path: string): void {
|
||||||
this.loadonly_tilemapLoadingQueue.enqueue({key: key, path: path});
|
this.loadonly_tilemapLoadingQueue.enqueue({key: key, path: path});
|
||||||
|
@ -150,7 +174,8 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retreives a loaded tilemap
|
* Retreives a loaded tilemap
|
||||||
* @param key
|
* @param key The key of the loaded tilemap
|
||||||
|
* @returns The tilemap data associated with the key
|
||||||
*/
|
*/
|
||||||
public getTilemap(key: string): TiledTilemapData {
|
public getTilemap(key: string): TiledTilemapData {
|
||||||
return this.tilemaps.get(key);
|
return this.tilemaps.get(key);
|
||||||
|
@ -158,7 +183,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all resources currently in the queue
|
* Loads all resources currently in the queue
|
||||||
* @param callback
|
* @param callback The function to cal when the resources are finished loading
|
||||||
*/
|
*/
|
||||||
loadResourcesFromQueue(callback: Function): void {
|
loadResourcesFromQueue(callback: Function): void {
|
||||||
this.loadonly_typesToLoad = 3;
|
this.loadonly_typesToLoad = 3;
|
||||||
|
@ -211,7 +236,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all tilemaps currently in the tilemap loading queue
|
* Loads all tilemaps currently in the tilemap loading queue
|
||||||
* @param onFinishLoading
|
* @param onFinishLoading The function to call when loading is complete
|
||||||
*/
|
*/
|
||||||
private loadTilemapsFromQueue(onFinishLoading: Function): void {
|
private loadTilemapsFromQueue(onFinishLoading: Function): void {
|
||||||
this.loadonly_tilemapsToLoad = this.loadonly_tilemapLoadingQueue.getSize();
|
this.loadonly_tilemapsToLoad = this.loadonly_tilemapLoadingQueue.getSize();
|
||||||
|
@ -230,9 +255,9 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a singular tilemap
|
* Loads a singular tilemap
|
||||||
* @param key
|
* @param key The key of the tilemap
|
||||||
* @param pathToTilemapJSON
|
* @param pathToTilemapJSON The path to the tilemap JSON file
|
||||||
* @param callbackIfLast
|
* @param callbackIfLast The function to call if this is the last tilemap to load
|
||||||
*/
|
*/
|
||||||
private loadTilemap(key: string, pathToTilemapJSON: string, callbackIfLast: Function): void {
|
private loadTilemap(key: string, pathToTilemapJSON: string, callbackIfLast: Function): void {
|
||||||
this.loadTextFile(pathToTilemapJSON, (fileText: string) => {
|
this.loadTextFile(pathToTilemapJSON, (fileText: string) => {
|
||||||
|
@ -263,7 +288,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish loading a tilemap. Calls the callback function if this is the last tilemap being loaded
|
* Finish loading a tilemap. Calls the callback function if this is the last tilemap being loaded
|
||||||
* @param callback
|
* @param callback The function to call if this is the last tilemap to load
|
||||||
*/
|
*/
|
||||||
private finishLoadingTilemap(callback: Function): void {
|
private finishLoadingTilemap(callback: Function): void {
|
||||||
this.loadonly_tilemapsLoaded += 1;
|
this.loadonly_tilemapsLoaded += 1;
|
||||||
|
@ -274,9 +299,9 @@ export default class ResourceManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all spritesheets currently in the spritesheet loading queue
|
* Loads all spritesheets currently in the spritesheet loading queue
|
||||||
* @param onFinishLoading
|
* @param onFinishLoading The function to call when the spritesheets are done loading
|
||||||
*/
|
*/
|
||||||
private loadSpritesheetsFromQueue(onFinishLoading: Function): void {
|
private loadSpritesheetsFromQueue(onFinishLoading: Function): void {
|
||||||
this.loadonly_spritesheetsToLoad = this.loadonly_spritesheetLoadingQueue.getSize();
|
this.loadonly_spritesheetsToLoad = this.loadonly_spritesheetLoadingQueue.getSize();
|
||||||
|
@ -295,9 +320,9 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a singular spritesheet
|
* Loads a singular spritesheet
|
||||||
* @param key
|
* @param key The key of the spritesheet to load
|
||||||
* @param pathToSpritesheetJSON
|
* @param pathToSpritesheetJSON The path to the spritesheet JSON file
|
||||||
* @param callbackIfLast
|
* @param callbackIfLast The function to call if this is the last spritesheet
|
||||||
*/
|
*/
|
||||||
private loadSpritesheet(key: string, pathToSpritesheetJSON: string, callbackIfLast: Function): void {
|
private loadSpritesheet(key: string, pathToSpritesheetJSON: string, callbackIfLast: Function): void {
|
||||||
this.loadTextFile(pathToSpritesheetJSON, (fileText: string) => {
|
this.loadTextFile(pathToSpritesheetJSON, (fileText: string) => {
|
||||||
|
@ -317,7 +342,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish loading a spritesheet. Calls the callback function if this is the last spritesheet being loaded
|
* Finish loading a spritesheet. Calls the callback function if this is the last spritesheet being loaded
|
||||||
* @param callback
|
* @param callback The function to call if this is the last spritesheet to load
|
||||||
*/
|
*/
|
||||||
private finishLoadingSpritesheet(callback: Function): void {
|
private finishLoadingSpritesheet(callback: Function): void {
|
||||||
this.loadonly_spritesheetsLoaded += 1;
|
this.loadonly_spritesheetsLoaded += 1;
|
||||||
|
@ -330,7 +355,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all images currently in the image loading queue
|
* Loads all images currently in the image loading queue
|
||||||
* @param onFinishLoading
|
* @param onFinishLoading The function to call when there are no more images to load
|
||||||
*/
|
*/
|
||||||
private loadImagesFromQueue(onFinishLoading: Function): void {
|
private loadImagesFromQueue(onFinishLoading: Function): void {
|
||||||
this.loadonly_imagesToLoad = this.loadonly_imageLoadingQueue.getSize();
|
this.loadonly_imagesToLoad = this.loadonly_imageLoadingQueue.getSize();
|
||||||
|
@ -349,9 +374,9 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a singular image
|
* Loads a singular image
|
||||||
* @param key
|
* @param key The key of the image to load
|
||||||
* @param path
|
* @param path The path to the image to load
|
||||||
* @param callbackIfLast
|
* @param callbackIfLast The function to call if this is the last image
|
||||||
*/
|
*/
|
||||||
public loadImage(key: string, path: string, callbackIfLast: Function): void {
|
public loadImage(key: string, path: string, callbackIfLast: Function): void {
|
||||||
var image = new Image();
|
var image = new Image();
|
||||||
|
@ -369,7 +394,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish loading an image. If this is the last image, it calls the callback function
|
* Finish loading an image. If this is the last image, it calls the callback function
|
||||||
* @param callback
|
* @param callback The function to call if this is the last image
|
||||||
*/
|
*/
|
||||||
private finishLoadingImage(callback: Function): void {
|
private finishLoadingImage(callback: Function): void {
|
||||||
this.loadonly_imagesLoaded += 1;
|
this.loadonly_imagesLoaded += 1;
|
||||||
|
@ -382,7 +407,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all audio currently in the tilemap loading queue
|
* Loads all audio currently in the tilemap loading queue
|
||||||
* @param onFinishLoading
|
* @param onFinishLoading The function to call when tilemaps are done loading
|
||||||
*/
|
*/
|
||||||
private loadAudioFromQueue(onFinishLoading: Function){
|
private loadAudioFromQueue(onFinishLoading: Function){
|
||||||
this.loadonly_audioToLoad = this.loadonly_audioLoadingQueue.getSize();
|
this.loadonly_audioToLoad = this.loadonly_audioLoadingQueue.getSize();
|
||||||
|
@ -401,9 +426,9 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a singular audio file
|
* Load a singular audio file
|
||||||
* @param key
|
* @param key The key to the audio file to load
|
||||||
* @param path
|
* @param path The path to the audio file to load
|
||||||
* @param callbackIfLast
|
* @param callbackIfLast The function to call if this is the last audio file to load
|
||||||
*/
|
*/
|
||||||
private loadAudio(key: string, path: string, callbackIfLast: Function): void {
|
private loadAudio(key: string, path: string, callbackIfLast: Function): void {
|
||||||
let audioCtx = AudioManager.getInstance().getAudioContext();
|
let audioCtx = AudioManager.getInstance().getAudioContext();
|
||||||
|
@ -428,7 +453,7 @@ export default class ResourceManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish loading an audio file. Calls the callback functon if this is the last audio sample being loaded.
|
* Finish loading an audio file. Calls the callback functon if this is the last audio sample being loaded.
|
||||||
* @param callback
|
* @param callback The function to call if this is the last audio file to load
|
||||||
*/
|
*/
|
||||||
private finishLoadingAudio(callback: Function): void {
|
private finishLoadingAudio(callback: Function): void {
|
||||||
this.loadonly_audioLoaded += 1;
|
this.loadonly_audioLoaded += 1;
|
||||||
|
@ -459,7 +484,7 @@ export default class ResourceManager {
|
||||||
/ this.loadonly_typesToLoad;
|
/ this.loadonly_typesToLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
if(this.loading){
|
if(this.loading){
|
||||||
if(this.onLoadProgress){
|
if(this.onLoadProgress){
|
||||||
this.onLoadProgress(this.getLoadPercent());
|
this.onLoadProgress(this.getLoadPercent());
|
||||||
|
@ -472,3 +497,8 @@ export default class ResourceManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KeyPathPair {
|
||||||
|
key: string
|
||||||
|
path: string
|
||||||
|
}
|
|
@ -13,9 +13,13 @@ import Slider from "../../Nodes/UIElements/Slider";
|
||||||
import TextInput from "../../Nodes/UIElements/TextInput";
|
import TextInput from "../../Nodes/UIElements/TextInput";
|
||||||
import Rect from "../../Nodes/Graphics/Rect";
|
import Rect from "../../Nodes/Graphics/Rect";
|
||||||
import ResourceManager from "../../ResourceManager/ResourceManager";
|
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||||
import UILayer from "../Layers/UILayer";
|
|
||||||
import ParallaxLayer from "../Layers/ParallaxLayer";
|
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory that abstracts adding @reference[CanvasNode]s to the @reference[Scene].
|
||||||
|
* Access methods in this factory through Scene.add.[methodName]().
|
||||||
|
*/
|
||||||
export default class CanvasNodeFactory {
|
export default class CanvasNodeFactory {
|
||||||
protected scene: Scene;
|
protected scene: Scene;
|
||||||
protected resourceManager: ResourceManager;
|
protected resourceManager: ResourceManager;
|
||||||
|
@ -30,6 +34,7 @@ export default class CanvasNodeFactory {
|
||||||
* @param type The type of UIElement to add
|
* @param type The type of UIElement to add
|
||||||
* @param layerName The layer to add the UIElement to
|
* @param layerName The layer to add the UIElement to
|
||||||
* @param options Any additional arguments to feed to the constructor
|
* @param options Any additional arguments to feed to the constructor
|
||||||
|
* @returns A new UIElement
|
||||||
*/
|
*/
|
||||||
addUIElement = (type: string | UIElementType, layerName: string, options?: Record<string, any>): UIElement => {
|
addUIElement = (type: string | UIElementType, layerName: string, options?: Record<string, any>): UIElement => {
|
||||||
// Get the layer
|
// Get the layer
|
||||||
|
@ -68,6 +73,7 @@ export default class CanvasNodeFactory {
|
||||||
* Adds a sprite to the current scene
|
* Adds a sprite to the current scene
|
||||||
* @param key The key of the image the sprite will represent
|
* @param key The key of the image the sprite will represent
|
||||||
* @param layerName The layer on which to add the sprite
|
* @param layerName The layer on which to add the sprite
|
||||||
|
* @returns A new Sprite
|
||||||
*/
|
*/
|
||||||
addSprite = (key: string, layerName: string): Sprite => {
|
addSprite = (key: string, layerName: string): Sprite => {
|
||||||
let layer = this.scene.getLayer(layerName);
|
let layer = this.scene.getLayer(layerName);
|
||||||
|
@ -88,6 +94,12 @@ export default class CanvasNodeFactory {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an AnimatedSprite to the current scene
|
||||||
|
* @param key The key of the image the sprite will represent
|
||||||
|
* @param layerName The layer on which to add the sprite
|
||||||
|
* @returns A new AnimatedSprite
|
||||||
|
*/
|
||||||
addAnimatedSprite = (key: string, layerName: string): AnimatedSprite => {
|
addAnimatedSprite = (key: string, layerName: string): AnimatedSprite => {
|
||||||
let layer = this.scene.getLayer(layerName);
|
let layer = this.scene.getLayer(layerName);
|
||||||
let spritesheet = this.resourceManager.getSpritesheet(key);
|
let spritesheet = this.resourceManager.getSpritesheet(key);
|
||||||
|
@ -112,6 +124,7 @@ export default class CanvasNodeFactory {
|
||||||
* @param type The type of graphic to add
|
* @param type The type of graphic to add
|
||||||
* @param layerName The layer on which to add the graphic
|
* @param layerName The layer on which to add the graphic
|
||||||
* @param options Any additional arguments to send to the graphic constructor
|
* @param options Any additional arguments to send to the graphic constructor
|
||||||
|
* @returns A new Graphic
|
||||||
*/
|
*/
|
||||||
addGraphic = (type: GraphicType | string, layerName: string, options?: Record<string, any>): Graphic => {
|
addGraphic = (type: GraphicType | string, layerName: string, options?: Record<string, any>): Graphic => {
|
||||||
// Get the layer
|
// Get the layer
|
||||||
|
|
|
@ -2,7 +2,18 @@ import Scene from "../Scene";
|
||||||
import CanvasNodeFactory from "./CanvasNodeFactory";
|
import CanvasNodeFactory from "./CanvasNodeFactory";
|
||||||
import TilemapFactory from "./TilemapFactory";
|
import TilemapFactory from "./TilemapFactory";
|
||||||
import Tilemap from "../../Nodes/Tilemap";
|
import Tilemap from "../../Nodes/Tilemap";
|
||||||
|
import { UIElementType } from "../../Nodes/UIElements/UIElementTypes";
|
||||||
|
import UIElement from "../../Nodes/UIElement";
|
||||||
|
import Sprite from "../../Nodes/Sprites/Sprite";
|
||||||
|
import { GraphicType } from "../../Nodes/Graphics/GraphicTypes";
|
||||||
|
import Graphic from "../../Nodes/Graphic";
|
||||||
|
import AnimatedSprite from "../../Nodes/Sprites/AnimatedSprite";
|
||||||
|
import Vec2 from "../../DataTypes/Vec2";
|
||||||
|
import Layer from "../Layer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The manager of all factories used for adding @reference[GameNode]s to the @reference[Scene].
|
||||||
|
*/
|
||||||
export default class FactoryManager {
|
export default class FactoryManager {
|
||||||
|
|
||||||
// Constructors are called here to allow assignment of their functions to functions in this class
|
// Constructors are called here to allow assignment of their functions to functions in this class
|
||||||
|
@ -15,9 +26,56 @@ export default class FactoryManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose all of the factories through the factory manager
|
// Expose all of the factories through the factory manager
|
||||||
uiElement = this.canvasNodeFactory.addUIElement;
|
/**
|
||||||
sprite = this.canvasNodeFactory.addSprite;
|
* Adds an instance of a UIElement to the current scene - i.e. any class that extends UIElement
|
||||||
animatedSprite = this.canvasNodeFactory.addAnimatedSprite;
|
* @param type The type of UIElement to add
|
||||||
graphic = this.canvasNodeFactory.addGraphic;
|
* @param layerName The layer to add the UIElement to
|
||||||
tilemap = this.tilemapFactory.add;
|
* @param options Any additional arguments to feed to the constructor
|
||||||
|
* @returns A new UIElement
|
||||||
|
*/
|
||||||
|
uiElement(type: string | UIElementType, layerName: string, options?: Record<string, any>): UIElement {
|
||||||
|
return this.canvasNodeFactory.addUIElement(type, layerName, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a sprite to the current scene
|
||||||
|
* @param key The key of the image the sprite will represent
|
||||||
|
* @param layerName The layer on which to add the sprite
|
||||||
|
* @returns A new Sprite
|
||||||
|
*/
|
||||||
|
sprite(key: string, layerName: string): Sprite {
|
||||||
|
return this.canvasNodeFactory.addSprite(key, layerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an AnimatedSprite to the current scene
|
||||||
|
* @param key The key of the image the sprite will represent
|
||||||
|
* @param layerName The layer on which to add the sprite
|
||||||
|
* @returns A new AnimatedSprite
|
||||||
|
*/
|
||||||
|
animatedSprite(key: string, layerName: string): AnimatedSprite {
|
||||||
|
return this.canvasNodeFactory.addAnimatedSprite(key, layerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new graphic element to the current Scene
|
||||||
|
* @param type The type of graphic to add
|
||||||
|
* @param layerName The layer on which to add the graphic
|
||||||
|
* @param options Any additional arguments to send to the graphic constructor
|
||||||
|
* @returns A new Graphic
|
||||||
|
*/
|
||||||
|
graphic(type: GraphicType | string, layerName: string, options?: Record<string, any>): Graphic {
|
||||||
|
return this.canvasNodeFactory.addGraphic(type, layerName, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a tilemap to the scene
|
||||||
|
* @param key The key of the loaded tilemap to load
|
||||||
|
* @param constr The constructor of the desired tilemap
|
||||||
|
* @param args Additional arguments to send to the tilemap constructor
|
||||||
|
* @returns An array of Layers, each of which contains a layer of the tilemap as its own Tilemap instance.
|
||||||
|
*/
|
||||||
|
tilemap(key: string, scale?: Vec2): Array<Layer> {
|
||||||
|
return this.tilemapFactory.add(key, scale);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,12 @@ import Sprite from "../../Nodes/Sprites/Sprite";
|
||||||
import PositionGraph from "../../DataTypes/Graphs/PositionGraph";
|
import PositionGraph from "../../DataTypes/Graphs/PositionGraph";
|
||||||
import Navmesh from "../../Pathfinding/Navmesh";
|
import Navmesh from "../../Pathfinding/Navmesh";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory that abstracts adding @reference[Tilemap]s to the @reference[Scene].
|
||||||
|
* Access methods in this factory through Scene.add.[methodName]().
|
||||||
|
*/
|
||||||
export default class TilemapFactory {
|
export default class TilemapFactory {
|
||||||
private scene: Scene;
|
private scene: Scene;
|
||||||
private tilemaps: Array<Tilemap>;
|
private tilemaps: Array<Tilemap>;
|
||||||
|
@ -30,6 +36,7 @@ export default class TilemapFactory {
|
||||||
* @param key The key of the loaded tilemap to load
|
* @param key The key of the loaded tilemap to load
|
||||||
* @param constr The constructor of the desired tilemap
|
* @param constr The constructor of the desired tilemap
|
||||||
* @param args Additional arguments to send to the tilemap constructor
|
* @param args Additional arguments to send to the tilemap constructor
|
||||||
|
* @returns An array of Layers, each of which contains a layer of the tilemap as its own Tilemap instance.
|
||||||
*/
|
*/
|
||||||
add = (key: string, scale: Vec2 = new Vec2(1, 1)): Array<Layer> => {
|
add = (key: string, scale: Vec2 = new Vec2(1, 1)): Array<Layer> => {
|
||||||
// Get Tilemap Data
|
// Get Tilemap Data
|
||||||
|
|
|
@ -4,7 +4,7 @@ import GameNode from "../Nodes/GameNode";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A layer in the scene. Has its own alpha value and parallax.
|
* A layer in the scene. Layers are used for sorting @reference[GameNode]s by depth.
|
||||||
*/
|
*/
|
||||||
export default class Layer {
|
export default class Layer {
|
||||||
/** The scene this layer belongs to */
|
/** The scene this layer belongs to */
|
||||||
|
@ -31,6 +31,11 @@ export default class Layer {
|
||||||
/** The depth of this layer compared to other layers */
|
/** The depth of this layer compared to other layers */
|
||||||
protected depth: number;
|
protected depth: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new layer. To do this in a game, use the addLayer() method in @refrence[Scene]
|
||||||
|
* @param scene The scene to add the layer to
|
||||||
|
* @param name The name of the layer
|
||||||
|
*/
|
||||||
constructor(scene: Scene, name: string){
|
constructor(scene: Scene, name: string){
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -42,30 +47,57 @@ export default class Layer {
|
||||||
this.depth = 0;
|
this.depth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives the name of the layer
|
||||||
|
* @returns The name of the layer
|
||||||
|
*/
|
||||||
getName(): string {
|
getName(): string {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses/Unpauses the layer. Affects all elements in this layer
|
||||||
|
* @param pauseValue True if the layer should be paused, false if not
|
||||||
|
*/
|
||||||
setPaused(pauseValue: boolean): void {
|
setPaused(pauseValue: boolean): void {
|
||||||
this.paused = pauseValue;
|
this.paused = pauseValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the layer is paused
|
||||||
|
*/
|
||||||
isPaused(): boolean {
|
isPaused(): boolean {
|
||||||
return this.paused;
|
return this.paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the opacity of the layer
|
||||||
|
* @param alpha The new opacity value in the range [0, 1]
|
||||||
|
*/
|
||||||
setAlpha(alpha: number): void {
|
setAlpha(alpha: number): void {
|
||||||
this.alpha = MathUtils.clamp(alpha, 0, 1);
|
this.alpha = MathUtils.clamp(alpha, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the opacity of the layer
|
||||||
|
* @returns The opacity
|
||||||
|
*/
|
||||||
getAlpha(): number {
|
getAlpha(): number {
|
||||||
return this.alpha;
|
return this.alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the layer's hidden value. If hidden, a layer will not be rendered, but will still update
|
||||||
|
* @param hidden The hidden value of the layer
|
||||||
|
*/
|
||||||
setHidden(hidden: boolean): void {
|
setHidden(hidden: boolean): void {
|
||||||
this.hidden = hidden;
|
this.hidden = hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideen value of the lyaer
|
||||||
|
* @returns True if the scene is hidden, false otherwise
|
||||||
|
*/
|
||||||
isHidden(): boolean {
|
isHidden(): boolean {
|
||||||
return this.hidden;
|
return this.hidden;
|
||||||
}
|
}
|
||||||
|
@ -82,27 +114,55 @@ export default class Layer {
|
||||||
this.hidden = false;
|
this.hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not the scene will ySort automatically.
|
||||||
|
* ySorting means that CanvasNodes on this layer will have their depth sorted depending on their y-value.
|
||||||
|
* This means that if an object is "higher" in the scene, it will sort behind objects that are "lower".
|
||||||
|
* This is useful for 3/4 view games, or similar situations, where you sometimes want to be in front of objects,
|
||||||
|
* and other times want to be behind the same objects.
|
||||||
|
* @param ySort True if ySorting should be active, false if not
|
||||||
|
*/
|
||||||
setYSort(ySort: boolean): void {
|
setYSort(ySort: boolean): void {
|
||||||
this.ySort = ySort;
|
this.ySort = ySort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ySort status of the scene
|
||||||
|
* @returns True if ySorting is occurring, false otherwise
|
||||||
|
*/
|
||||||
getYSort(): boolean {
|
getYSort(): boolean {
|
||||||
return this.ySort;
|
return this.ySort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the depth of the layer compared to other layers. A larger number means the layer will be closer to the screen.
|
||||||
|
* @param depth The depth of the layer.
|
||||||
|
*/
|
||||||
setDepth(depth: number): void {
|
setDepth(depth: number): void {
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the depth of the layer.
|
||||||
|
* @returns The depth
|
||||||
|
*/
|
||||||
getDepth(): number {
|
getDepth(): number {
|
||||||
return this.depth;
|
return this.depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a node to this layer
|
||||||
|
* @param node The node to add to this layer.
|
||||||
|
*/
|
||||||
addNode(node: GameNode): void {
|
addNode(node: GameNode): void {
|
||||||
this.items.push(node);
|
this.items.push(node);
|
||||||
node.setLayer(this);
|
node.setLayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreives all GameNodes from this layer
|
||||||
|
* @returns an Array that contains all of the GameNodes in this layer.
|
||||||
|
*/
|
||||||
getItems(): Array<GameNode> {
|
getItems(): Array<GameNode> {
|
||||||
return this.items;
|
return this.items;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,20 @@ import Layer from "../Layer";
|
||||||
import Vec2 from "../../DataTypes/Vec2";
|
import Vec2 from "../../DataTypes/Vec2";
|
||||||
import Scene from "../Scene";
|
import Scene from "../Scene";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of a Layer that has a parallax value.
|
||||||
|
*/
|
||||||
export default class ParallaxLayer extends Layer {
|
export default class ParallaxLayer extends Layer {
|
||||||
|
/** The value of the parallax of the Layer */
|
||||||
parallax: Vec2;
|
parallax: Vec2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ParallaxLayer.
|
||||||
|
* Use addParallaxLayer() in @reference[Scene] to add a layer of this type to your game.
|
||||||
|
* @param scene The Scene to add this ParallaxLayer to
|
||||||
|
* @param name The name of the ParallaxLayer
|
||||||
|
* @param parallax The parallax level
|
||||||
|
*/
|
||||||
constructor(scene: Scene, name: string, parallax: Vec2){
|
constructor(scene: Scene, name: string, parallax: Vec2){
|
||||||
super(scene, name);
|
super(scene, name);
|
||||||
this.parallax = parallax;
|
this.parallax = parallax;
|
||||||
|
|
|
@ -2,7 +2,18 @@ import Vec2 from "../../DataTypes/Vec2";
|
||||||
import Scene from "../Scene";
|
import Scene from "../Scene";
|
||||||
import ParallaxLayer from "./ParallaxLayer";
|
import ParallaxLayer from "./ParallaxLayer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Layer strictly to be used for managing UIElements.
|
||||||
|
* This is intended to be a Layer that always stays in the same place,
|
||||||
|
* and thus renders things like a HUD or an inventory without taking into consideration the \reference[Viewport] scroll.
|
||||||
|
*/
|
||||||
export default class UILayer extends ParallaxLayer {
|
export default class UILayer extends ParallaxLayer {
|
||||||
|
/**
|
||||||
|
* Creates a new UILayer.
|
||||||
|
* Use addUILayer() in @reference[Scene] to add a layer of this type to your game.
|
||||||
|
* @param scene The Scene to add this UILayer to
|
||||||
|
* @param name The name of the UILayer
|
||||||
|
*/
|
||||||
constructor(scene: Scene, name: string){
|
constructor(scene: Scene, name: string){
|
||||||
super(scene, name, Vec2.ZERO);
|
super(scene, name, Vec2.ZERO);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,16 @@ import ParallaxLayer from "./Layers/ParallaxLayer";
|
||||||
import UILayer from "./Layers/UILayer";
|
import UILayer from "./Layers/UILayer";
|
||||||
import CanvasNode from "../Nodes/CanvasNode";
|
import CanvasNode from "../Nodes/CanvasNode";
|
||||||
import GameNode from "../Nodes/GameNode";
|
import GameNode from "../Nodes/GameNode";
|
||||||
import ArrayUtils from "../Utils/ArrayUtils";
|
import SceneOptions from "./SceneOptions";
|
||||||
import RenderingManager from "../Rendering/RenderingManager";
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
import Debug from "../Debug/Debug";
|
import Debug from "../Debug/Debug";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenes are the main container in the game engine.
|
||||||
|
* Your main scene is the current level or menu of the game, and will contain all of the GameNodes needed.
|
||||||
|
* Scenes provide an easy way to load assets, add assets to the game world, and unload assets,
|
||||||
|
* and have lifecycle methods exposed for these functions.
|
||||||
|
*/
|
||||||
export default class Scene implements Updateable {
|
export default class Scene implements Updateable {
|
||||||
/** The size of the game world. */
|
/** The size of the game world. */
|
||||||
protected worldSize: Vec2;
|
protected worldSize: Vec2;
|
||||||
|
@ -82,6 +88,14 @@ export default class Scene implements Updateable {
|
||||||
/** The configuration options for this scene */
|
/** The configuration options for this scene */
|
||||||
public sceneOptions: SceneOptions;
|
public sceneOptions: SceneOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Scene. To add a new Scene in your game, use addScene() in @reference[SceneManager]
|
||||||
|
* @param viewport The viewport of the game
|
||||||
|
* @param sceneManager The SceneManager that owns this Scene
|
||||||
|
* @param renderingManager The RenderingManager that will handle this Scene's rendering
|
||||||
|
* @param game The instance of the GameLoop
|
||||||
|
* @param options The options for Scene initialization
|
||||||
|
*/
|
||||||
constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, game: GameLoop, options: Record<string, any>){
|
constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, game: GameLoop, options: Record<string, any>){
|
||||||
this.sceneOptions = SceneOptions.parse(options);
|
this.sceneOptions = SceneOptions.parse(options);
|
||||||
|
|
||||||
|
@ -122,7 +136,7 @@ export default class Scene implements Updateable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A lifecycle method called every frame of the game. This is where you can dynamically do things like add in new enemies
|
* A lifecycle method called every frame of the game. This is where you can dynamically do things like add in new enemies
|
||||||
* @param delta
|
* @param delta The time this frame represents
|
||||||
*/
|
*/
|
||||||
updateScene(deltaT: number): void {}
|
updateScene(deltaT: number): void {}
|
||||||
|
|
||||||
|
@ -149,6 +163,9 @@ export default class Scene implements Updateable {
|
||||||
this.viewport.update(deltaT);
|
this.viewport.update(deltaT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects renderable sets and coordinates with the RenderingManager to draw the Scene
|
||||||
|
*/
|
||||||
render(): void {
|
render(): void {
|
||||||
// Get the visible set of nodes
|
// Get the visible set of nodes
|
||||||
let visibleSet = this.sceneGraph.getVisibleSet();
|
let visibleSet = this.sceneGraph.getVisibleSet();
|
||||||
|
@ -171,10 +188,18 @@ export default class Scene implements Updateable {
|
||||||
Debug.setNodes(nodes);
|
Debug.setNodes(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scene as running or not
|
||||||
|
* @param running True if the Scene should be running, false if not
|
||||||
|
*/
|
||||||
setRunning(running: boolean): void {
|
setRunning(running: boolean): void {
|
||||||
this.running = running;
|
this.running = running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the Scene is running
|
||||||
|
* @returns True if the scene is running, false otherwise
|
||||||
|
*/
|
||||||
isRunning(): boolean {
|
isRunning(): boolean {
|
||||||
return this.running;
|
return this.running;
|
||||||
}
|
}
|
||||||
|
@ -183,6 +208,7 @@ export default class Scene implements Updateable {
|
||||||
* Adds a new layer to the scene and returns it
|
* Adds a new layer to the scene and returns it
|
||||||
* @param name The name of the new layer
|
* @param name The name of the new layer
|
||||||
* @param depth The depth of the layer
|
* @param depth The depth of the layer
|
||||||
|
* @returns The newly created Layer
|
||||||
*/
|
*/
|
||||||
addLayer(name: string, depth?: number): Layer {
|
addLayer(name: string, depth?: number): Layer {
|
||||||
if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){
|
if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){
|
||||||
|
@ -205,6 +231,7 @@ export default class Scene implements Updateable {
|
||||||
* @param name The name of the parallax layer
|
* @param name The name of the parallax layer
|
||||||
* @param parallax The parallax level
|
* @param parallax The parallax level
|
||||||
* @param depth The depth of the layer
|
* @param depth The depth of the layer
|
||||||
|
* @returns The newly created ParallaxLayer
|
||||||
*/
|
*/
|
||||||
addParallaxLayer(name: string, parallax: Vec2, depth?: number): ParallaxLayer {
|
addParallaxLayer(name: string, parallax: Vec2, depth?: number): ParallaxLayer {
|
||||||
if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){
|
if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){
|
||||||
|
@ -225,6 +252,7 @@ export default class Scene implements Updateable {
|
||||||
/**
|
/**
|
||||||
* Adds a new UILayer to the scene
|
* Adds a new UILayer to the scene
|
||||||
* @param name The name of the new UIlayer
|
* @param name The name of the new UIlayer
|
||||||
|
* @returns The newly created UILayer
|
||||||
*/
|
*/
|
||||||
addUILayer(name: string): UILayer {
|
addUILayer(name: string): UILayer {
|
||||||
if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){
|
if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){
|
||||||
|
@ -239,8 +267,10 @@ export default class Scene implements Updateable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a layer from the scene by name if it exists
|
* Gets a layer from the scene by name if it exists.
|
||||||
|
* This can be a Layer or any of its subclasses
|
||||||
* @param name The name of the layer
|
* @param name The name of the layer
|
||||||
|
* @returns The Layer found with that name
|
||||||
*/
|
*/
|
||||||
getLayer(name: string): Layer {
|
getLayer(name: string): Layer {
|
||||||
if(this.layers.has(name)){
|
if(this.layers.has(name)){
|
||||||
|
@ -256,7 +286,8 @@ export default class Scene implements Updateable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this layer is a ParallaxLayer
|
* Returns true if this layer is a ParallaxLayer
|
||||||
* @param name
|
* @param name The name of the layer
|
||||||
|
* @returns True if this layer is a ParallaxLayer
|
||||||
*/
|
*/
|
||||||
isParallaxLayer(name: string): boolean {
|
isParallaxLayer(name: string): boolean {
|
||||||
return this.parallaxLayers.has(name);
|
return this.parallaxLayers.has(name);
|
||||||
|
@ -264,15 +295,18 @@ export default class Scene implements Updateable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this layer is a UILayer
|
* Returns true if this layer is a UILayer
|
||||||
* @param name
|
* @param name The name of the layer
|
||||||
|
* @returns True if this layer is ParallaxLayer
|
||||||
*/
|
*/
|
||||||
isUILayer(name: string): boolean {
|
isUILayer(name: string): boolean {
|
||||||
return this.uiLayers.has(name);
|
return this.uiLayers.has(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the translation of this node with respect to camera space (due to the viewport moving);
|
* Returns the translation of this node with respect to camera space (due to the viewport moving).
|
||||||
* @param node
|
* This value is affected by the parallax level of the @reference[Layer] the node is on.
|
||||||
|
* @param node The node to check the viewport with respect to
|
||||||
|
* @returns A Vec2 containing the translation of viewport with respect to this node.
|
||||||
*/
|
*/
|
||||||
getViewTranslation(node: GameNode): Vec2 {
|
getViewTranslation(node: GameNode): Vec2 {
|
||||||
let layer = node.getLayer();
|
let layer = node.getLayer();
|
||||||
|
@ -284,40 +318,75 @@ export default class Scene implements Updateable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the scale level of the view */
|
/**
|
||||||
|
* Returns the scale level of the view
|
||||||
|
* @returns The zoom level of the viewport
|
||||||
|
*/
|
||||||
getViewScale(): number {
|
getViewScale(): number {
|
||||||
return this.viewport.getZoomLevel();
|
return this.viewport.getZoomLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the viewport associated with this scene */
|
/**
|
||||||
|
* Returns the Viewport associated with this scene
|
||||||
|
* @returns The current Viewport
|
||||||
|
*/
|
||||||
getViewport(): Viewport {
|
getViewport(): Viewport {
|
||||||
return this.viewport;
|
return this.viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the world size of this Scene
|
||||||
|
* @returns The world size in a Vec2
|
||||||
|
*/
|
||||||
getWorldSize(): Vec2 {
|
getWorldSize(): Vec2 {
|
||||||
return this.worldSize;
|
return this.worldSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the SceneGraph associated with this Scene
|
||||||
|
* @returns The SceneGraph
|
||||||
|
*/
|
||||||
getSceneGraph(): SceneGraph {
|
getSceneGraph(): SceneGraph {
|
||||||
return this.sceneGraph;
|
return this.sceneGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the PhysicsManager associated with this Scene
|
||||||
|
* @returns The PhysicsManager
|
||||||
|
*/
|
||||||
getPhysicsManager(): PhysicsManager {
|
getPhysicsManager(): PhysicsManager {
|
||||||
return this.physicsManager;
|
return this.physicsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the NavigationManager associated with this Scene
|
||||||
|
* @returns The NavigationManager
|
||||||
|
*/
|
||||||
getNavigationManager(): NavigationManager {
|
getNavigationManager(): NavigationManager {
|
||||||
return this.navManager;
|
return this.navManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the AIManager associated with this Scene
|
||||||
|
* @returns The AIManager
|
||||||
|
*/
|
||||||
getAIManager(): AIManager {
|
getAIManager(): AIManager {
|
||||||
return this.aiManager;
|
return this.aiManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an ID for a GameNode
|
||||||
|
* @returns The new ID
|
||||||
|
*/
|
||||||
generateId(): number {
|
generateId(): number {
|
||||||
return this.sceneManager.generateId();
|
return this.sceneManager.generateId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a Tilemap in this Scene
|
||||||
|
* @param name The name of the Tilemap
|
||||||
|
* @returns The Tilemap, if one this name exists, otherwise null
|
||||||
|
*/
|
||||||
getTilemap(name: string): Tilemap {
|
getTilemap(name: string): Tilemap {
|
||||||
for(let tilemap of this .tilemaps){
|
for(let tilemap of this .tilemaps){
|
||||||
if(tilemap.name === name){
|
if(tilemap.name === name){
|
||||||
|
@ -328,29 +397,3 @@ export default class Scene implements Updateable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SceneOptions {
|
|
||||||
physics: {
|
|
||||||
numPhysicsLayers: number,
|
|
||||||
physicsLayerNames: Array<string>,
|
|
||||||
physicsLayerCollisions: Array<Array<number>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
static parse(options: Record<string, any>): SceneOptions{
|
|
||||||
let sOpt = new SceneOptions();
|
|
||||||
|
|
||||||
sOpt.physics = {
|
|
||||||
numPhysicsLayers: 10,
|
|
||||||
physicsLayerNames: null,
|
|
||||||
physicsLayerCollisions: ArrayUtils.ones2d(10, 10)
|
|
||||||
};
|
|
||||||
|
|
||||||
if(options.physics){
|
|
||||||
if(options.physics.numPhysicsLayers) sOpt.physics.numPhysicsLayers = options.physics.numPhysicsLayers;
|
|
||||||
if(options.physics.physicsLayerNames) sOpt.physics.physicsLayerNames = options.physics.physicsLayerNames;
|
|
||||||
if(options.physics.physicsLayerCollisions) sOpt.physics.physicsLayerCollisions = options.physics.physicsLayerCollisions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sOpt;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,14 +4,31 @@ import Viewport from "../SceneGraph/Viewport";
|
||||||
import GameLoop from "../Loop/GameLoop";
|
import GameLoop from "../Loop/GameLoop";
|
||||||
import RenderingManager from "../Rendering/RenderingManager";
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SceneManager of the game engine. There is only one of theses.
|
||||||
|
* The SceneManager acts as an interface to create Scenes, and handles the lifecycle methods of Scenes.
|
||||||
|
* The Scene manager keeps track of systems that are constant across scene, such as the @reference[ResourceManager]
|
||||||
|
*/
|
||||||
export default class SceneManager {
|
export default class SceneManager {
|
||||||
|
/** The current Scene of the game */
|
||||||
protected currentScene: Scene;
|
protected currentScene: Scene;
|
||||||
|
/** The Viewport of the game */
|
||||||
protected viewport: Viewport;
|
protected viewport: Viewport;
|
||||||
|
/** A reference to the ResourceManager */
|
||||||
protected resourceManager: ResourceManager;
|
protected resourceManager: ResourceManager;
|
||||||
|
/** The GameLoop this SceneManager belongs to */
|
||||||
protected game: GameLoop;
|
protected game: GameLoop;
|
||||||
|
/** A counter to keep track of game ids */
|
||||||
protected idCounter: number;
|
protected idCounter: number;
|
||||||
|
/** The RenderingManager of the game */
|
||||||
protected renderingManager: RenderingManager;
|
protected renderingManager: RenderingManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SceneManager
|
||||||
|
* @param viewport The Viewport of the game
|
||||||
|
* @param game The GameLoop instance
|
||||||
|
* @param renderingManager The RenderingManager of the game
|
||||||
|
*/
|
||||||
constructor(viewport: Viewport, game: GameLoop, renderingManager: RenderingManager){
|
constructor(viewport: Viewport, game: GameLoop, renderingManager: RenderingManager){
|
||||||
this.resourceManager = ResourceManager.getInstance();
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
|
@ -21,7 +38,8 @@ export default class SceneManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a scene as the main scene
|
* Add a scene as the main scene.
|
||||||
|
* Use this method if you've created a subclass of Scene, and you want to add it as the main Scene.
|
||||||
* @param constr The constructor of the scene to add
|
* @param constr The constructor of the scene to add
|
||||||
*/
|
*/
|
||||||
public addScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
public addScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
||||||
|
@ -43,7 +61,8 @@ export default class SceneManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change from the current scene to this new scene
|
* Change from the current scene to this new scene.
|
||||||
|
* Use this method if you've created a subclass of Scene, and you want to add it as the main Scene.
|
||||||
* @param constr The constructor of the scene to change to
|
* @param constr The constructor of the scene to change to
|
||||||
*/
|
*/
|
||||||
public changeScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
public changeScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
||||||
|
@ -57,14 +76,25 @@ export default class SceneManager {
|
||||||
this.addScene(constr, options);
|
this.addScene(constr, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a unique ID
|
||||||
|
* @returns A new ID
|
||||||
|
*/
|
||||||
public generateId(): number {
|
public generateId(): number {
|
||||||
return this.idCounter++;
|
return this.idCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(){
|
/**
|
||||||
|
* Renders the current Scene
|
||||||
|
*/
|
||||||
|
public render(): void {
|
||||||
this.currentScene.render();
|
this.currentScene.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current Scene
|
||||||
|
* @param deltaT The timestep of the Scene
|
||||||
|
*/
|
||||||
public update(deltaT: number){
|
public update(deltaT: number){
|
||||||
if(this.currentScene.isRunning()){
|
if(this.currentScene.isRunning()){
|
||||||
this.currentScene.update(deltaT);
|
this.currentScene.update(deltaT);
|
||||||
|
|
32
src/Scene/SceneOptions.ts
Normal file
32
src/Scene/SceneOptions.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import ArrayUtils from "../Utils/ArrayUtils";
|
||||||
|
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options to give a @reference[Scene] for initialization
|
||||||
|
*/
|
||||||
|
export default class SceneOptions {
|
||||||
|
physics: {
|
||||||
|
numPhysicsLayers: number,
|
||||||
|
physicsLayerNames: Array<string>,
|
||||||
|
physicsLayerCollisions: Array<Array<number>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parse(options: Record<string, any>): SceneOptions{
|
||||||
|
let sOpt = new SceneOptions();
|
||||||
|
|
||||||
|
sOpt.physics = {
|
||||||
|
numPhysicsLayers: 10,
|
||||||
|
physicsLayerNames: null,
|
||||||
|
physicsLayerCollisions: ArrayUtils.ones2d(10, 10)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(options.physics){
|
||||||
|
if(options.physics.numPhysicsLayers) sOpt.physics.numPhysicsLayers = options.physics.numPhysicsLayers;
|
||||||
|
if(options.physics.physicsLayerNames) sOpt.physics.physicsLayerNames = options.physics.physicsLayerNames;
|
||||||
|
if(options.physics.physicsLayerCollisions) sOpt.physics.physicsLayerCollisions = options.physics.physicsLayerCollisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sOpt;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,14 +6,25 @@ import Scene from "../Scene/Scene";
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract interface of a SceneGraph. Exposes methods for use by other code, but leaves the implementation up to the subclasses.
|
* An abstract interface of a SceneGraph.
|
||||||
|
* Exposes methods for use by other code, but leaves the implementation up to the subclasses.
|
||||||
|
* The SceneGraph manages the positions of all GameNodes, and can easily prune a visible set for rendering.
|
||||||
*/
|
*/
|
||||||
export default abstract class SceneGraph {
|
export default abstract class SceneGraph {
|
||||||
|
/** A reference to the viewport */
|
||||||
protected viewport: Viewport;
|
protected viewport: Viewport;
|
||||||
|
/** A map of CanvasNodes in this SceneGraph */
|
||||||
protected nodeMap: Map<CanvasNode>;
|
protected nodeMap: Map<CanvasNode>;
|
||||||
|
/** A counter of IDs for nodes in this SceneGraph */
|
||||||
protected idCounter: number;
|
protected idCounter: number;
|
||||||
|
/** A reference to the Scene this SceneGraph belongs to */
|
||||||
protected scene: Scene;
|
protected scene: Scene;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SceneGraph
|
||||||
|
* @param viewport The viewport
|
||||||
|
* @param scene The Scene this SceneGraph belongs to
|
||||||
|
*/
|
||||||
constructor(viewport: Viewport, scene: Scene){
|
constructor(viewport: Viewport, scene: Scene){
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
|
@ -24,6 +35,7 @@ export default abstract class SceneGraph {
|
||||||
/**
|
/**
|
||||||
* Add a node to the SceneGraph
|
* Add a node to the SceneGraph
|
||||||
* @param node The CanvasNode to add to the SceneGraph
|
* @param node The CanvasNode to add to the SceneGraph
|
||||||
|
* @returns The SceneGraph ID of this newly added CanvasNode
|
||||||
*/
|
*/
|
||||||
addNode(node: CanvasNode): number {
|
addNode(node: CanvasNode): number {
|
||||||
this.nodeMap.add(this.idCounter.toString(), node);
|
this.nodeMap.add(this.idCounter.toString(), node);
|
||||||
|
@ -63,15 +75,17 @@ export default abstract class SceneGraph {
|
||||||
/**
|
/**
|
||||||
* Get a specific node using its id
|
* Get a specific node using its id
|
||||||
* @param id The id of the CanvasNode to retrieve
|
* @param id The id of the CanvasNode to retrieve
|
||||||
|
* @returns The node with this ID
|
||||||
*/
|
*/
|
||||||
getNode(id: string): CanvasNode {
|
getNode(id: string): CanvasNode {
|
||||||
return this.nodeMap.get(id);
|
return this.nodeMap.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the node at specific coordinates
|
* Returns the nodes at specific coordinates
|
||||||
* @param vecOrX
|
* @param vecOrX The x-coordinate of the position, or the coordinates in a Vec2
|
||||||
* @param y
|
* @param y The y-coordinate of the position
|
||||||
|
* @returns An array of nodes found at the position provided
|
||||||
*/
|
*/
|
||||||
getNodesAt(vecOrX: Vec2 | number, y: number = null): Array<CanvasNode> {
|
getNodesAt(vecOrX: Vec2 | number, y: number = null): Array<CanvasNode> {
|
||||||
if(vecOrX instanceof Vec2){
|
if(vecOrX instanceof Vec2){
|
||||||
|
@ -81,8 +95,17 @@ export default abstract class SceneGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nodes that overlap a specific boundary
|
||||||
|
* @param boundary The region to check
|
||||||
|
* @returns An array of nodes found overlapping the provided boundary
|
||||||
|
*/
|
||||||
abstract getNodesInRegion(boundary: AABB): Array<CanvasNode>;
|
abstract getNodesInRegion(boundary: AABB): Array<CanvasNode>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all nodes in the SceneGraph
|
||||||
|
* @returns An Array containing all nodes in the SceneGraph
|
||||||
|
*/
|
||||||
getAllNodes(): Array<CanvasNode> {
|
getAllNodes(): Array<CanvasNode> {
|
||||||
let arr = new Array<CanvasNode>();
|
let arr = new Array<CanvasNode>();
|
||||||
this.nodeMap.forEach(key => arr.push(this.nodeMap.get(key)));
|
this.nodeMap.forEach(key => arr.push(this.nodeMap.get(key)));
|
||||||
|
@ -91,8 +114,8 @@ export default abstract class SceneGraph {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The specific implementation of getting a node at certain coordinates
|
* The specific implementation of getting a node at certain coordinates
|
||||||
* @param x
|
* @param x The x-coordinates of the node
|
||||||
* @param y
|
* @param y The y-coordinates of the node
|
||||||
*/
|
*/
|
||||||
protected abstract getNodesAtCoords(x: number, y: number): Array<CanvasNode>;
|
protected abstract getNodesAtCoords(x: number, y: number): Array<CanvasNode>;
|
||||||
|
|
||||||
|
@ -101,7 +124,8 @@ export default abstract class SceneGraph {
|
||||||
abstract render(ctx: CanvasRenderingContext2D): void;
|
abstract render(ctx: CanvasRenderingContext2D): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the visible set of CanvasNodes based on the viewport
|
* Gets the visible set of CanvasNodes based on the @reference[Viewport]
|
||||||
|
* @returns An array containing all visible nodes in the SceneGraph
|
||||||
*/
|
*/
|
||||||
abstract getVisibleSet(): Array<CanvasNode>;
|
abstract getVisibleSet(): Array<CanvasNode>;
|
||||||
}
|
}
|
|
@ -2,37 +2,41 @@ import SceneGraph from "./SceneGraph";
|
||||||
import CanvasNode from "../Nodes/CanvasNode";
|
import CanvasNode from "../Nodes/CanvasNode";
|
||||||
import Viewport from "./Viewport";
|
import Viewport from "./Viewport";
|
||||||
import Scene from "../Scene/Scene";
|
import Scene from "../Scene/Scene";
|
||||||
import Stack from "../DataTypes/Stack";
|
|
||||||
import Layer from "../Scene/Layer"
|
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import Stats from "../Debug/Stats";
|
import Stats from "../Debug/Stats";
|
||||||
|
|
||||||
export default class SceneGraphArray extends SceneGraph{
|
/**
|
||||||
private nodeList: Array<CanvasNode>;
|
* An implementation of a SceneGraph that simply stored CanvasNodes in an array.
|
||||||
private turnOffViewportCulling_demoTool: boolean;
|
*/
|
||||||
|
export default class SceneGraphArray extends SceneGraph {
|
||||||
|
/** The list of CanvasNodes in this SceneGraph */
|
||||||
|
private nodeList: Array<CanvasNode>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SceneGraphArray
|
||||||
|
* @param viewport The Viewport
|
||||||
|
* @param scene The Scene this SceneGraph belongs to
|
||||||
|
*/
|
||||||
constructor(viewport: Viewport, scene: Scene){
|
constructor(viewport: Viewport, scene: Scene){
|
||||||
super(viewport, scene);
|
super(viewport, scene);
|
||||||
|
|
||||||
this.nodeList = new Array<CanvasNode>();
|
this.nodeList = new Array<CanvasNode>();
|
||||||
this.turnOffViewportCulling_demoTool = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setViewportCulling_demoTool(bool: boolean): void {
|
// @override
|
||||||
this.turnOffViewportCulling_demoTool = bool;
|
protected addNodeSpecific(node: CanvasNode, id: string): void {
|
||||||
}
|
|
||||||
|
|
||||||
addNodeSpecific(node: CanvasNode, id: string): void {
|
|
||||||
this.nodeList.push(node);
|
this.nodeList.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeNodeSpecific(node: CanvasNode, id: string): void {
|
// @override
|
||||||
|
protected removeNodeSpecific(node: CanvasNode, id: string): void {
|
||||||
let index = this.nodeList.indexOf(node);
|
let index = this.nodeList.indexOf(node);
|
||||||
if(index > -1){
|
if(index > -1){
|
||||||
this.nodeList.splice(index, 1);
|
this.nodeList.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getNodesAtCoords(x: number, y: number): Array<CanvasNode> {
|
getNodesAtCoords(x: number, y: number): Array<CanvasNode> {
|
||||||
let results = [];
|
let results = [];
|
||||||
|
|
||||||
|
@ -45,6 +49,7 @@ export default class SceneGraphArray extends SceneGraph{
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getNodesInRegion(boundary: AABB): Array<CanvasNode> {
|
getNodesInRegion(boundary: AABB): Array<CanvasNode> {
|
||||||
let t0 = performance.now();
|
let t0 = performance.now();
|
||||||
let results = [];
|
let results = [];
|
||||||
|
@ -73,16 +78,8 @@ export default class SceneGraphArray extends SceneGraph{
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {}
|
render(ctx: CanvasRenderingContext2D): void {}
|
||||||
|
|
||||||
|
// @override
|
||||||
getVisibleSet(): Array<CanvasNode> {
|
getVisibleSet(): Array<CanvasNode> {
|
||||||
// If viewport culling is turned off for demonstration
|
|
||||||
if(this.turnOffViewportCulling_demoTool){
|
|
||||||
let visibleSet = new Array<CanvasNode>();
|
|
||||||
for(let node of this.nodeList){
|
|
||||||
visibleSet.push(node);
|
|
||||||
}
|
|
||||||
return visibleSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
let visibleSet = new Array<CanvasNode>();
|
let visibleSet = new Array<CanvasNode>();
|
||||||
|
|
||||||
for(let node of this.nodeList){
|
for(let node of this.nodeList){
|
||||||
|
|
|
@ -7,10 +7,21 @@ import Vec2 from "../DataTypes/Vec2";
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import Stats from "../Debug/Stats";
|
import Stats from "../Debug/Stats";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a SceneGraph that uses a @reference[RegionQuadTree] to store @reference[CanvasNode]s.
|
||||||
|
*/
|
||||||
export default class SceneGraphQuadTree extends SceneGraph {
|
export default class SceneGraphQuadTree extends SceneGraph {
|
||||||
|
/** The QuadTree used to store the CanvasNodes */
|
||||||
private qt: RegionQuadTree<CanvasNode>;
|
private qt: RegionQuadTree<CanvasNode>;
|
||||||
|
|
||||||
|
/** A list of nodes to help out the QuadTree */
|
||||||
private nodes: Array<CanvasNode>;
|
private nodes: Array<CanvasNode>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SceneGraphQuadTree
|
||||||
|
* @param viewport The Viewport
|
||||||
|
* @param scene The Scene this SceneGraph belongs to
|
||||||
|
*/
|
||||||
constructor(viewport: Viewport, scene: Scene){
|
constructor(viewport: Viewport, scene: Scene){
|
||||||
super(viewport, scene);
|
super(viewport, scene);
|
||||||
|
|
||||||
|
@ -19,21 +30,25 @@ export default class SceneGraphQuadTree extends SceneGraph {
|
||||||
this.nodes = new Array();
|
this.nodes = new Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
addNodeSpecific(node: CanvasNode, id: string): void {
|
// @override
|
||||||
|
protected addNodeSpecific(node: CanvasNode, id: string): void {
|
||||||
this.nodes.push(node);
|
this.nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeNodeSpecific(node: CanvasNode, id: string): void {
|
// @override
|
||||||
|
protected removeNodeSpecific(node: CanvasNode, id: string): void {
|
||||||
let index = this.nodes.indexOf(node);
|
let index = this.nodes.indexOf(node);
|
||||||
if(index >= 0){
|
if(index >= 0){
|
||||||
this.nodes.splice(index, 1);
|
this.nodes.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getNodesAtCoords(x: number, y: number): Array<CanvasNode> {
|
getNodesAtCoords(x: number, y: number): Array<CanvasNode> {
|
||||||
return this.qt.queryPoint(new Vec2(x, y));
|
return this.qt.queryPoint(new Vec2(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getNodesInRegion(boundary: AABB): Array<CanvasNode> {
|
getNodesInRegion(boundary: AABB): Array<CanvasNode> {
|
||||||
let t0 = performance.now();
|
let t0 = performance.now();
|
||||||
let res = this.qt.queryRegion(boundary);
|
let res = this.qt.queryRegion(boundary);
|
||||||
|
@ -72,6 +87,7 @@ export default class SceneGraphQuadTree extends SceneGraph {
|
||||||
this.qt.render_demo(ctx, origin, zoom);
|
this.qt.render_demo(ctx, origin, zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
getVisibleSet(): Array<CanvasNode> {
|
getVisibleSet(): Array<CanvasNode> {
|
||||||
let visibleSet = this.qt.queryRegion(this.viewport.getView());
|
let visibleSet = this.qt.queryRegion(this.viewport.getView());
|
||||||
|
|
||||||
|
|
|
@ -4,29 +4,37 @@ import CanvasNode from "../Nodes/CanvasNode";
|
||||||
import MathUtils from "../Utils/MathUtils";
|
import MathUtils from "../Utils/MathUtils";
|
||||||
import Queue from "../DataTypes/Queue";
|
import Queue from "../DataTypes/Queue";
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import Debug from "../Debug/Debug";
|
|
||||||
import InputReceiver from "../Input/InputReceiver";
|
import InputReceiver from "../Input/InputReceiver";
|
||||||
import ParallaxLayer from "../Scene/Layers/ParallaxLayer";
|
import ParallaxLayer from "../Scene/Layers/ParallaxLayer";
|
||||||
import UILayer from "../Scene/Layers/UILayer";
|
import UILayer from "../Scene/Layers/UILayer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The viewport of the game. Corresponds to the visible window displayed in the browser.
|
||||||
|
* The viewport keeps track of its position in the game world, and can act as a camera to follow objects.
|
||||||
|
*/
|
||||||
export default class Viewport {
|
export default class Viewport {
|
||||||
|
/** The AABB that contains the position and size of the viewport view */
|
||||||
private view: AABB;
|
private view: AABB;
|
||||||
private boundary: AABB;
|
/** The boundary for the viewport. This represents the limits to where the viewport can go */
|
||||||
|
private boundary: AABB;
|
||||||
|
/** The GameNode the Viewport is following */
|
||||||
private following: GameNode;
|
private following: GameNode;
|
||||||
|
/** The position the GameNode is focusing on. This is overridden if "following" is set. */
|
||||||
private focus: Vec2;
|
private focus: Vec2;
|
||||||
|
|
||||||
/**
|
/** A queue of previous positions of what this viewport is following. Used for smoothing viewport movement */
|
||||||
* A queue of previous positions of what this viewport is following. Used for smoothing viewport movement
|
|
||||||
*/
|
|
||||||
private lastPositions: Queue<Vec2>;
|
private lastPositions: Queue<Vec2>;
|
||||||
|
|
||||||
/**
|
/** The number of previous positions this viewport tracks */
|
||||||
* The number of previous positions this viewport tracks
|
|
||||||
*/
|
|
||||||
private smoothingFactor: number;
|
private smoothingFactor: number;
|
||||||
|
|
||||||
|
/** A boolean tha represents whether the player can zoom by scrolling with the mouse wheel */
|
||||||
private scrollZoomEnabled: boolean;
|
private scrollZoomEnabled: boolean;
|
||||||
|
|
||||||
|
/** The amount that is zoomed in or out. */
|
||||||
private ZOOM_FACTOR: number = 1.2;
|
private ZOOM_FACTOR: number = 1.2;
|
||||||
|
|
||||||
|
/** The size of the canvas */
|
||||||
private canvasSize: Vec2;
|
private canvasSize: Vec2;
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
|
@ -39,24 +47,30 @@ export default class Viewport {
|
||||||
this.focus = Vec2.ZERO;
|
this.focus = Vec2.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Enables the viewport to zoom in and out */
|
||||||
enableZoom(): void {
|
enableZoom(): void {
|
||||||
this.scrollZoomEnabled = true;
|
this.scrollZoomEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the position of the viewport as a Vec2
|
* Returns the position of the viewport
|
||||||
|
* @returns The center of the viewport as a Vec2
|
||||||
*/
|
*/
|
||||||
getCenter(): Vec2 {
|
getCenter(): Vec2 {
|
||||||
return this.view.center;
|
return this.view.center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new Vec2 with the origin of the viewport */
|
/**
|
||||||
|
* Returns a new Vec2 with the origin of the viewport
|
||||||
|
* @returns The top left cornder of the Vieport as a Vec2
|
||||||
|
*/
|
||||||
getOrigin(): Vec2 {
|
getOrigin(): Vec2 {
|
||||||
return new Vec2(this.view.left, this.view.top);
|
return new Vec2(this.view.left, this.view.top);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the region visible to this viewport
|
* Returns the region visible to this viewport
|
||||||
|
* @returns The AABB containing the region visible to the viewport
|
||||||
*/
|
*/
|
||||||
getView(): AABB {
|
getView(): AABB {
|
||||||
return this.view;
|
return this.view;
|
||||||
|
@ -64,8 +78,8 @@ export default class Viewport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the position of the viewport
|
* Set the position of the viewport
|
||||||
* @param vecOrX
|
* @param vecOrX The new position or the x-coordinate of the new position
|
||||||
* @param y
|
* @param y The y-coordinate of the new position
|
||||||
*/
|
*/
|
||||||
setCenter(vecOrX: Vec2 | number, y: number = null): void {
|
setCenter(vecOrX: Vec2 | number, y: number = null): void {
|
||||||
let pos: Vec2;
|
let pos: Vec2;
|
||||||
|
@ -81,6 +95,7 @@ export default class Viewport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of the viewport as a Vec2
|
* Returns the size of the viewport as a Vec2
|
||||||
|
* @returns The half-size of the viewport as a Vec2
|
||||||
*/
|
*/
|
||||||
getHalfSize(): Vec2 {
|
getHalfSize(): Vec2 {
|
||||||
return this.view.getHalfSize();
|
return this.view.getHalfSize();
|
||||||
|
@ -88,8 +103,8 @@ export default class Viewport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the size of the viewport
|
* Sets the size of the viewport
|
||||||
* @param vecOrX
|
* @param vecOrX The new width of the viewport or the new size as a Vec2
|
||||||
* @param y
|
* @param y The new height of the viewport
|
||||||
*/
|
*/
|
||||||
setSize(vecOrX: Vec2 | number, y: number = null): void {
|
setSize(vecOrX: Vec2 | number, y: number = null): void {
|
||||||
if(vecOrX instanceof Vec2){
|
if(vecOrX instanceof Vec2){
|
||||||
|
@ -99,6 +114,11 @@ export default class Viewport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the half-size of the viewport
|
||||||
|
* @param vecOrX The new half-width of the viewport or the new half-size as a Vec2
|
||||||
|
* @param y The new height of the viewport
|
||||||
|
*/
|
||||||
setHalfSize(vecOrX: Vec2 | number, y: number = null): void {
|
setHalfSize(vecOrX: Vec2 | number, y: number = null): void {
|
||||||
if(vecOrX instanceof Vec2){
|
if(vecOrX instanceof Vec2){
|
||||||
this.view.setHalfSize(vecOrX.clone());
|
this.view.setHalfSize(vecOrX.clone());
|
||||||
|
@ -108,9 +128,9 @@ export default class Viewport {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the size of the canvas that the viewport is projecting to.
|
* Updates the viewport with the size of the current Canvas
|
||||||
* @param vecOrX
|
* @param vecOrX The width of the canvas, or the canvas size as a Vec2
|
||||||
* @param y
|
* @param y The height of the canvas
|
||||||
*/
|
*/
|
||||||
setCanvasSize(vecOrX: Vec2 | number, y: number = null): void {
|
setCanvasSize(vecOrX: Vec2 | number, y: number = null): void {
|
||||||
if(vecOrX instanceof Vec2){
|
if(vecOrX instanceof Vec2){
|
||||||
|
@ -120,10 +140,18 @@ export default class Viewport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the zoom level of the viewport
|
||||||
|
* @param zoom The zoom level
|
||||||
|
*/
|
||||||
setZoomLevel(zoom: number): void {
|
setZoomLevel(zoom: number): void {
|
||||||
this.view.halfSize.scale(1/zoom);
|
this.view.halfSize.scale(1/zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the zoom level of the viewport
|
||||||
|
* @returns The zoom level
|
||||||
|
*/
|
||||||
getZoomLevel(): number {
|
getZoomLevel(): number {
|
||||||
return this.canvasSize.x/this.view.hw/2
|
return this.canvasSize.x/this.view.hw/2
|
||||||
}
|
}
|
||||||
|
@ -139,7 +167,7 @@ export default class Viewport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the viewport to focus on a point. Overidden by "following".
|
* Tells the viewport to focus on a point. Overidden by "following".
|
||||||
* @param focus
|
* @param focus The point the viewport should focus on
|
||||||
*/
|
*/
|
||||||
setFocus(focus: Vec2): void {
|
setFocus(focus: Vec2): void {
|
||||||
this.focus.copy(focus);
|
this.focus.copy(focus);
|
||||||
|
@ -147,7 +175,8 @@ export default class Viewport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the CanvasNode is inside of the viewport
|
* Returns true if the CanvasNode is inside of the viewport
|
||||||
* @param node
|
* @param node The node to check
|
||||||
|
* @returns True if the node is currently visible in the viewport, false if not
|
||||||
*/
|
*/
|
||||||
includes(node: CanvasNode): boolean {
|
includes(node: CanvasNode): boolean {
|
||||||
let parallax = node.getLayer() instanceof ParallaxLayer || node.getLayer() instanceof UILayer ? (<ParallaxLayer>node.getLayer()).parallax : new Vec2(1, 1);
|
let parallax = node.getLayer() instanceof ParallaxLayer || node.getLayer() instanceof UILayer ? (<ParallaxLayer>node.getLayer()).parallax : new Vec2(1, 1);
|
||||||
|
@ -162,10 +191,10 @@ export default class Viewport {
|
||||||
// TODO: This should probably be done automatically, or should consider the aspect ratio or something
|
// TODO: This should probably be done automatically, or should consider the aspect ratio or something
|
||||||
/**
|
/**
|
||||||
* Sets the bounds of the viewport
|
* Sets the bounds of the viewport
|
||||||
* @param lowerX
|
* @param lowerX The left edge of the viewport
|
||||||
* @param lowerY
|
* @param lowerY The top edge of the viewport
|
||||||
* @param upperX
|
* @param upperX The right edge of the viewport
|
||||||
* @param upperY
|
* @param upperY The bottom edge of the viewport
|
||||||
*/
|
*/
|
||||||
setBounds(lowerX: number, lowerY: number, upperX: number, upperY: number): void {
|
setBounds(lowerX: number, lowerY: number, upperX: number, upperY: number): void {
|
||||||
let hwidth = (upperX - lowerX)/2;
|
let hwidth = (upperX - lowerX)/2;
|
||||||
|
|
|
@ -3,9 +3,18 @@ import Receiver from "../Events/Receiver";
|
||||||
import ResourceManager from "../ResourceManager/ResourceManager";
|
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||||
import { GameEventType } from "../Events/GameEventType";
|
import { GameEventType } from "../Events/GameEventType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages any sounds or music needed for the game.
|
||||||
|
* Through the EventQueue, exposes interface to play sounds so GameNodes can activate sounds without
|
||||||
|
* needing direct references to the audio system
|
||||||
|
*/
|
||||||
export default class AudioManager {
|
export default class AudioManager {
|
||||||
private static instance: AudioManager;
|
private static instance: AudioManager;
|
||||||
|
|
||||||
|
/** The event receiver of this AudioManager */
|
||||||
private receiver: Receiver;
|
private receiver: Receiver;
|
||||||
|
|
||||||
|
/** A Map of the names of currently playing (or paused) sounds to their AudioBuffers */
|
||||||
private currentSounds: Map<AudioBufferSourceNode>;
|
private currentSounds: Map<AudioBufferSourceNode>;
|
||||||
|
|
||||||
private audioCtx: AudioContext;
|
private audioCtx: AudioContext;
|
||||||
|
@ -19,6 +28,7 @@ export default class AudioManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the instance of the AudioManager class or create a new one if none exists
|
* Get the instance of the AudioManager class or create a new one if none exists
|
||||||
|
* @returns The AudioManager
|
||||||
*/
|
*/
|
||||||
public static getInstance(): AudioManager {
|
public static getInstance(): AudioManager {
|
||||||
if(!this.instance){
|
if(!this.instance){
|
||||||
|
@ -42,15 +52,12 @@ export default class AudioManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current audio context
|
* Returns the current audio context
|
||||||
|
* @returns The AudioContext
|
||||||
*/
|
*/
|
||||||
public getAudioContext(): AudioContext {
|
public getAudioContext(): AudioContext {
|
||||||
return this.audioCtx;
|
return this.audioCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new sound from the key of a loaded audio file
|
|
||||||
* @param key The key of the loaded audio file to create a new sound for
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
According to the MDN, create a new sound for every call:
|
According to the MDN, create a new sound for every call:
|
||||||
|
|
||||||
|
@ -61,6 +68,11 @@ export default class AudioManager {
|
||||||
hold a reference to it. It will automatically be garbage-collected at an appropriate time, which won't be
|
hold a reference to it. It will automatically be garbage-collected at an appropriate time, which won't be
|
||||||
until sometime after the sound has finished playing.
|
until sometime after the sound has finished playing.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Creates a new sound from the key of a loaded audio file
|
||||||
|
* @param key The key of the loaded audio file to create a new sound for
|
||||||
|
* @returns The newly created AudioBuffer
|
||||||
|
*/
|
||||||
protected createSound(key: string): AudioBufferSourceNode {
|
protected createSound(key: string): AudioBufferSourceNode {
|
||||||
// Get audio buffer
|
// Get audio buffer
|
||||||
let buffer = ResourceManager.getInstance().getAudio(key);
|
let buffer = ResourceManager.getInstance().getAudio(key);
|
||||||
|
@ -109,10 +121,6 @@ export default class AudioManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the AudioManager
|
|
||||||
* @param deltaT
|
|
||||||
*/
|
|
||||||
update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
// Play each audio clip requested
|
// Play each audio clip requested
|
||||||
// TODO - Add logic to merge sounds if there are multiple of the same key
|
// TODO - Add logic to merge sounds if there are multiple of the same key
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
/** A class containing some utility functions for Arrays */
|
||||||
export default class ArrayUtils {
|
export default class ArrayUtils {
|
||||||
/**
|
/**
|
||||||
* Returns a 2d array of dim1 x dim2 filled with 1s
|
* Returns a 2d array of dim1 x dim2 filled with 1s
|
||||||
* @param dim1
|
* @param dim1 The first dimension of the array to create
|
||||||
* @param dim2
|
* @param dim2 The second dimension of the array to create
|
||||||
|
* @returns A dim1 x dim2 Array filled with 1s
|
||||||
*/
|
*/
|
||||||
static ones2d(dim1: number, dim2: number): number[][] {
|
static ones2d(dim1: number, dim2: number): number[][] {
|
||||||
let arr = new Array<Array<number>>(dim1);
|
let arr = new Array<Array<number>>(dim1);
|
||||||
|
@ -18,6 +20,13 @@ export default class ArrayUtils {
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a 2d array of dim1 x dim2 filled with true or false
|
||||||
|
* @param dim1 The first dimension of the array to create
|
||||||
|
* @param dim2 The second dimension of the array to create
|
||||||
|
* @param flag The boolean to fill the array with
|
||||||
|
* @returns A dim1 x dim2 Array filled with flag
|
||||||
|
*/
|
||||||
static bool2d(dim1: number, dim2: number, flag: boolean): boolean[][] {
|
static bool2d(dim1: number, dim2: number, flag: boolean): boolean[][] {
|
||||||
let arr = new Array<Array<boolean>>(dim1);
|
let arr = new Array<Array<boolean>>(dim1);
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
import MathUtils from "./MathUtils";
|
import MathUtils from "./MathUtils";
|
||||||
|
|
||||||
// TODO: This should be moved to the datatypes folder
|
// TODO: This should be moved to the datatypes folder
|
||||||
|
/**
|
||||||
|
* A Color util class that keeps track of colors like a vector, but can be converted into a string format
|
||||||
|
*/
|
||||||
export default class Color {
|
export default class Color {
|
||||||
|
/** The red value */
|
||||||
public r: number;
|
public r: number;
|
||||||
|
/** The green value */
|
||||||
public g: number;
|
public g: number;
|
||||||
|
/** The blue value */
|
||||||
public b: number;
|
public b: number;
|
||||||
|
/** The alpha value */
|
||||||
public a: number;
|
public a: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new color
|
||||||
|
* @param r Red
|
||||||
|
* @param g Green
|
||||||
|
* @param b Blue
|
||||||
|
* @param a Alpha
|
||||||
|
*/
|
||||||
constructor(r: number = 0, g: number = 0, b: number = 0, a: number = 1){
|
constructor(r: number = 0, g: number = 0, b: number = 0, a: number = 1){
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
|
@ -14,45 +28,93 @@ export default class Color {
|
||||||
this.a = a;
|
this.a = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transparent color
|
||||||
|
* @returns rgba(0, 0, 0, 0)
|
||||||
|
*/
|
||||||
static get TRANSPARENT(): Color {
|
static get TRANSPARENT(): Color {
|
||||||
return new Color(0, 0, 0, 0);
|
return new Color(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Red color
|
||||||
|
* @returns rgb(255, 0, 0)
|
||||||
|
*/
|
||||||
static get RED(): Color {
|
static get RED(): Color {
|
||||||
return new Color(255, 0, 0, 1);
|
return new Color(255, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Green color
|
||||||
|
* @returns rgb(0, 255, 0)
|
||||||
|
*/
|
||||||
static get GREEN(): Color {
|
static get GREEN(): Color {
|
||||||
return new Color(0, 255, 0, 1);
|
return new Color(0, 255, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blue color
|
||||||
|
* @returns rgb(0, 0, 255)
|
||||||
|
*/
|
||||||
static get BLUE(): Color {
|
static get BLUE(): Color {
|
||||||
return new Color(0, 0, 255, 1);
|
return new Color(0, 0, 255, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yellow color
|
||||||
|
* @returns rgb(255, 255, 0)
|
||||||
|
*/
|
||||||
static get YELLOW(): Color {
|
static get YELLOW(): Color {
|
||||||
return new Color(255, 255, 0, 1);
|
return new Color(255, 255, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purple color
|
||||||
|
* @returns rgb(255, 0, 255)
|
||||||
|
*/
|
||||||
static get PURPLE(): Color {
|
static get PURPLE(): Color {
|
||||||
return new Color(255, 0, 255, 1);
|
return new Color(255, 0, 255, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cyan color
|
||||||
|
* @returns rgb(0, 255, 255)
|
||||||
|
*/
|
||||||
static get CYAN(): Color {
|
static get CYAN(): Color {
|
||||||
return new Color(0, 255, 255, 1);
|
return new Color(0, 255, 255, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* White color
|
||||||
|
* @returns rgb(255, 255, 255)
|
||||||
|
*/
|
||||||
static get WHITE(): Color {
|
static get WHITE(): Color {
|
||||||
return new Color(255, 255, 255, 1);
|
return new Color(255, 255, 255, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Black color
|
||||||
|
* @returns rgb(0, 0, 0)
|
||||||
|
*/
|
||||||
static get BLACK(): Color {
|
static get BLACK(): Color {
|
||||||
return new Color(0, 0, 0, 1);
|
return new Color(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orange color
|
||||||
|
* @returns rgb(255, 100, 0)
|
||||||
|
*/
|
||||||
static get ORANGE(): Color {
|
static get ORANGE(): Color {
|
||||||
return new Color(255, 100, 0, 1);
|
return new Color(255, 100, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the color to the values provided
|
||||||
|
* @param r Red
|
||||||
|
* @param g Green
|
||||||
|
* @param b Blue
|
||||||
|
* @param a Alpha
|
||||||
|
*/
|
||||||
set(r: number, g: number, b: number, a: number = 1): void {
|
set(r: number, g: number, b: number, a: number = 1): void {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
|
@ -62,6 +124,7 @@ export default class Color {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new color slightly lighter than the current color
|
* Returns a new color slightly lighter than the current color
|
||||||
|
* @returns A new lighter Color
|
||||||
*/
|
*/
|
||||||
lighten(): Color {
|
lighten(): Color {
|
||||||
return new Color(MathUtils.clamp(this.r + 40, 0, 255), MathUtils.clamp(this.g + 40, 0, 255), MathUtils.clamp(this.b + 40, 0, 255), this.a);
|
return new Color(MathUtils.clamp(this.r + 40, 0, 255), MathUtils.clamp(this.g + 40, 0, 255), MathUtils.clamp(this.b + 40, 0, 255), this.a);
|
||||||
|
@ -69,6 +132,7 @@ export default class Color {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new color slightly darker than the current color
|
* Returns a new color slightly darker than the current color
|
||||||
|
* @returns A new darker Color
|
||||||
*/
|
*/
|
||||||
darken(): Color {
|
darken(): Color {
|
||||||
return new Color(MathUtils.clamp(this.r - 40, 0, 255), MathUtils.clamp(this.g - 40, 0, 255), MathUtils.clamp(this.b - 40, 0, 255), this.a);
|
return new Color(MathUtils.clamp(this.r - 40, 0, 255), MathUtils.clamp(this.g - 40, 0, 255), MathUtils.clamp(this.b - 40, 0, 255), this.a);
|
||||||
|
@ -76,6 +140,7 @@ export default class Color {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color as a string of the form #RRGGBB
|
* Returns the color as a string of the form #RRGGBB
|
||||||
|
* @returns #RRGGBB
|
||||||
*/
|
*/
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return "#" + MathUtils.toHex(this.r, 2) + MathUtils.toHex(this.g, 2) + MathUtils.toHex(this.b, 2);
|
return "#" + MathUtils.toHex(this.r, 2) + MathUtils.toHex(this.g, 2) + MathUtils.toHex(this.b, 2);
|
||||||
|
@ -83,6 +148,7 @@ export default class Color {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color as a string of the form rgb(r, g, b)
|
* Returns the color as a string of the form rgb(r, g, b)
|
||||||
|
* @returns rgb(r, g, b)
|
||||||
*/
|
*/
|
||||||
toStringRGB(): string {
|
toStringRGB(): string {
|
||||||
return "rgb(" + this.r.toString() + ", " + this.g.toString() + ", " + this.b.toString() + ")";
|
return "rgb(" + this.r.toString() + ", " + this.g.toString() + ", " + this.b.toString() + ")";
|
||||||
|
@ -90,6 +156,7 @@ export default class Color {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color as a string of the form rgba(r, g, b, a)
|
* Returns the color as a string of the form rgba(r, g, b, a)
|
||||||
|
* @returns rgba(r, g, b, a)
|
||||||
*/
|
*/
|
||||||
toStringRGBA(): string {
|
toStringRGBA(): string {
|
||||||
if(this.a === 0){
|
if(this.a === 0){
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// @ignorePage
|
||||||
|
|
||||||
export default class EaseFunctions {
|
export default class EaseFunctions {
|
||||||
|
|
||||||
static easeInOutSine(x: number): number {
|
static easeInOutSine(x: number): number {
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
import Graph, { EdgeNode } from "../DataTypes/Graphs/Graph";
|
import Graph from "../DataTypes/Graphs/Graph";
|
||||||
|
import EdgeNode from "../DataTypes/Graphs/EdgeNode";
|
||||||
|
|
||||||
|
/** A class to provides some utility functions for graphs */
|
||||||
export default class GraphUtils {
|
export default class GraphUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of Djikstra's shortest path algorithm based on the one described in The Algorithm Design Manual.
|
||||||
|
* @param g The graph
|
||||||
|
* @param start The number to start the shortest path from
|
||||||
|
* @returns An array containing the parent of each node of the Graph in the shortest path.
|
||||||
|
*/
|
||||||
static djikstra(g: Graph, start: number): Array<number> {
|
static djikstra(g: Graph, start: number): Array<number> {
|
||||||
let i: number; // Counter
|
let i: number; // Counter
|
||||||
let p: EdgeNode; // Pointer to edgenode
|
let p: EdgeNode; // Pointer to edgenode
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import Vec2 from "../DataTypes/Vec2";
|
import Vec2 from "../DataTypes/Vec2";
|
||||||
|
|
||||||
|
/** A class containing some utility functions for math operations */
|
||||||
export default class MathUtils {
|
export default class MathUtils {
|
||||||
/**
|
/**
|
||||||
* Returns the sign of the value provided
|
* Returns the sign of the value provided
|
||||||
* @param x The value to extract the sign from
|
* @param x The value to extract the sign from
|
||||||
|
* @returns -1 if the number is less than 0, 1 otherwise
|
||||||
*/
|
*/
|
||||||
static sign(x: number): number {
|
static sign(x: number): number {
|
||||||
return x < 0 ? -1 : 1;
|
return x < 0 ? -1 : 1;
|
||||||
|
@ -14,6 +16,7 @@ export default class MathUtils {
|
||||||
* @param x The value to be clamped
|
* @param x The value to be clamped
|
||||||
* @param min The min of the range
|
* @param min The min of the range
|
||||||
* @param max The max of the range
|
* @param max The max of the range
|
||||||
|
* @returns x, if it is between min and max, or min/max if it exceeds their bounds
|
||||||
*/
|
*/
|
||||||
static clamp(x: number, min: number, max: number): number {
|
static clamp(x: number, min: number, max: number): number {
|
||||||
if(x < min) return min;
|
if(x < min) return min;
|
||||||
|
@ -24,6 +27,7 @@ export default class MathUtils {
|
||||||
/**
|
/**
|
||||||
* Clamps the value x to the range between 0 and 1
|
* Clamps the value x to the range between 0 and 1
|
||||||
* @param x The value to be clamped
|
* @param x The value to be clamped
|
||||||
|
* @returns x, if it is between 0 and 1, or 0/1 if it exceeds their bounds
|
||||||
*/
|
*/
|
||||||
static clamp01(x: number): number {
|
static clamp01(x: number): number {
|
||||||
return MathUtils.clamp(x, 0, 1);
|
return MathUtils.clamp(x, 0, 1);
|
||||||
|
@ -42,11 +46,19 @@ export default class MathUtils {
|
||||||
* @param a The first value for the interpolation bound
|
* @param a The first value for the interpolation bound
|
||||||
* @param b The second value for the interpolation bound
|
* @param b The second value for the interpolation bound
|
||||||
* @param t The time we are interpolating to
|
* @param t The time we are interpolating to
|
||||||
|
* @returns The value between a and b at time t
|
||||||
*/
|
*/
|
||||||
static lerp(a: number, b: number, t: number): number {
|
static lerp(a: number, b: number, t: number): number {
|
||||||
return a + t * (b - a);
|
return a + t * (b - a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverse Linear Interpolation. Finds the time at which a value between a and b would occur
|
||||||
|
* @param a The first value for the interpolation bound
|
||||||
|
* @param b The second value for the interpolation bound
|
||||||
|
* @param value The current value
|
||||||
|
* @returns The time at which the current value occurs between a and b
|
||||||
|
*/
|
||||||
static invLerp(a: number, b: number, value: number){
|
static invLerp(a: number, b: number, value: number){
|
||||||
return (value - a)/(b - a);
|
return (value - a)/(b - a);
|
||||||
}
|
}
|
||||||
|
@ -55,6 +67,7 @@ export default class MathUtils {
|
||||||
* Cuts off decimal points of a number after a specified place
|
* Cuts off decimal points of a number after a specified place
|
||||||
* @param num The number to floor
|
* @param num The number to floor
|
||||||
* @param place The last decimal place of the new number
|
* @param place The last decimal place of the new number
|
||||||
|
* @returns The floored number
|
||||||
*/
|
*/
|
||||||
static floorToPlace(num: number, place: number): number {
|
static floorToPlace(num: number, place: number): number {
|
||||||
if(place === 0){
|
if(place === 0){
|
||||||
|
@ -75,6 +88,7 @@ export default class MathUtils {
|
||||||
* Returns the number as a hexadecimal
|
* Returns the number as a hexadecimal
|
||||||
* @param num The number to convert to hex
|
* @param num The number to convert to hex
|
||||||
* @param minLength The length of the returned hex string (adds zero padding if needed)
|
* @param minLength The length of the returned hex string (adds zero padding if needed)
|
||||||
|
* @returns The hex representation of the number as a string
|
||||||
*/
|
*/
|
||||||
static toHex(num: number, minLength: number = null): string {
|
static toHex(num: number, minLength: number = null): string {
|
||||||
let factor = 1;
|
let factor = 1;
|
||||||
|
@ -99,8 +113,9 @@ export default class MathUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the number to hexadecimal
|
* Converts a digit to hexadecimal. In this case, a digit is between 0 and 15 inclusive
|
||||||
* @param num The number to convert to hexadecimal
|
* @param num The digit to convert to hexadecimal
|
||||||
|
* @returns The hex representation of the digit as a string
|
||||||
*/
|
*/
|
||||||
static toHexDigit(num: number): string {
|
static toHexDigit(num: number): string {
|
||||||
if(num < 10){
|
if(num < 10){
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { Physical } from "../DataTypes/Interfaces/Descriptors";
|
|
||||||
|
|
||||||
export default class PhysicsUtils {
|
|
||||||
static sweepAndPrune(nodes: Array<Physical>){
|
|
||||||
// Sort
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,9 @@ const permutation = [ 151,160,137,91,90,15,
|
||||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A noise generator
|
||||||
|
*/
|
||||||
export default class Perlin {
|
export default class Perlin {
|
||||||
|
|
||||||
private p: Int16Array;
|
private p: Int16Array;
|
||||||
|
@ -30,11 +33,12 @@ export default class Perlin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a random perlin noise value
|
* Returns a random perlin noise value
|
||||||
* @param x
|
* @param x An input value
|
||||||
* @param y
|
* @param y An input value
|
||||||
* @param z
|
* @param z An input value
|
||||||
|
* @returns A noise value
|
||||||
*/
|
*/
|
||||||
perlin(x: number, y: number, z: number = 0){
|
perlin(x: number, y: number, z: number = 0): number {
|
||||||
if(this.repeat > 0) {
|
if(this.repeat > 0) {
|
||||||
x = x%this.repeat;
|
x = x%this.repeat;
|
||||||
y = y%this.repeat;
|
y = y%this.repeat;
|
||||||
|
|
|
@ -10,11 +10,13 @@ class Noise {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A class that has some random generator utils */
|
||||||
export default class RandUtils {
|
export default class RandUtils {
|
||||||
/**
|
/**
|
||||||
* Generates a random integer in the specified range
|
* Generates a random integer in the specified range
|
||||||
* @param min The min of the range (inclusive)
|
* @param min The min of the range (inclusive)
|
||||||
* @param max The max of the range (exclusive)
|
* @param max The max of the range (exclusive)
|
||||||
|
* @returns A random int in the range [min, max)
|
||||||
*/
|
*/
|
||||||
static randInt(min: number, max: number): number {
|
static randInt(min: number, max: number): number {
|
||||||
return Math.floor(Math.random()*(max - min) + min);
|
return Math.floor(Math.random()*(max - min) + min);
|
||||||
|
@ -24,6 +26,7 @@ export default class RandUtils {
|
||||||
* Generates a random hexadecimal number in the specified range
|
* Generates a random hexadecimal number in the specified range
|
||||||
* @param min The min of the range (inclusive)
|
* @param min The min of the range (inclusive)
|
||||||
* @param max The max of the range (exclusive)
|
* @param max The max of the range (exclusive)
|
||||||
|
* @returns a random hex number in the range [min, max) as a string
|
||||||
*/
|
*/
|
||||||
static randHex(min: number, max: number): string {
|
static randHex(min: number, max: number): string {
|
||||||
return MathUtils.toHex(RandUtils.randInt(min, max));
|
return MathUtils.toHex(RandUtils.randInt(min, max));
|
||||||
|
@ -31,6 +34,7 @@ export default class RandUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random color
|
* Generates a random color
|
||||||
|
* @returns A random Color
|
||||||
*/
|
*/
|
||||||
static randColor(): Color {
|
static randColor(): Color {
|
||||||
let r = RandUtils.randInt(0, 256);
|
let r = RandUtils.randInt(0, 256);
|
||||||
|
@ -39,6 +43,7 @@ export default class RandUtils {
|
||||||
return new Color(r, g, b);
|
return new Color(r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A noise generator */
|
||||||
static noise: Noise = new Noise();
|
static noise: Noise = new Noise();
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,7 +1,14 @@
|
||||||
|
/** Some utility functions for sorting arrays */
|
||||||
export default class SortingUtils {
|
export default class SortingUtils {
|
||||||
/**
|
/**
|
||||||
*
|
* An implementation of insertion sort.
|
||||||
* @param arr
|
* In game engines, this is particularly useful to sort node positions because of temporal coherence -
|
||||||
|
* the idea that nodes are almost in the same place as last frame, and thus, in a frame-to-frame comparison,
|
||||||
|
* nodes essentially do not change position.
|
||||||
|
* This means we have a nearly sorted array of nodes if we keep track of this,
|
||||||
|
* so something like insertion sort actually becomes essentailly O(n),
|
||||||
|
* as it performs very well on nearly sorted arrays.
|
||||||
|
* @param arr The array to sort in place
|
||||||
* @param comparator Compares element a and b in the array. Returns -1 if a < b, 0 if a = b, and 1 if a > b
|
* @param comparator Compares element a and b in the array. Returns -1 if a < b, 0 if a = b, and 1 if a > b
|
||||||
*/
|
*/
|
||||||
static insertionSort<T>(arr: Array<T>, comparator: (a: T, b: T) => number): void {
|
static insertionSort<T>(arr: Array<T>, comparator: (a: T, b: T) => number): void {
|
||||||
|
@ -16,7 +23,13 @@ export default class SortingUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static swap<T>(arr: Array<T>, i: number, j: number){
|
/**
|
||||||
|
* Swaps two elements in the provided array
|
||||||
|
* @param arr The array to perform the swap on in place
|
||||||
|
* @param i The first index
|
||||||
|
* @param j The second index
|
||||||
|
*/
|
||||||
|
static swap<T>(arr: Array<T>, i: number, j: number): void {
|
||||||
let temp = arr[i];
|
let temp = arr[i];
|
||||||
arr[i] = arr[j];
|
arr[i] = arr[j];
|
||||||
arr[j] = temp;
|
arr[j] = temp;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
/** Some utility functions for dealing with strings */
|
||||||
export default class StringUtils {
|
export default class StringUtils {
|
||||||
/**
|
/**
|
||||||
* Extracts the path from a filepath that includes the file
|
* Extracts the path from a filepath that includes the file
|
||||||
* @param filePath the filepath to extract the path form
|
* @param filePath the filepath to extract the path from
|
||||||
|
* @returns The path portion of the filepath provided
|
||||||
*/
|
*/
|
||||||
static getPathFromFilePath(filePath: string): string {
|
static getPathFromFilePath(filePath: string): string {
|
||||||
let splitPath = filePath.split("/");
|
let splitPath = filePath.split("/");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user