diff --git a/src/DataTypes/Interfaces/Descriptors.ts b/src/DataTypes/Interfaces/Descriptors.ts index 671a839..3892c4a 100644 --- a/src/DataTypes/Interfaces/Descriptors.ts +++ b/src/DataTypes/Interfaces/Descriptors.ts @@ -13,6 +13,9 @@ export interface Unique { export interface Positioned { /** The center of this object. */ position: Vec2; + + /** The center of this object relative to the viewport. */ + readonly relativePosition: Vec2; } export interface Region { @@ -22,6 +25,9 @@ export interface Region { /** The scale of this object. */ scale: Vec2; + /** The size of the object taking into account the zoom and scale */ + readonly sizeWithZoom: Vec2; + /** The bounding box of this object. */ boundary: AABB; } @@ -95,13 +101,13 @@ export interface Physical { * Tells the physics engine to handle a move by this object. * @param velocity The velocity with which to move the object. */ - move: (velocity: Vec2) => void; + move(velocity: Vec2): void; /** * The move actually done by the physics engine after collision checks are done. * @param velocity The velocity with which the object will move. */ - finishMove: () => void; + finishMove(): void; /** * Adds physics to this object @@ -109,20 +115,20 @@ export interface Physical { * @param isCollidable Whether this object will be able to collide with other objects * @param isStatic Whether this object will be static or not */ - addPhysics: (collisionShape?: Shape, colliderOffset?: Vec2, isCollidable?: boolean, isStatic?: boolean) => void; + addPhysics(collisionShape?: Shape, colliderOffset?: Vec2, isCollidable?: boolean, isStatic?: boolean): void; /** * Adds a trigger to this object for a specific group * @param group The name of the group that activates the trigger * @param eventType The name of the event to send when this trigger is activated */ - addTrigger: (group: string, eventType: string) => void; + addTrigger(group: string, eventType: string): void; /** * Sets the physics layer of this node * @param layer The name of the layer */ - setPhysicsLayer: (layer: String) => void; + setPhysicsLayer(layer: string): void; /** * If used before "move()", it will tell you the velocity of the node after its last movement @@ -148,22 +154,41 @@ export interface Actor { /** The id of the actor according to the AIManager */ actorId: number; + /** The path that navigation will follow */ path: NavigationPath; + /** A flag representing whether or not the actor is currently pathfinding */ pathfinding: boolean; - addAI: (ai: string | (new () => T), options: Record) => void; + /** + * Adds an AI to this Actor. + * @param ai The name of the AI, or the actual AI, to add to the Actor. + * @param options The options to give to the AI for initialization. + */ + addAI(ai: string | (new () => T), options: Record): void; - setAIActive: (active: boolean) => void; + /** + * Sets the AI to start/stop for this Actor. + * @param active The new active status of the AI. + */ + setAIActive(active: boolean): void; } export interface Navigable { - getNavigationPath: (fromPosition: Vec2, toPosition: Vec2) => NavigationPath; + /** + * Gets a new navigation path based on this Navigable object. + * @param fromPosition The position to start navigation from. + * @param toPosition The position to navigate to. + */ + getNavigationPath(fromPosition: Vec2, toPosition: Vec2): NavigationPath; } export interface Updateable { - /** Updates this object. */ - update: (deltaT: number) => void; + /** + * Updates this object. + * @param deltaT The timestep of the update. + */ + update(deltaT: number): void; } export interface DebugRenderable { diff --git a/src/DataTypes/Vec2.ts b/src/DataTypes/Vec2.ts index 299aeca..7119cee 100644 --- a/src/DataTypes/Vec2.ts +++ b/src/DataTypes/Vec2.ts @@ -6,6 +6,7 @@ import MathUtils from "../Utils/MathUtils"; export default class Vec2 { // Store x and y in an array + /** The array that stores the actual vector values */ private vec: Float32Array; /** @@ -13,6 +14,11 @@ export default class Vec2 { */ private onChange: Function = () => {}; + /** + * Creates a new Vec2 + * @param x The x value of the vector + * @param y The y value of the vector + */ constructor(x: number = 0, y: number = 0) { this.vec = new Float32Array(2); this.vec[0] = x; @@ -58,6 +64,18 @@ export default class Vec2 { return new Vec2(0, -1); } + static get DOWN() { + return new Vec2(0, 1); + } + + static get LEFT() { + return new Vec2(-1, 0); + } + + static get RIGHT() { + return new Vec2(1, 0); + } + /** * The squared magnitude of the vector */ @@ -86,7 +104,7 @@ export default class Vec2 { /** * Returns a new vector that is the normalized version of this one */ - normalized(){ + normalized(): Vec2 { let mag = this.mag(); return new Vec2(this.x/mag, this.y/mag); } @@ -94,7 +112,7 @@ export default class Vec2 { /** * Sets the x and y elements of this vector to zero */ - zero(){ + zero(): Vec2 { return this.set(0, 0); } @@ -335,10 +353,19 @@ export default class Vec2 { 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 + * @param a The first vector + * @param b The second vector + * @param t The time of the lerp, with 0 being vector A, and 1 being vector B + */ 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)); } diff --git a/src/Nodes/CanvasNode.ts b/src/Nodes/CanvasNode.ts index 49f60db..6aa42eb 100644 --- a/src/Nodes/CanvasNode.ts +++ b/src/Nodes/CanvasNode.ts @@ -13,8 +13,9 @@ export default abstract class CanvasNode extends GameNode implements Region { private _scale: Vec2; private _boundary: AABB; - visible = true; - + /** A flag for whether or not the CanvasNode is visible */ + visible: boolean = true; + constructor(){ super(); this._size = new Vec2(0, 0); @@ -55,19 +56,24 @@ export default abstract class CanvasNode extends GameNode implements Region { this.scale.y = value; } + // @override protected positionChanged(): void { super.positionChanged(); this.updateBoundary(); } + /** Called if the size vector is changed or replaced. */ protected sizeChanged(): void { this.updateBoundary(); } + /** Called if the scale vector is changed or replaced */ protected scaleChanged(): void { this.updateBoundary(); } + // @docIgnore + /** Called if the position, size, or scale of the CanvasNode is changed. Updates the boundary. */ private updateBoundary(): void { this._boundary.center.set(this.position.x, this.position.y); this._boundary.halfSize.set(this.size.x*this.scale.x/2, this.size.y*this.scale.y/2); @@ -85,13 +91,15 @@ export default abstract class CanvasNode extends GameNode implements Region { /** * Returns true if the point (x, y) is inside of this canvas object - * @param x - * @param y + * @param x The x position of the point + * @param y The y position of the point + * @returns A flag representing whether or not this node contains the point. */ contains(x: number, y: number): boolean { return this._boundary.containsPoint(new Vec2(x, y)); } + // @implemented debugRender(): void { super.debugRender(); let color = this.isColliding ? Color.RED : Color.GREEN; diff --git a/src/Nodes/GameNode.ts b/src/Nodes/GameNode.ts index d263192..9072ea8 100644 --- a/src/Nodes/GameNode.ts +++ b/src/Nodes/GameNode.ts @@ -14,7 +14,8 @@ import Debug from "../Debug/Debug"; import Color from "../Utils/Color"; /** - * The representation of an object in the game world + * The representation of an object in the game world. + * To construct GameNodes, see the @reference[Scene] documentation. */ export default abstract class GameNode implements Positioned, Unique, Updateable, Physical, Actor, DebugRenderable { /*---------- POSITIONED ----------*/ @@ -52,15 +53,24 @@ export default abstract class GameNode implements Positioned, Unique, Updateable pathfinding: boolean = false; /*---------- GENERAL ----------*/ + /** An reference to the user input handler. This allows subclasses to easily access information about user input. */ protected input: InputReceiver; + /** An event receiver. */ protected receiver: Receiver; + /** An event emitter. */ protected emitter: Emitter; + /** A reference to the scene this GameNode is a part of. */ protected scene: Scene; + /** The visual layer this GameNode resides in. */ protected layer: Layer; + /** A utility that allows the use of tweens on this GameNode */ tweens: TweenManager; + /** A tweenable property for rotation. Does not affect the bounding box of this GameNode - Only rendering. */ rotation: number; + /** The opacity value of this GameNode */ alpha: number; + // Constructor docs are ignored, as the user should NOT create new GameNodes with a raw constructor constructor(){ this.input = InputReceiver.getInstance(); this._position = new Vec2(0, 0); @@ -105,18 +115,20 @@ export default abstract class GameNode implements Positioned, Unique, Updateable } /*---------- PHYSICAL ----------*/ + // @implemented /** * @param velocity The velocity with which to move the object. */ - move = (velocity: Vec2): void => { + move(velocity: Vec2): void { this.moving = true; this._velocity = velocity; }; + // @implemented /** * @param velocity The velocity with which the object will move. */ - finishMove = (): void => { + finishMove(): void { this.moving = false; this.position.add(this._velocity); if(this.pathfinding){ @@ -124,13 +136,15 @@ export default abstract class GameNode implements Positioned, Unique, Updateable } } + // @implemented /** * @param collisionShape The collider for this object. If this has a region (implements Region), * it will be used when no collision shape is specified (or if collision shape is null). * @param isCollidable Whether this is collidable or not. True by default. * @param isStatic Whether this is static or not. False by default */ - addPhysics = (collisionShape?: Shape, colliderOffset?: Vec2, isCollidable: boolean = true, isStatic: boolean = false): void => { + addPhysics(collisionShape?: Shape, colliderOffset?: Vec2, isCollidable: boolean = true, isStatic: boolean = false): void { + // Initialize the physics variables this.hasPhysics = true; this.moving = false; this.onGround = false; @@ -147,6 +161,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this.collidedWithTilemap = false; this.physicsLayer = -1; + // Set the collision shape if provided, or simply use the the region if there is one. if(collisionShape){ this.collisionShape = collisionShape; } else if (isRegion(this)) { @@ -156,29 +171,39 @@ export default abstract class GameNode implements Positioned, Unique, Updateable throw "No collision shape specified for physics object." } + // If we were provided with a collider offset, set it. Otherwise there is no offset, so use the zero vector if(colliderOffset){ this.colliderOffset = colliderOffset; } else { this.colliderOffset = Vec2.ZERO; } + // Initialize the swept rect this.sweptRect = this.collisionShape.getBoundingRect(); + + // Register the object with physics this.scene.getPhysicsManager().registerObject(this); } + // @implemented /** * @param group The name of the group that will activate the trigger * @param eventType The type of this event to send when this trigger is activated */ - addTrigger = (group: string, eventType: string): void => { + addTrigger(group: string, eventType: string): void { this.isTrigger = true; this.triggers.add(group, eventType); }; - setPhysicsLayer = (layer: string): void => { + // @implemented + /** + * @param layer The physics layer this node should belong to + */ + setPhysicsLayer(layer: string): void { this.scene.getPhysicsManager().setLayer(this, layer); } + // @implemened getLastVelocity(): Vec2 { return this._velocity; } @@ -198,6 +223,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this.aiActive = true; } + // @implemented addAI(ai: string | (new () => T), options?: Record): void { if(!this._ai){ this.scene.getAIManager().registerActor(this); @@ -214,6 +240,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this.aiActive = true; } + // @implemented setAIActive(active: boolean): void { this.aiActive = active; } @@ -240,7 +267,10 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this.scene = scene; } - /** Gets the scene this object is in. */ + /** + * Gets the scene this object is in. + * @returns The scene this object belongs to + */ getScene(): Scene { return this.scene; } @@ -253,24 +283,30 @@ export default abstract class GameNode implements Positioned, Unique, Updateable this.layer = layer; } - /** Returns the layer this object is on. */ + /** + * Returns the layer this object is on. + * @returns This layer this object is on. + */ getLayer(): Layer { return this.layer; } - /** - * Called if the position vector is modified or replaced - */ + /** Called if the position vector is modified or replaced */ protected positionChanged(): void { if(this.hasPhysics){ this.collisionShape.center = this.position.clone().add(this.colliderOffset); } }; + /** + * Updates this GameNode + * @param deltaT The timestep of the update. + */ update(deltaT: number): void { this.tweens.update(deltaT); } + // @implemented debugRender(): void { let color = this.isColliding ? Color.RED : Color.GREEN; Debug.drawPoint(this.relativePosition, color); diff --git a/src/Nodes/Graphic.ts b/src/Nodes/Graphic.ts index 6aa4cc2..c30c8ee 100644 --- a/src/Nodes/Graphic.ts +++ b/src/Nodes/Graphic.ts @@ -5,7 +5,7 @@ import Color from "../Utils/Color"; * The representation of a game object that doesn't rely on any resources to render - it is drawn to the screen by the canvas */ export default abstract class Graphic extends CanvasNode { - + /** The color of the Graphic */ color: Color; constructor(){ @@ -13,6 +13,11 @@ export default abstract class Graphic extends CanvasNode { this.color = Color.RED; } + // @deprecated + /** + * Sets the color of the Graphic. DEPRECATED + * @param color The new color of the Graphic. + */ setColor(color: Color){ this.color = color; } diff --git a/src/Nodes/Graphics/Point.ts b/src/Nodes/Graphics/Point.ts index 3a81e40..0f75c5a 100644 --- a/src/Nodes/Graphics/Point.ts +++ b/src/Nodes/Graphics/Point.ts @@ -1,6 +1,7 @@ import Graphic from "../Graphic"; import Vec2 from "../../DataTypes/Vec2"; +/** A basic point to be drawn on the screen. */ export default class Point extends Graphic { constructor(position: Vec2){ diff --git a/src/Nodes/Graphics/Rect.ts b/src/Nodes/Graphics/Rect.ts index 4ea2c54..7bac72a 100644 --- a/src/Nodes/Graphics/Rect.ts +++ b/src/Nodes/Graphics/Rect.ts @@ -2,10 +2,14 @@ import Graphic from "../Graphic"; import Vec2 from "../../DataTypes/Vec2"; import Color from "../../Utils/Color"; +/** A basic rectangle to be drawn on the screen. */ export default class Rect extends Graphic { + /** The border color of the Rect */ borderColor: Color; - protected borderWidth: number; + + /** The width of the border */ + borderWidth: number; constructor(position: Vec2, size: Vec2){ super(); @@ -19,16 +23,17 @@ export default class Rect extends Graphic { * Sets the border color of this rectangle * @param color The border color */ - setBorderColor(color: Color){ + setBorderColor(color: Color): void { this.borderColor = color; } + // @deprecated getBorderColor(): Color { return this.borderColor; } - /**Sets the border width of this rectangle - * + /** + * Sets the border width of this rectangle * @param width The width of the rectangle in pixels */ setBorderWidth(width: number){ diff --git a/src/Nodes/Sprites/AnimatedSprite.ts b/src/Nodes/Sprites/AnimatedSprite.ts index a5866d8..ae4ffef 100644 --- a/src/Nodes/Sprites/AnimatedSprite.ts +++ b/src/Nodes/Sprites/AnimatedSprite.ts @@ -3,6 +3,7 @@ import AnimationManager from "../../Rendering/Animations/AnimationManager"; import Spritesheet from "../../DataTypes/Spritesheet"; import Vec2 from "../../DataTypes/Vec2"; +/** An sprite with specified animation frames. */ export default class AnimatedSprite extends Sprite { /** The number of columns in this sprite sheet */ protected numCols: number; @@ -29,6 +30,11 @@ export default class AnimatedSprite extends Sprite { } } + /** + * Gets the image offset for the current index of animation + * @param index The index we're at in the animation + * @returns A Vec2 containing the image offset + */ getAnimationOffset(index: number): Vec2 { return new Vec2((index % this.numCols) * this.size.x, Math.floor(index / this.numCols) * this.size.y); } diff --git a/src/Nodes/Sprites/Sprite.ts b/src/Nodes/Sprites/Sprite.ts index 73bc98d..4184e1b 100644 --- a/src/Nodes/Sprites/Sprite.ts +++ b/src/Nodes/Sprites/Sprite.ts @@ -6,9 +6,13 @@ import Vec2 from "../../DataTypes/Vec2"; * The representation of a sprite - an in-game image */ export default class Sprite extends CanvasNode { + /** The id of the image from the resourceManager */ imageId: string; + /** The offset of the sprite in an atlas image */ imageOffset: Vec2; + /** Whether or not the x-axis should be inverted on render */ invertX: boolean; + /** Whether or not the y-axis should be inverted on render */ invertY: boolean; constructor(imageId: string){ @@ -23,7 +27,7 @@ export default class Sprite extends CanvasNode { /** * Sets the offset of the sprite from (0, 0) in the image's coordinates - * @param offset + * @param offset The offset of the sprite from (0, 0) in image coordinates */ setImageOffset(offset: Vec2): void { this.imageOffset = offset; diff --git a/src/Nodes/Tilemap.ts b/src/Nodes/Tilemap.ts index 3cdb656..52fa107 100644 --- a/src/Nodes/Tilemap.ts +++ b/src/Nodes/Tilemap.ts @@ -7,10 +7,19 @@ import CanvasNode from "./CanvasNode"; * The representation of a tilemap - this can consist of a combination of tilesets in one layer */ export default abstract class Tilemap extends CanvasNode { + /** An array of the tilesets that this tilemap uses */ protected tilesets: Array; + + /** The size of a tile in this tilemap */ protected tileSize: Vec2; + + /** An array of tile data */ protected data: Array; + + /** An array of tile collision data */ protected collisionMap: Array; + + /** The name of the tilemap */ name: string; // TODO: Make this no longer be specific to Tiled @@ -37,6 +46,7 @@ export default abstract class Tilemap extends CanvasNode { /** * Returns an array of the tilesets associated with this tilemap + * @returns An array of all of the tilesets assocaited with this tilemap. */ getTilesets(): Tileset[] { return this.tilesets; @@ -44,18 +54,25 @@ export default abstract class Tilemap extends CanvasNode { /** * Returns the size of tiles in this tilemap as they appear in the game world after scaling + * @returns A vector containing the size of tiles in this tilemap as they appear in the game world after scaling. */ getTileSize(): Vec2 { return this.tileSize.scaled(this.scale.x, this.scale.y); } + /** + * Gets the tile size taking zoom into account + * @returns The tile size with zoom + */ getTileSizeWithZoom(): Vec2 { let zoom = this.scene.getViewScale(); return this.getTileSize().scale(zoom); } - /** Adds this tilemap to the physics system */ + /** + * Adds this tilemap to the physics system + */ addPhysics = (): void => { this.scene.getPhysicsManager().registerTilemap(this); } @@ -63,18 +80,21 @@ export default abstract class Tilemap extends CanvasNode { /** * Returns the value of the tile at the specified position * @param worldCoords The position in world coordinates + * @returns A number that represents the data value of the tile at the specified world position. */ abstract getTileAtWorldPosition(worldCoords: Vec2): number; /** * Returns the world position of the top left corner of the tile at the specified index - * @param index + * @param index The index of the tile in the tileData array + * @returns The world position of the tile at the specified index */ abstract getTileWorldPosition(index: number): Vec2; /** * Returns the value of the tile at the specified index - * @param index + * @param index The index of the tile in the tileData array + * @returns The value of the tile in the tileData array */ abstract getTile(index: number): number; @@ -85,9 +105,11 @@ export default abstract class Tilemap extends CanvasNode { */ abstract setTile(index: number, type: number): void; + // TODO: This shouldn't use tiled data specifically - it should be more general /** * Sets up the tileset using the data loaded from file + * @param tilemapData The tilemap data from file + * @param layer The layer data from file */ - // TODO: This shouldn't use tiled data specifically - it should be more general protected abstract parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void; } \ No newline at end of file diff --git a/src/Nodes/Tilemaps/OrthogonalTilemap.ts b/src/Nodes/Tilemaps/OrthogonalTilemap.ts index a439c15..f842913 100644 --- a/src/Nodes/Tilemaps/OrthogonalTilemap.ts +++ b/src/Nodes/Tilemaps/OrthogonalTilemap.ts @@ -8,15 +8,12 @@ import Color from "../../Utils/Color"; * The representation of an orthogonal tilemap - i.e. a top down or platformer tilemap */ export default class OrthogonalTilemap extends Tilemap { - + /** The number of columns in the tilemap */ protected numCols: number; + /** The number of rows in the tilemap */ protected numRows: number; - /** - * Parses the tilemap data loaded from the json file. DOES NOT process images automatically - the ResourceManager class does this while loading tilemaps - * @param tilemapData - * @param layer - */ + // @override protected parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void { // The size of the tilemap in local space this.numCols = tilemapData.width; @@ -47,10 +44,19 @@ export default class OrthogonalTilemap extends Tilemap { } } + /** + * Gets the dimensions of the tilemap + * @returns A Vec2 containing the number of columns and the number of rows in the tilemap. + */ getDimensions(): Vec2 { return new Vec2(this.numCols, this.numRows); } + /** + * Gets the data value of the tile at the specified world position + * @param worldCoords The coordinates in world space + * @returns The data value of the tile + */ getTileAtWorldPosition(worldCoords: Vec2): number { let localCoords = this.getColRowAt(worldCoords); return this.getTileAtRowCol(localCoords); @@ -58,7 +64,8 @@ export default class OrthogonalTilemap extends Tilemap { /** * Get the tile at the specified row and column - * @param rowCol + * @param rowCol The coordinates in tilemap space + * @returns The data value of the tile */ getTileAtRowCol(rowCol: Vec2): number { if(rowCol.x < 0 || rowCol.x >= this.numCols || rowCol.y < 0 || rowCol.y >= this.numRows){ @@ -68,6 +75,11 @@ export default class OrthogonalTilemap extends Tilemap { return this.data[rowCol.y * this.numCols + rowCol.x]; } + /** + * Gets the world position of the tile at the specified index + * @param index The index of the tile + * @returns A Vec2 containing the world position of the tile + */ getTileWorldPosition(index: number): Vec2 { // Get the local position let col = index % this.numCols; @@ -80,14 +92,29 @@ export default class OrthogonalTilemap extends Tilemap { return new Vec2(x, y); } + /** + * Gets the data value of the tile at the specified index + * @param index The index of the tile + * @returns The data value of the tile + */ getTile(index: number): number { return this.data[index]; } + /** + * 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 { this.data[index] = type; } + /** + * Sets the tile at the specified row and column + * @param rowCol The position of the tile in tilemap space + * @param type The new data value of the tile + */ setTileAtRowCol(rowCol: Vec2, type: number): void { let index = rowCol.y * this.numCols + rowCol.x; this.setTile(index, type); @@ -95,8 +122,9 @@ export default class OrthogonalTilemap extends Tilemap { /** * Returns true if the tile at the specified row and column of the tilemap is collidable - * @param indexOrCol - * @param row + * @param indexOrCol The index of the tile or the column it is in + * @param row The row the tile is in + * @returns A flag representing whether or not the tile is collidable. */ isTileCollidable(indexOrCol: number, row?: number): boolean { // The value of the tile @@ -123,7 +151,8 @@ export default class OrthogonalTilemap extends Tilemap { /** * Takes in world coordinates and returns the row and column of the tile at that position - * @param worldCoords + * @param worldCoords The coordinates of the potential tile in world space + * @returns A Vec2 containing the coordinates of the potential tile in tilemap space */ getColRowAt(worldCoords: Vec2): Vec2 { let col = Math.floor(worldCoords.x / this.tileSize.x / this.scale.x); @@ -132,8 +161,10 @@ export default class OrthogonalTilemap extends Tilemap { return new Vec2(col, row); } + // @override update(deltaT: number): void {} + // @override debugRender(){ let tileSize = this.getTileSizeWithZoom(); let origin = this.relativePosition.sub(this.sizeWithZoom); diff --git a/src/Nodes/UIElement.ts b/src/Nodes/UIElement.ts index 03256b3..fb5fafb 100644 --- a/src/Nodes/UIElement.ts +++ b/src/Nodes/UIElement.ts @@ -6,24 +6,39 @@ import Vec2 from "../DataTypes/Vec2"; * The representation of a UIElement - the parent class of things like buttons */ export default abstract class UIElement extends CanvasNode { - // Style attributes + // Style attributes - TODO - abstract this into a style object/interface + /** The backgound color */ backgroundColor: Color; + /** The border color */ borderColor: Color; + /** The border radius */ borderRadius: number; + /** The border width */ borderWidth: number; + /** The padding */ padding: Vec2; // EventAttributes + /** The reaction of this UIElement on a click */ onClick: Function; + /** The event propagated on click */ onClickEventId: string; + /** The reaction to the release of a click */ onRelease: Function; + /** The event propagated on the release of a click */ onReleaseEventId: string; + /** The reaction when a mouse enters this UIElement */ onEnter: Function; + /** The event propagated when a mouse enters this UIElement */ onEnterEventId: string; + /** The reaction when a mouse leaves this UIElement */ onLeave: Function; + /** The event propogated when a mouse leaves this UIElement */ onLeaveEventId: string; + /** Whether or not this UIElement is currently clicked on */ protected isClicked: boolean; + /** Whether or not this UIElement is currently hovered over */ protected isEntered: boolean; constructor(position: Vec2){ @@ -50,10 +65,12 @@ export default abstract class UIElement extends CanvasNode { this.isEntered = false; } + // @deprecated setBackgroundColor(color: Color): void { this.backgroundColor = color; } + // @deprecated setPadding(padding: Vec2): void { this.padding.copy(padding); } @@ -116,6 +133,7 @@ export default abstract class UIElement extends CanvasNode { /** * Overridable method for calculating background color - useful for elements that want to be colored on different after certain events + * @returns The background color of the UIElement */ calculateBackgroundColor(): string { return this.backgroundColor.toStringRGBA(); @@ -123,6 +141,7 @@ export default abstract class UIElement extends CanvasNode { /** * Overridable method for calculating border color - useful for elements that want to be colored on different after certain events + * @returns The border color of the UIElement */ calculateBorderColor(): string { return this.borderColor.toStringRGBA(); diff --git a/src/Nodes/UIElements/Button.ts b/src/Nodes/UIElements/Button.ts index 4556773..4b9db1f 100644 --- a/src/Nodes/UIElements/Button.ts +++ b/src/Nodes/UIElements/Button.ts @@ -2,6 +2,7 @@ import Label from "./Label"; import Color from "../../Utils/Color"; import Vec2 from "../../DataTypes/Vec2"; +/** A clickable button UIElement */ export default class Button extends Label { constructor(position: Vec2, text: string){ @@ -12,6 +13,7 @@ export default class Button extends Label { this.textColor = new Color(255, 255, 255); } + // @override calculateBackgroundColor(): string { // Change the background color if clicked or hovered if(this.isEntered && !this.isClicked){ diff --git a/src/Nodes/UIElements/Label.ts b/src/Nodes/UIElements/Label.ts index b290fab..0e79d86 100644 --- a/src/Nodes/UIElements/Label.ts +++ b/src/Nodes/UIElements/Label.ts @@ -2,12 +2,19 @@ import Vec2 from "../../DataTypes/Vec2"; import Color from "../../Utils/Color"; import UIElement from "../UIElement"; +/** A basic text-containing label */ export default class Label extends UIElement{ + /** The color of the text of this UIElement */ textColor: Color; + /** The value of the text of this UIElement */ text: string; + /** The name of the font */ protected font: string; + /** The size of the font */ protected fontSize: number; + /** The horizontal alignment of the text within the label */ protected hAlign: string; + /** The vertical alignment of text within the label */ protected vAlign: string; /** A flag for if the width of the text has been measured on the canvas for auto width assignment */ @@ -25,32 +32,46 @@ export default class Label extends UIElement{ this.sizeAssigned = false; } + // @deprecated setText(text: string): void { this.text = text; } + // @deprecated setTextColor(color: Color): void { this.textColor = color; } - getFontString(): string { + /** + * Gets a string containg the font details for rendering + * @returns A string containing the font details + */ + protected getFontString(): string { return this.fontSize + "px " + this.font; } /** * Overridable method for calculating text color - useful for elements that want to be colored on different after certain events + * @returns a string containg the text color */ calculateTextColor(): string { return this.textColor.toStringRGBA(); } + /** + * Uses the canvas to calculate the width of the text + * @param ctx The rendering context + * @returns A number representing the rendered text width + */ protected calculateTextWidth(ctx: CanvasRenderingContext2D): number { ctx.font = this.fontSize + "px " + this.font; return ctx.measureText(this.text).width; } /** - * Calculate the offset of the text - this is useful for rendering text with different alignments + * Calculate the offset of the text - this is used for rendering text with different alignments + * @param ctx The rendering context + * @returns The offset of the text in a Vec2 */ calculateTextOffset(ctx: CanvasRenderingContext2D): Vec2 { let textWidth = this.calculateTextWidth(ctx); @@ -83,6 +104,10 @@ export default class Label extends UIElement{ this.sizeAssigned = true; } + /** + * Automatically sizes the element to the text within it + * @param ctx The rendering context + */ protected autoSize(ctx: CanvasRenderingContext2D): void { let width = this.calculateTextWidth(ctx); let height = this.fontSize; @@ -90,6 +115,10 @@ export default class Label extends UIElement{ this.sizeAssigned = true; } + /** + * Initially assigns a size to the UIElement if none is provided + * @param ctx The rendering context + */ handleInitialSizing(ctx: CanvasRenderingContext2D): void { if(!this.sizeAssigned){ this.autoSize(ctx); diff --git a/src/Nodes/UIElements/Slider.ts b/src/Nodes/UIElements/Slider.ts index f0150f3..db5fc75 100644 --- a/src/Nodes/UIElements/Slider.ts +++ b/src/Nodes/UIElements/Slider.ts @@ -3,12 +3,17 @@ import Color from "../../Utils/Color"; import MathUtils from "../../Utils/MathUtils"; import UIElement from "../UIElement"; +/** A slider UIElement */ export default class Slider extends UIElement { /** The value of the slider from [0, 1] */ protected value: number; + /** The color of the slider nib */ public nibColor: Color; + /** The color of the slider track */ public sliderColor: Color; + /** The reaction of this UIElement to a value change */ public onValueChange: (value: number) => void; + /** The event propagated by this UIElement when value changes */ public onValueChangeEventId: string; constructor(position: Vec2){ @@ -24,10 +29,15 @@ export default class Slider extends UIElement { this.size.set(200, 20); } + /** + * Retrieves the value of the slider + * @returns The value of the slider + */ getValue(): number { return this.value; } - + + /** A method called in response to the value changing */ protected valueChanged(): void { if(this.onValueChange){ this.onValueChange(this.value); diff --git a/src/Nodes/UIElements/TextInput.ts b/src/Nodes/UIElements/TextInput.ts index 198bfc7..9ba8104 100644 --- a/src/Nodes/UIElements/TextInput.ts +++ b/src/Nodes/UIElements/TextInput.ts @@ -2,8 +2,11 @@ import Vec2 from "../../DataTypes/Vec2"; import Color from "../../Utils/Color"; import Label from "./Label"; +/** A text input UIElement */ export default class TextInput extends Label { + /** A flag the represents whether the user can type in this TextInput */ focused: boolean; + /** The position of the cursor in this TextInput */ cursorCounter: number; constructor(position: Vec2){ @@ -39,7 +42,6 @@ export default class TextInput extends Label { let specialChars = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?"; let letters = "qwertyuiopasdfghjklzxcvbnm"; let mask = nums + specialChars + letters; - // THIS ISN'T ACTUALLY AN ERROR, DISREGARD keys = keys.filter(key => mask.includes(key)); let shiftPressed = this.input.isPressed("shift"); let backspacePressed = this.input.isJustPressed("backspace");