From c241aa99bc44d723c03cff51ab7649ca6935fb83 Mon Sep 17 00:00:00 2001 From: Joe Weaver Date: Thu, 4 Mar 2021 19:10:41 -0500 Subject: [PATCH] add Timers and Lines, and modified Registry --- src/Wolfie2D/DataTypes/Graphs/Graph.ts | 20 ++++ src/Wolfie2D/DataTypes/Interfaces/Actor.ts | 7 ++ src/Wolfie2D/DataTypes/Queue.ts | 18 ++++ src/Wolfie2D/DataTypes/State/State.ts | 7 +- src/Wolfie2D/DataTypes/State/StateMachine.ts | 13 ++- src/Wolfie2D/DataTypes/Vec2.ts | 21 ++++ src/Wolfie2D/Input/Input.ts | 4 +- src/Wolfie2D/Loop/Game.ts | 9 +- src/Wolfie2D/Nodes/GameNode.ts | 15 ++- src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts | 1 + src/Wolfie2D/Nodes/Graphics/Line.ts | 33 +++++++ src/Wolfie2D/Nodes/Tilemap.ts | 2 +- src/Wolfie2D/Pathfinding/NavigationPath.ts | 7 +- src/Wolfie2D/Registry/Registries/Registry.ts | 21 ++++ .../Registry/Registries/ShaderRegistry.ts | 3 +- src/Wolfie2D/Registry/Registry.ts | 16 --- src/Wolfie2D/Registry/RegistryManager.ts | 31 ++++++ src/Wolfie2D/Rendering/CanvasRenderer.ts | 22 ++++- .../CanvasRendering/GraphicRenderer.ts | 12 +++ src/Wolfie2D/Rendering/WebGLRenderer.ts | 14 +-- .../ResourceManager/ResourceManager.ts | 99 +++++++++++++++++-- .../Scene/Factories/CanvasNodeFactory.ts | 15 ++- src/Wolfie2D/Scene/Layer.ts | 18 ++++ src/Wolfie2D/Scene/Scene.ts | 7 ++ src/Wolfie2D/Timing/Timer.ts | 80 +++++++++++++++ src/Wolfie2D/Timing/TimerManager.ts | 33 +++++++ src/Wolfie2D/Utils/Color.ts | 9 ++ src/Wolfie2D/Utils/MathUtils.ts | 28 ++++++ 28 files changed, 513 insertions(+), 52 deletions(-) create mode 100644 src/Wolfie2D/Nodes/Graphics/Line.ts create mode 100644 src/Wolfie2D/Registry/Registries/Registry.ts delete mode 100644 src/Wolfie2D/Registry/Registry.ts create mode 100644 src/Wolfie2D/Registry/RegistryManager.ts create mode 100644 src/Wolfie2D/Timing/Timer.ts create mode 100644 src/Wolfie2D/Timing/TimerManager.ts diff --git a/src/Wolfie2D/DataTypes/Graphs/Graph.ts b/src/Wolfie2D/DataTypes/Graphs/Graph.ts index 39ba18e..21168c2 100644 --- a/src/Wolfie2D/DataTypes/Graphs/Graph.ts +++ b/src/Wolfie2D/DataTypes/Graphs/Graph.ts @@ -50,6 +50,8 @@ export default class Graph { addEdge(x: number, y: number, weight?: number): void { let edge = new EdgeNode(y, weight); + + if(this.edges[x]){ edge.next = this.edges[x]; } @@ -69,6 +71,24 @@ export default class Graph { this.numEdges += 1; } + /** + * Checks whether or not an edge exists between two nodes. + * This check is directional if this is a directed graph. + * @param x The first node + * @param y The second node + * @returns true if an edge exists, false otherwise + */ + edgeExists(x: number, y: number): boolean { + let edge = this.edges[x]; + + while(edge !== null){ + if(edge.y === y){ + return true; + } + edge = edge.next; + } + } + /** * Gets the edge list associated with node x * @param x The index of the node diff --git a/src/Wolfie2D/DataTypes/Interfaces/Actor.ts b/src/Wolfie2D/DataTypes/Interfaces/Actor.ts index 37dc91a..15f5076 100644 --- a/src/Wolfie2D/DataTypes/Interfaces/Actor.ts +++ b/src/Wolfie2D/DataTypes/Interfaces/Actor.ts @@ -33,4 +33,11 @@ export default interface Actor { * @param options An object that allows options to be pased to the activated AI */ setAIActive(active: boolean, options: Record): void; + + /** + * Moves this GameNode along a path + * @param speed The speed to move with + * @param path The path we're moving along + */ + moveOnPath(speed: number, path: NavigationPath): void; } \ No newline at end of file diff --git a/src/Wolfie2D/DataTypes/Queue.ts b/src/Wolfie2D/DataTypes/Queue.ts index 8378746..89ec40e 100644 --- a/src/Wolfie2D/DataTypes/Queue.ts +++ b/src/Wolfie2D/DataTypes/Queue.ts @@ -109,4 +109,22 @@ export default class Queue implements Collection { i = (i + 1) % this.MAX_ELEMENTS; } } + + /** + * Converts this queue into a string format + * @returns A string representing this queue + */ + toString(): string { + let retval = ""; + + this.forEach( (item, index) => { + let str = item.toString() + if(index !== 0){ + str += " -> " + } + retval = str + retval; + }); + + return "Top -> " + retval; + } } \ No newline at end of file diff --git a/src/Wolfie2D/DataTypes/State/State.ts b/src/Wolfie2D/DataTypes/State/State.ts index 9ed22f9..3a72e24 100644 --- a/src/Wolfie2D/DataTypes/State/State.ts +++ b/src/Wolfie2D/DataTypes/State/State.ts @@ -25,8 +25,9 @@ export default abstract class State implements Updateable { /** * A method that is called when this state is entered. Use this to initialize any variables before updates occur. + * @param options Information to pass to this state */ - abstract onEnter(): void; + abstract onEnter(options: Record): void; /** * A lifecycle method that handles an input event, such as taking damage. @@ -42,11 +43,13 @@ export default abstract class State implements Updateable { * @param stateName The name of the state to transition to */ protected finished(stateName: string): void { + console.log("Finished"); this.parent.changeState(stateName); } /** * A lifecycle method is called when the state is ending. + * @returns info to pass to the next state */ - abstract onExit(): void; + abstract onExit(): Record; } \ No newline at end of file diff --git a/src/Wolfie2D/DataTypes/State/StateMachine.ts b/src/Wolfie2D/DataTypes/State/StateMachine.ts index 3215c62..a6382e7 100644 --- a/src/Wolfie2D/DataTypes/State/StateMachine.ts +++ b/src/Wolfie2D/DataTypes/State/StateMachine.ts @@ -67,9 +67,10 @@ export default class StateMachine implements Updateable { * Initializes this state machine with an initial state and sets it running * @param initialState The name of initial state of the state machine */ - initialize(initialState: string): void { + initialize(initialState: string, options: Record = {}): void { this.stack.push(this.stateMap.get(initialState)); this.currentState = this.stack.peek(); + this.currentState.onEnter(options); this.setActive(true); } @@ -88,7 +89,7 @@ export default class StateMachine implements Updateable { */ changeState(state: string): void { // Exit the current state - this.currentState.onExit(); + let options = this.currentState.onExit(); // Make sure the correct state is at the top of the stack if(state === "previous"){ @@ -109,7 +110,7 @@ export default class StateMachine implements Updateable { } // Enter the new state - this.currentState.onEnter(); + this.currentState.onEnter(options); } /** @@ -124,6 +125,12 @@ export default class StateMachine implements Updateable { // @implemented update(deltaT: number): void { + // Distribute events + while(this.receiver.hasNextEvent()){ + let event = this.receiver.getNextEvent(); + this.handleEvent(event); + } + // Delegate the update to the current state this.currentState.update(deltaT); } diff --git a/src/Wolfie2D/DataTypes/Vec2.ts b/src/Wolfie2D/DataTypes/Vec2.ts index 63d1195..1764525 100644 --- a/src/Wolfie2D/DataTypes/Vec2.ts +++ b/src/Wolfie2D/DataTypes/Vec2.ts @@ -110,6 +110,10 @@ export default class Vec2 { * @returns A new vector that is the unit vector for this one */ normalized(): Vec2 { + if(this.isZero()){ + return this; + } + let mag = this.mag(); return new Vec2(this.x/mag, this.y/mag); } @@ -235,6 +239,23 @@ export default class Vec2 { return this; } + /** + * Increments the fields of this vector. Both are incremented with a, if only a is provided. + * @param a The first number to increment by + * @param b The second number to increment by + * @returnss This vector after incrementing + */ + inc(a: number, b?: number): Vec2 { + if(b === undefined){ + this.x += a; + this.y += a; + } else { + this.x += a; + this.y += b; + } + return this; + } + /** * Subtracts another vector from this vector * @param other The Vec2 to subtract from this one diff --git a/src/Wolfie2D/Input/Input.ts b/src/Wolfie2D/Input/Input.ts index 54e251b..f12cddc 100644 --- a/src/Wolfie2D/Input/Input.ts +++ b/src/Wolfie2D/Input/Input.ts @@ -260,7 +260,7 @@ export default class Input { * @returns The mouse position stored as a Vec2 */ static getMousePosition(): Vec2 { - return Input.mousePosition; + return Input.mousePosition.scaled(1/this.viewport.getZoomLevel()); } /** @@ -269,7 +269,7 @@ export default class Input { * @returns The mouse position stored as a Vec2 */ static getGlobalMousePosition(): Vec2 { - return Input.mousePosition.clone().add(Input.viewport.getOrigin()); + return Input.mousePosition.clone().scale(1/this.viewport.getZoomLevel()).add(Input.viewport.getOrigin()); } /** diff --git a/src/Wolfie2D/Loop/Game.ts b/src/Wolfie2D/Loop/Game.ts index 0e40f36..4f012ab 100644 --- a/src/Wolfie2D/Loop/Game.ts +++ b/src/Wolfie2D/Loop/Game.ts @@ -16,7 +16,7 @@ import GameLoop from "./GameLoop"; import FixedUpdateGameLoop from "./FixedUpdateGameLoop"; import EnvironmentInitializer from "./EnvironmentInitializer"; import Vec2 from "../DataTypes/Vec2"; -import Registry from "../Registry/Registry"; +import RegistryManager from "../Registry/RegistryManager"; import WebGLRenderer from "../Rendering/WebGLRenderer"; import Scene from "../Scene/Scene"; @@ -139,7 +139,7 @@ export default class Game { this.loop.doRender = () => this.render(); // Preload registry items - Registry.preload(); + RegistryManager.preload(); // Load the items with the resource manager this.resourceManager.loadResourcesFromQueue(() => { @@ -185,6 +185,11 @@ export default class Game { this.sceneManager.render(); + // Hacky debug mode + if(Input.isKeyJustPressed("g")){ + this.showDebug = !this.showDebug; + } + // Debug render if(this.showDebug){ Debug.render(); diff --git a/src/Wolfie2D/Nodes/GameNode.ts b/src/Wolfie2D/Nodes/GameNode.ts index c3a0fd1..02a08bb 100644 --- a/src/Wolfie2D/Nodes/GameNode.ts +++ b/src/Wolfie2D/Nodes/GameNode.ts @@ -109,7 +109,6 @@ export default abstract class GameNode implements Positioned, Unique, Updateable inRelativeCoordinates(point: Vec2): Vec2 { let origin = this.scene.getViewTranslation(this); let zoom = this.scene.getViewScale(); - return point.clone().sub(origin).scale(zoom); } @@ -137,6 +136,14 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this._velocity = velocity; }; + moveOnPath(speed: number, path: NavigationPath): void { + this.path = path; + let dir = path.getMoveDirection(this); + this.moving = true; + this.pathfinding = true; + this._velocity = dir.scale(speed); + } + // @implemented /** * @param velocity The velocity with which the object will move. @@ -146,6 +153,8 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this.position.add(this._velocity); if(this.pathfinding){ this.path.handlePathProgress(this); + this.path = null; + this.pathfinding = false; } } @@ -266,7 +275,9 @@ export default abstract class GameNode implements Positioned, Unique, Updateable // @implemented setAIActive(active: boolean, options: Record): void { this.aiActive = active; - this.ai.activate(options); + if(this.aiActive){ + this.ai.activate(options); + } } /*---------- TWEENABLE PROPERTIES ----------*/ diff --git a/src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts b/src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts index 4ed20f9..3631c46 100644 --- a/src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts +++ b/src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts @@ -1,4 +1,5 @@ export enum GraphicType { POINT = "POINT", RECT = "RECT", + LINE = "LINE", } \ No newline at end of file diff --git a/src/Wolfie2D/Nodes/Graphics/Line.ts b/src/Wolfie2D/Nodes/Graphics/Line.ts new file mode 100644 index 0000000..b6242ee --- /dev/null +++ b/src/Wolfie2D/Nodes/Graphics/Line.ts @@ -0,0 +1,33 @@ +import Vec2 from "../../DataTypes/Vec2"; +import Graphic from "../Graphic"; + +export default class Line extends Graphic { + protected _end: Vec2; + thickness: number; + + constructor(start: Vec2, end: Vec2){ + super(); + this.start = start; + this.end = end; + this.thickness = 2; + + // Does this really have a meaning for lines? + this.size.set(5, 5); + } + + set start(pos: Vec2){ + this.position = pos; + } + + get start(): Vec2 { + return this.position; + } + + set end(pos: Vec2){ + this._end = pos; + } + + get end(): Vec2 { + return this._end; + } +} \ No newline at end of file diff --git a/src/Wolfie2D/Nodes/Tilemap.ts b/src/Wolfie2D/Nodes/Tilemap.ts index 524a173..eea97b2 100644 --- a/src/Wolfie2D/Nodes/Tilemap.ts +++ b/src/Wolfie2D/Nodes/Tilemap.ts @@ -31,7 +31,7 @@ export default abstract class Tilemap extends CanvasNode { let tilecount = 0; for(let tileset of tilesets){ - tilecount += tileset.getTileCount(); + tilecount += tileset.getTileCount() + 1; } this.collisionMap = new Array(tilecount); diff --git a/src/Wolfie2D/Pathfinding/NavigationPath.ts b/src/Wolfie2D/Pathfinding/NavigationPath.ts index dcb1b62..52ff112 100644 --- a/src/Wolfie2D/Pathfinding/NavigationPath.ts +++ b/src/Wolfie2D/Pathfinding/NavigationPath.ts @@ -19,9 +19,8 @@ export default class NavigationPath { */ constructor(path: Stack){ this.path = path; - console.log(path.toString()) this.currentMoveDirection = Vec2.ZERO; - this.distanceThreshold = 20; + this.distanceThreshold = 4; } /** @@ -52,4 +51,8 @@ export default class NavigationPath { this.path.pop(); } } + + toString(): string { + return this.path.toString() + } } \ No newline at end of file diff --git a/src/Wolfie2D/Registry/Registries/Registry.ts b/src/Wolfie2D/Registry/Registries/Registry.ts new file mode 100644 index 0000000..f67d135 --- /dev/null +++ b/src/Wolfie2D/Registry/Registries/Registry.ts @@ -0,0 +1,21 @@ +import Map from "../../DataTypes/Map"; + +export default abstract class Registry extends Map{ + + /** Preloads registry data */ + public abstract preload(): void; + + /** + * Registers an item and preloads any necessary files + * @param key The key to register this item with + * @param args Any additional arguments needed for registration + */ + public abstract registerAndPreloadItem(key: string, ...args: any): void; + + /** + * Registers an item and preloads any necessary files + * @param key The key to register this item with + * @param args Any aditional arguments needed for registration + */ + public abstract registerItem(key: string, ...args: any): void; +} \ No newline at end of file diff --git a/src/Wolfie2D/Registry/Registries/ShaderRegistry.ts b/src/Wolfie2D/Registry/Registries/ShaderRegistry.ts index ec88e11..7e566a3 100644 --- a/src/Wolfie2D/Registry/Registries/ShaderRegistry.ts +++ b/src/Wolfie2D/Registry/Registries/ShaderRegistry.ts @@ -5,11 +5,12 @@ import PointShaderType from "../../Rendering/WebGLRendering/ShaderTypes/PointSha import RectShaderType from "../../Rendering/WebGLRendering/ShaderTypes/RectShaderType"; import SpriteShaderType from "../../Rendering/WebGLRendering/ShaderTypes/SpriteShaderType"; import ResourceManager from "../../ResourceManager/ResourceManager"; +import Registry from "./Registry"; /** * A registry that handles shaders */ -export default class ShaderRegistry extends Map { +export default class ShaderRegistry extends Registry { // Shader names public static POINT_SHADER = "point"; diff --git a/src/Wolfie2D/Registry/Registry.ts b/src/Wolfie2D/Registry/Registry.ts deleted file mode 100644 index 113fb51..0000000 --- a/src/Wolfie2D/Registry/Registry.ts +++ /dev/null @@ -1,16 +0,0 @@ -import ShaderRegistry from "./Registries/ShaderRegistry"; - -/** - * The Registry is the system's way of converting classes and types into string - * representations for use elsewhere in the application. - * It allows classes to be accessed without explicitly using constructors in code, - * and for resources to be loaded at Game creation time. - */ -export default class Registry { - - public static shaders = new ShaderRegistry(); - - static preload(){ - this.shaders.preload(); - } -} \ No newline at end of file diff --git a/src/Wolfie2D/Registry/RegistryManager.ts b/src/Wolfie2D/Registry/RegistryManager.ts new file mode 100644 index 0000000..d3cd802 --- /dev/null +++ b/src/Wolfie2D/Registry/RegistryManager.ts @@ -0,0 +1,31 @@ +import Map from "../DataTypes/Map"; +import Registry from "./Registries/Registry"; +import ShaderRegistry from "./Registries/ShaderRegistry"; + +/** + * The Registry is the system's way of converting classes and types into string + * representations for use elsewhere in the application. + * It allows classes to be accessed without explicitly using constructors in code, + * and for resources to be loaded at Game creation time. + */ +export default class RegistryManager { + + public static shaders = new ShaderRegistry(); + + /** Additional custom registries to add to the registry manager */ + protected static registries: Map> = new Map(); + + static preload(){ + this.shaders.preload(); + + this.registries.forEach((key: string) => this.registries.get(key).preload()); + } + + static addCustomRegistry(name: string, registry: Registry){ + this.registries.add(name, registry); + } + + static getRegistry(key: string){ + return this.registries.get(key); + } +} \ No newline at end of file diff --git a/src/Wolfie2D/Rendering/CanvasRenderer.ts b/src/Wolfie2D/Rendering/CanvasRenderer.ts index d6f5433..0bb4f96 100644 --- a/src/Wolfie2D/Rendering/CanvasRenderer.ts +++ b/src/Wolfie2D/Rendering/CanvasRenderer.ts @@ -20,6 +20,8 @@ import TextInput from "../Nodes/UIElements/TextInput"; import AnimatedSprite from "../Nodes/Sprites/AnimatedSprite"; import Vec2 from "../DataTypes/Vec2"; import Color from "../Utils/Color"; +import Line from "../Nodes/Graphics/Line"; +import Debug from "../Debug/Debug"; /** * An implementation of the RenderingManager class using CanvasRenderingContext2D. @@ -112,9 +114,19 @@ export default class CanvasRenderer extends RenderingManager { } // Render the uiLayers on top of everything else - uiLayers.forEach(key => { - if(!uiLayers.get(key).isHidden()) - uiLayers.get(key).getItems().forEach(node => this.renderNode(node)) + let sortedUILayers = new Array(); + + uiLayers.forEach(key => sortedUILayers.push(uiLayers.get(key))); + + sortedUILayers = sortedUILayers.sort((ui1, ui2) => ui1.getDepth() - ui2.getDepth()); + + sortedUILayers.forEach(uiLayer => { + if(!uiLayer.isHidden()) + uiLayer.getItems().forEach(node => { + if((node).visible){ + this.renderNode(node) + } + }) }); } @@ -141,7 +153,7 @@ export default class CanvasRenderer extends RenderingManager { this.ctx.setTransform(xScale, 0, 0, yScale, (node.position.x - this.origin.x)*this.zoom, (node.position.y - this.origin.y)*this.zoom); this.ctx.rotate(-node.rotation); let globalAlpha = this.ctx.globalAlpha; - this.ctx.globalAlpha = node.alpha; + this.ctx.globalAlpha = ((node).color ? (node).color.a : 1) * node.alpha; if(node instanceof AnimatedSprite){ this.renderAnimatedSprite(node); @@ -205,6 +217,8 @@ export default class CanvasRenderer extends RenderingManager { protected renderGraphic(graphic: Graphic): void { if(graphic instanceof Point){ this.graphicRenderer.renderPoint(graphic, this.zoom); + } else if(graphic instanceof Line){ + this.graphicRenderer.renderLine(graphic, this.origin, this.zoom); } else if(graphic instanceof Rect){ this.graphicRenderer.renderRect(graphic, this.zoom); } diff --git a/src/Wolfie2D/Rendering/CanvasRendering/GraphicRenderer.ts b/src/Wolfie2D/Rendering/CanvasRendering/GraphicRenderer.ts index 62e21ba..230249d 100644 --- a/src/Wolfie2D/Rendering/CanvasRendering/GraphicRenderer.ts +++ b/src/Wolfie2D/Rendering/CanvasRendering/GraphicRenderer.ts @@ -1,3 +1,5 @@ +import Vec2 from "../../DataTypes/Vec2"; +import Line from "../../Nodes/Graphics/Line"; import Point from "../../Nodes/Graphics/Point"; import Rect from "../../Nodes/Graphics/Rect"; import ResourceManager from "../../ResourceManager/ResourceManager"; @@ -38,6 +40,16 @@ export default class GraphicRenderer { point.size.x*zoom, point.size.y*zoom); } + renderLine(line: Line, origin: Vec2, zoom: number): void { + this.ctx.strokeStyle = line.color.toStringRGBA(); + this.ctx.lineWidth = line.thickness; + this.ctx.beginPath(); + this.ctx.moveTo(0, 0); + this.ctx.lineTo((line.end.x - line.start.x)*zoom, (line.end.y - line.start.y)*zoom); + this.ctx.closePath(); + this.ctx.stroke(); + } + /** * Renders a rect * @param rect The rect to render diff --git a/src/Wolfie2D/Rendering/WebGLRenderer.ts b/src/Wolfie2D/Rendering/WebGLRenderer.ts index 1157146..0ad5a19 100644 --- a/src/Wolfie2D/Rendering/WebGLRenderer.ts +++ b/src/Wolfie2D/Rendering/WebGLRenderer.ts @@ -13,7 +13,7 @@ import Tilemap from "../Nodes/Tilemap"; import UIElement from "../Nodes/UIElement"; import Label from "../Nodes/UIElements/Label"; import ShaderRegistry from "../Registry/Registries/ShaderRegistry"; -import Registry from "../Registry/Registry"; +import RegistryManager from "../Registry/RegistryManager"; import ResourceManager from "../ResourceManager/ResourceManager"; import ParallaxLayer from "../Scene/Layers/ParallaxLayer"; import UILayer from "../Scene/Layers/UILayer"; @@ -106,13 +106,13 @@ export default class WebGLRenderer extends RenderingManager { } protected renderSprite(sprite: Sprite): void { - let shader = Registry.shaders.get(ShaderRegistry.SPRITE_SHADER); + let shader = RegistryManager.shaders.get(ShaderRegistry.SPRITE_SHADER); let options = this.addOptions(shader.getOptions(sprite), sprite); shader.render(this.gl, options); } protected renderAnimatedSprite(sprite: AnimatedSprite): void { - let shader = Registry.shaders.get(ShaderRegistry.SPRITE_SHADER); + let shader = RegistryManager.shaders.get(ShaderRegistry.SPRITE_SHADER); let options = this.addOptions(shader.getOptions(sprite), sprite); shader.render(this.gl, options); } @@ -120,11 +120,11 @@ export default class WebGLRenderer extends RenderingManager { protected renderGraphic(graphic: Graphic): void { if(graphic instanceof Point){ - let shader = Registry.shaders.get(ShaderRegistry.POINT_SHADER); + let shader = RegistryManager.shaders.get(ShaderRegistry.POINT_SHADER); let options = this.addOptions(shader.getOptions(graphic), graphic); shader.render(this.gl, options); } else if(graphic instanceof Rect) { - let shader = Registry.shaders.get(ShaderRegistry.RECT_SHADER); + let shader = RegistryManager.shaders.get(ShaderRegistry.RECT_SHADER); let options = this.addOptions(shader.getOptions(graphic), graphic); shader.render(this.gl, options); } @@ -136,7 +136,7 @@ export default class WebGLRenderer extends RenderingManager { protected renderUIElement(uiElement: UIElement): void { if(uiElement instanceof Label){ - let shader = Registry.shaders.get(ShaderRegistry.LABEL_SHADER); + let shader = RegistryManager.shaders.get(ShaderRegistry.LABEL_SHADER); let options = this.addOptions(shader.getOptions(uiElement), uiElement); shader.render(this.gl, options); @@ -158,7 +158,7 @@ export default class WebGLRenderer extends RenderingManager { } protected renderCustom(node: CanvasNode): void { - let shader = Registry.shaders.get(node.customShaderKey); + let shader = RegistryManager.shaders.get(node.customShaderKey); let options = this.addOptions(shader.getOptions(node), node); shader.render(this.gl, options); } diff --git a/src/Wolfie2D/ResourceManager/ResourceManager.ts b/src/Wolfie2D/ResourceManager/ResourceManager.ts index 031c644..0fe454b 100644 --- a/src/Wolfie2D/ResourceManager/ResourceManager.ts +++ b/src/Wolfie2D/ResourceManager/ResourceManager.ts @@ -69,6 +69,11 @@ export default class ResourceManager { /** The total number of "types" of things that need to be loaded (i.e. images and tilemaps) */ private loadonly_typesToLoad: number; + private loadonly_jsonLoaded: number; + private loadonly_jsonToLoad: number; + private loadonly_jsonLoadingQueue: Queue; + private jsonObjects: Map>; + /* ########## INFORMATION SPECIAL TO WEBGL ########## */ private gl_WebGLActive: boolean; @@ -109,6 +114,11 @@ export default class ResourceManager { this.loadonly_audioLoadingQueue = new Queue(); this.audioBuffers = new Map(); + this.loadonly_jsonLoaded = 0; + this.loadonly_jsonToLoad = 0; + this.loadonly_jsonLoadingQueue = new Queue(); + this.jsonObjects = new Map(); + this.loadonly_gl_ShaderProgramsLoaded = 0; this.loadonly_gl_ShaderProgramsToLoad = 0; this.loadonly_gl_ShaderLoadingQueue = new Queue(); @@ -222,6 +232,24 @@ export default class ResourceManager { return this.tilemaps.get(key); } + /** + * Loads an object from a json file. + * @param key The key to associate with the loaded object + * @param path The path to the json file to load + */ + public object(key: string, path: string){ + this.loadonly_jsonLoadingQueue.enqueue({key: key, path: path}); + } + + /** + * Retreives a loaded object + * @param key The key of the loaded object + * @returns The object data associated with the key + */ + public getObject(key: string){ + return this.jsonObjects.get(key); + } + /** * Loads all resources currently in the queue * @param callback The function to cal when the resources are finished loading @@ -240,15 +268,18 @@ export default class ResourceManager { console.log("Loaded Images"); this.loadAudioFromQueue(() => { console.log("Loaded Audio"); - - if(this.gl_WebGLActive){ - this.gl_LoadShadersFromQueue(() => { - console.log("Loaded Shaders"); + this.loadObjectsFromQueue(() => { + console.log("Loaded Objects"); + + if(this.gl_WebGLActive){ + this.gl_LoadShadersFromQueue(() => { + console.log("Loaded Shaders"); + this.finishLoading(callback); + }); + } else { this.finishLoading(callback); - }); - } else { - this.finishLoading(callback); - } + } + }) }); }); }); @@ -303,6 +334,7 @@ export default class ResourceManager { // If no items to load, we're finished if(this.loadonly_tilemapsToLoad === 0){ onFinishLoading(); + return; } while(this.loadonly_tilemapLoadingQueue.hasItems()){ @@ -368,6 +400,7 @@ export default class ResourceManager { // If no items to load, we're finished if(this.loadonly_spritesheetsToLoad === 0){ onFinishLoading(); + return; } while(this.loadonly_spritesheetLoadingQueue.hasItems()){ @@ -422,6 +455,7 @@ export default class ResourceManager { // If no items to load, we're finished if(this.loadonly_imagesToLoad === 0){ onFinishLoading(); + return; } while(this.loadonly_imageLoadingQueue.hasItems()){ @@ -479,6 +513,7 @@ export default class ResourceManager { // If no items to load, we're finished if(this.loadonly_audioToLoad === 0){ onFinishLoading(); + return; } while(this.loadonly_audioLoadingQueue.hasItems()){ @@ -527,6 +562,53 @@ export default class ResourceManager { } } + /** + * Loads all objects currently in the object loading queue + * @param onFinishLoading The function to call when there are no more objects to load + */ + private loadObjectsFromQueue(onFinishLoading: Function): void { + this.loadonly_jsonToLoad = this.loadonly_jsonLoadingQueue.getSize(); + this.loadonly_jsonLoaded = 0; + + // If no items to load, we're finished + if(this.loadonly_jsonToLoad === 0){ + onFinishLoading(); + return; + } + + while(this.loadonly_jsonLoadingQueue.hasItems()){ + let obj = this.loadonly_jsonLoadingQueue.dequeue(); + this.loadObject(obj.key, obj.path, onFinishLoading); + } + } + + /** + * Loads a singular object + * @param key The key of the object to load + * @param path The path to the object to load + * @param callbackIfLast The function to call if this is the last object + */ + public loadObject(key: string, path: string, callbackIfLast: Function): void { + this.loadTextFile(path, (fileText: string) => { + let obj = JSON.parse(fileText); + this.jsonObjects.add(key, obj); + this.finishLoadingObject(callbackIfLast); + }); + } + + /** + * Finish loading an object. If this is the last object, it calls the callback function + * @param callback The function to call if this is the last object + */ + private finishLoadingObject(callback: Function): void { + this.loadonly_jsonLoaded += 1; + + if(this.loadonly_jsonLoaded === this.loadonly_jsonToLoad){ + // We're done loading objects + callback(); + } + } + /* ########## WEBGL SPECIFIC FUNCTIONS ########## */ public getTexture(key: string): number { @@ -631,6 +713,7 @@ export default class ResourceManager { // If webGL isn'active or there are no items to load, we're finished if(!this.gl_WebGLActive || this.loadonly_gl_ShaderProgramsToLoad === 0){ onFinishLoading(); + return; } while(this.loadonly_gl_ShaderLoadingQueue.hasItems()){ diff --git a/src/Wolfie2D/Scene/Factories/CanvasNodeFactory.ts b/src/Wolfie2D/Scene/Factories/CanvasNodeFactory.ts index 6d2056b..2c90295 100644 --- a/src/Wolfie2D/Scene/Factories/CanvasNodeFactory.ts +++ b/src/Wolfie2D/Scene/Factories/CanvasNodeFactory.ts @@ -13,6 +13,7 @@ import Slider from "../../Nodes/UIElements/Slider"; import TextInput from "../../Nodes/UIElements/TextInput"; import Rect from "../../Nodes/Graphics/Rect"; import ResourceManager from "../../ResourceManager/ResourceManager"; +import Line from "../../Nodes/Graphics/Line"; // @ignorePage @@ -135,10 +136,13 @@ export default class CanvasNodeFactory { switch(type){ case GraphicType.POINT: instance = this.buildPoint(options); - break; + break; + case GraphicType.LINE: + instance = this.buildLine(options); + break; case GraphicType.RECT: instance = this.buildRect(options); - break; + break; default: throw `GraphicType '${type}' does not exist, or is registered incorrectly.` } @@ -191,6 +195,13 @@ export default class CanvasNodeFactory { return new Point(options.position); } + buildLine(options?: Record): Point { + this.checkIfPropExists("Line", options, "start", Vec2, "Vec2"); + this.checkIfPropExists("Line", options, "end", Vec2, "Vec2"); + + return new Line(options.start, options.end); + } + buildRect(options?: Record): Rect { this.checkIfPropExists("Rect", options, "position", Vec2, "Vec2"); this.checkIfPropExists("Rect", options, "size", Vec2, "Vec2"); diff --git a/src/Wolfie2D/Scene/Layer.ts b/src/Wolfie2D/Scene/Layer.ts index d529f35..532685e 100644 --- a/src/Wolfie2D/Scene/Layer.ts +++ b/src/Wolfie2D/Scene/Layer.ts @@ -159,6 +159,24 @@ export default class Layer { node.setLayer(this); } + /** + * Removes a node from this layer + * @param node The node to remove + * @returns true if the node was removed, false otherwise + */ + removeNode(node: GameNode): boolean { + // Find and remove the node + for(let i = 0; i < this.items.length; i++){ + if(this.items[i].id === node.id){ + this.items.splice(i, 1); + node.setLayer(null); + return true; + } + } + + return false; + } + /** * Retreives all GameNodes from this layer * @returns an Array that contains all of the GameNodes in this layer. diff --git a/src/Wolfie2D/Scene/Scene.ts b/src/Wolfie2D/Scene/Scene.ts index 0b2f506..8a098df 100644 --- a/src/Wolfie2D/Scene/Scene.ts +++ b/src/Wolfie2D/Scene/Scene.ts @@ -23,6 +23,7 @@ import GameNode from "../Nodes/GameNode"; import SceneOptions from "./SceneOptions"; import RenderingManager from "../Rendering/RenderingManager"; import Debug from "../Debug/Debug"; +import TimerManager from "../Timing/TimerManager"; /** * Scenes are the main container in the game engine. @@ -119,6 +120,9 @@ export default class Scene implements Updateable { this.add = new FactoryManager(this, this.tilemaps); this.load = ResourceManager.getInstance(); + + // Get the timer manager and clear any existing timers + TimerManager.getInstance().clearTimers(); } /** A lifecycle method that gets called immediately after a new scene is created, before anything else. */ @@ -142,6 +146,9 @@ export default class Scene implements Updateable { update(deltaT: number): void { this.updateScene(deltaT); + // Do time updates + TimerManager.getInstance().update(deltaT); + // Do all AI updates this.aiManager.update(deltaT); diff --git a/src/Wolfie2D/Timing/Timer.ts b/src/Wolfie2D/Timing/Timer.ts new file mode 100644 index 0000000..dd52f67 --- /dev/null +++ b/src/Wolfie2D/Timing/Timer.ts @@ -0,0 +1,80 @@ +import Updateable from "../DataTypes/Interfaces/Updateable"; +import MathUtils from "../Utils/MathUtils"; +import TimerManager from "./TimerManager"; + +export default class Timer implements Updateable { + + protected state: TimerState; + protected onEnd: Function; + protected loop: boolean; + protected totalTime: number; + protected timeLeft: number; + + constructor(time: number, onEnd?: Function, loop: boolean = false){ + // Register this timer + TimerManager.getInstance().addTimer(this); + + this.totalTime = time; + this.timeLeft = 0; + this.onEnd = onEnd; + this.loop = loop; + this.state = TimerState.STOPPED; + } + + isStopped(){ + return this.state === TimerState.STOPPED; + } + + isPaused(){ + return this.state === TimerState.PAUSED; + } + + start(time?: number){ + if(time !== undefined){ + this.totalTime = time; + } + this.state = TimerState.ACTIVE; + this.timeLeft = this.totalTime; + } + + pause(): void { + this.state = TimerState.PAUSED; + } + + update(deltaT: number){ + if(this.state === TimerState.ACTIVE){ + this.timeLeft -= deltaT*1000; + + if(this.timeLeft <= 0){ + this.timeLeft = MathUtils.clampLow0(this.timeLeft); + this.end(); + } + } + } + + protected end(){ + // Update the state + this.state = TimerState.STOPPED; + + // Call the end function if there is one + if(this.onEnd){ + this.onEnd(); + } + + // Loop if we want to + if(this.loop){ + this.state = TimerState.ACTIVE; + this.timeLeft = this.totalTime; + } + } + + toString(): string{ + return "Timer: " + this.state + " - Time Left: " + this.timeLeft + "ms of " + this.totalTime + "ms"; + } +} + +export enum TimerState { + ACTIVE = "ACTIVE", + PAUSED = "PAUSED", + STOPPED = "STOPPED" +} \ No newline at end of file diff --git a/src/Wolfie2D/Timing/TimerManager.ts b/src/Wolfie2D/Timing/TimerManager.ts new file mode 100644 index 0000000..6ac83d1 --- /dev/null +++ b/src/Wolfie2D/Timing/TimerManager.ts @@ -0,0 +1,33 @@ +import Updateable from "../DataTypes/Interfaces/Updateable"; +import Timer from "./Timer"; + +export default class TimerManager implements Updateable { + + protected timers: Array; + + constructor(){ + this.timers = new Array(); + } + + protected static instance: TimerManager; + + static getInstance(): TimerManager { + if(!this.instance){ + this.instance = new TimerManager(); + } + + return this.instance; + } + + addTimer(timer: Timer){ + this.timers.push(timer); + } + + clearTimers(){ + this.timers = new Array(); + } + + update(deltaT: number): void { + this.timers.forEach(timer => timer.update(deltaT)); + } +} \ No newline at end of file diff --git a/src/Wolfie2D/Utils/Color.ts b/src/Wolfie2D/Utils/Color.ts index 2bbbce9..e7f89d0 100644 --- a/src/Wolfie2D/Utils/Color.ts +++ b/src/Wolfie2D/Utils/Color.ts @@ -185,4 +185,13 @@ export default class Color { this.a ]); } + + static fromStringHex(str: string): Color { + let i = 0; + if(str.charAt(0) == "#") i+= 1; + let r = MathUtils.fromHex(str.substring(i, i+2)); + let g = MathUtils.fromHex(str.substring(i+2, i+4)); + let b = MathUtils.fromHex(str.substring(i+4, i+6)); + return new Color(r, g, b); + } } \ No newline at end of file diff --git a/src/Wolfie2D/Utils/MathUtils.ts b/src/Wolfie2D/Utils/MathUtils.ts index 1b4c0c8..edd1def 100644 --- a/src/Wolfie2D/Utils/MathUtils.ts +++ b/src/Wolfie2D/Utils/MathUtils.ts @@ -49,6 +49,25 @@ export default class MathUtils { return MathUtils.clamp(x, 0, 1); } + /** + * Clamps the lower end of the value of x to the range to min + * @param x The value to be clamped + * @param min The minimum allowed value of x + * @returns x, if it is greater than min, otherwise min + */ + static clampLow(x: number, min: number): number { + return x < min ? min : x; + } + + /** + * Clamps the lower end of the value of x to zero + * @param x The value to be clamped + * @returns x, if it is greater than 0, otherwise 0 + */ + static clampLow0(x: number): number { + return MathUtils.clampLow(x, 0); + } + static clampMagnitude(v: Vec2, m: number): Vec2 { if(v.magSq() > m*m){ return v.scaleTo(m); @@ -104,6 +123,15 @@ export default class MathUtils { } + /** + * Returns a number from a hex string + * @param str the string containing the hex number + * @returns the number in decimal represented by the hex string + */ + static fromHex(str: string): number { + return parseInt(str, 16); + } + /** * Returns the number as a hexadecimal * @param num The number to convert to hex