added better documentation to Nodes folder

This commit is contained in:
Joe Weaver 2021-01-05 11:31:17 -05:00
parent ea33e71619
commit 3661ee3ada
16 changed files with 284 additions and 52 deletions

View File

@ -13,6 +13,9 @@ export interface Unique {
export interface Positioned { export interface Positioned {
/** The center of this object. */ /** The center of this object. */
position: Vec2; position: Vec2;
/** The center of this object relative to the viewport. */
readonly relativePosition: Vec2;
} }
export interface Region { export interface Region {
@ -22,6 +25,9 @@ export interface Region {
/** The scale of this object. */ /** The scale of this object. */
scale: Vec2; scale: Vec2;
/** The size of the object taking into account the zoom and scale */
readonly sizeWithZoom: Vec2;
/** The bounding box of this object. */ /** The bounding box of this object. */
boundary: AABB; boundary: AABB;
} }
@ -95,13 +101,13 @@ export interface Physical {
* Tells the physics engine to handle a move by this object. * Tells the physics engine to handle a move by this object.
* @param velocity The velocity with which to move the 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. * The move actually done by the physics engine after collision checks are done.
* @param velocity The velocity with which the object will move. * @param velocity The velocity with which the object will move.
*/ */
finishMove: () => void; finishMove(): void;
/** /**
* Adds physics to this object * 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 isCollidable Whether this object will be able to collide with other objects
* @param isStatic Whether this object will be static or not * @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 * Adds a trigger to this object for a specific group
* @param group The name of the group that activates the trigger * @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 * @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 * Sets the physics layer of this node
* @param layer The name of the layer * @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 * 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 */ /** The id of the actor according to the AIManager */
actorId: number; actorId: number;
/** The path that navigation will follow */
path: NavigationPath; path: NavigationPath;
/** A flag representing whether or not the actor is currently pathfinding */
pathfinding: boolean; pathfinding: boolean;
addAI: <T extends AI>(ai: string | (new () => T), options: Record<string, any>) => 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<T extends AI>(ai: string | (new () => T), options: Record<string, any>): 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 { 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 { 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 { export interface DebugRenderable {

View File

@ -6,6 +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 */
private vec: Float32Array; private vec: Float32Array;
/** /**
@ -13,6 +14,11 @@ export default class Vec2 {
*/ */
private onChange: Function = () => {}; 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) { constructor(x: number = 0, y: number = 0) {
this.vec = new Float32Array(2); this.vec = new Float32Array(2);
this.vec[0] = x; this.vec[0] = x;
@ -58,6 +64,18 @@ export default class Vec2 {
return new Vec2(0, -1); 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 * 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 * Returns a new vector that is the normalized version of this one
*/ */
normalized(){ normalized(): Vec2 {
let mag = this.mag(); let mag = this.mag();
return new Vec2(this.x/mag, this.y/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 * Sets the x and y elements of this vector to zero
*/ */
zero(){ zero(): Vec2 {
return this.set(0, 0); return this.set(0, 0);
} }
@ -335,10 +353,19 @@ export default class Vec2 {
this.onChange = f; this.onChange = f;
} }
/**
* Gets the function that is called whenever this vector is changed
*/
getOnChange(): string { getOnChange(): string {
return this.onChange.toString(); 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 { 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));
} }

View File

@ -13,8 +13,9 @@ export default abstract class CanvasNode extends GameNode implements Region {
private _scale: Vec2; private _scale: Vec2;
private _boundary: AABB; private _boundary: AABB;
visible = true; /** A flag for whether or not the CanvasNode is visible */
visible: boolean = true;
constructor(){ constructor(){
super(); super();
this._size = new Vec2(0, 0); this._size = new Vec2(0, 0);
@ -55,19 +56,24 @@ export default abstract class CanvasNode extends GameNode implements Region {
this.scale.y = value; this.scale.y = value;
} }
// @override
protected positionChanged(): void { protected positionChanged(): void {
super.positionChanged(); super.positionChanged();
this.updateBoundary(); this.updateBoundary();
} }
/** Called if the size vector is changed or replaced. */
protected sizeChanged(): void { protected sizeChanged(): void {
this.updateBoundary(); this.updateBoundary();
} }
/** Called if the scale vector is changed or replaced */
protected scaleChanged(): void { protected scaleChanged(): void {
this.updateBoundary(); this.updateBoundary();
} }
// @docIgnore
/** Called if the position, size, or scale of the CanvasNode is changed. Updates the boundary. */
private updateBoundary(): void { private updateBoundary(): void {
this._boundary.center.set(this.position.x, this.position.y); 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); 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 * Returns true if the point (x, y) is inside of this canvas object
* @param x * @param x The x position of the point
* @param y * @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 { contains(x: number, y: number): boolean {
return this._boundary.containsPoint(new Vec2(x, y)); return this._boundary.containsPoint(new Vec2(x, y));
} }
// @implemented
debugRender(): void { debugRender(): void {
super.debugRender(); super.debugRender();
let color = this.isColliding ? Color.RED : Color.GREEN; let color = this.isColliding ? Color.RED : Color.GREEN;

View File

@ -14,7 +14,8 @@ import Debug from "../Debug/Debug";
import Color from "../Utils/Color"; 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 { export default abstract class GameNode implements Positioned, Unique, Updateable, Physical, Actor, DebugRenderable {
/*---------- POSITIONED ----------*/ /*---------- POSITIONED ----------*/
@ -52,15 +53,24 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
pathfinding: boolean = false; pathfinding: boolean = false;
/*---------- GENERAL ----------*/ /*---------- GENERAL ----------*/
/** An reference to the user input handler. This allows subclasses to easily access information about user input. */
protected input: InputReceiver; protected input: InputReceiver;
/** An event receiver. */
protected receiver: Receiver; protected receiver: Receiver;
/** An event emitter. */
protected emitter: Emitter; protected emitter: Emitter;
/** A reference to the scene this GameNode is a part of. */
protected scene: Scene; protected scene: Scene;
/** The visual layer this GameNode resides in. */
protected layer: Layer; protected layer: Layer;
/** A utility that allows the use of tweens on this GameNode */
tweens: TweenManager; tweens: TweenManager;
/** A tweenable property for rotation. Does not affect the bounding box of this GameNode - Only rendering. */
rotation: number; rotation: number;
/** The opacity value of this GameNode */
alpha: number; alpha: number;
// Constructor docs are ignored, as the user should NOT create new GameNodes with a raw constructor
constructor(){ constructor(){
this.input = InputReceiver.getInstance(); this.input = InputReceiver.getInstance();
this._position = new Vec2(0, 0); this._position = new Vec2(0, 0);
@ -105,18 +115,20 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
} }
/*---------- PHYSICAL ----------*/ /*---------- PHYSICAL ----------*/
// @implemented
/** /**
* @param velocity The velocity with which to move the object. * @param velocity The velocity with which to move the object.
*/ */
move = (velocity: Vec2): void => { move(velocity: Vec2): void {
this.moving = true; this.moving = true;
this._velocity = velocity; this._velocity = velocity;
}; };
// @implemented
/** /**
* @param velocity The velocity with which the object will move. * @param velocity The velocity with which the object will move.
*/ */
finishMove = (): void => { finishMove(): void {
this.moving = false; this.moving = false;
this.position.add(this._velocity); this.position.add(this._velocity);
if(this.pathfinding){ 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), * @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). * 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 isCollidable Whether this is collidable or not. True by default.
* @param isStatic Whether this is static or not. False 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.hasPhysics = true;
this.moving = false; this.moving = false;
this.onGround = false; this.onGround = false;
@ -147,6 +161,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
this.collidedWithTilemap = false; this.collidedWithTilemap = false;
this.physicsLayer = -1; this.physicsLayer = -1;
// Set the collision shape if provided, or simply use the the region if there is one.
if(collisionShape){ if(collisionShape){
this.collisionShape = collisionShape; this.collisionShape = collisionShape;
} else if (isRegion(this)) { } 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." 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){ if(colliderOffset){
this.colliderOffset = colliderOffset; this.colliderOffset = colliderOffset;
} else { } else {
this.colliderOffset = Vec2.ZERO; this.colliderOffset = Vec2.ZERO;
} }
// Initialize the swept rect
this.sweptRect = this.collisionShape.getBoundingRect(); this.sweptRect = this.collisionShape.getBoundingRect();
// Register the object with physics
this.scene.getPhysicsManager().registerObject(this); this.scene.getPhysicsManager().registerObject(this);
} }
// @implemented
/** /**
* @param group The name of the group that will activate the trigger * @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 * @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.isTrigger = true;
this.triggers.add(group, eventType); 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); this.scene.getPhysicsManager().setLayer(this, layer);
} }
// @implemened
getLastVelocity(): Vec2 { getLastVelocity(): Vec2 {
return this._velocity; return this._velocity;
} }
@ -198,6 +223,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
this.aiActive = true; this.aiActive = true;
} }
// @implemented
addAI<T extends AI>(ai: string | (new () => T), options?: Record<string, any>): void { addAI<T extends AI>(ai: string | (new () => T), options?: Record<string, any>): void {
if(!this._ai){ if(!this._ai){
this.scene.getAIManager().registerActor(this); this.scene.getAIManager().registerActor(this);
@ -214,6 +240,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
this.aiActive = true; this.aiActive = true;
} }
// @implemented
setAIActive(active: boolean): void { setAIActive(active: boolean): void {
this.aiActive = active; this.aiActive = active;
} }
@ -240,7 +267,10 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
this.scene = scene; 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 { getScene(): Scene {
return this.scene; return this.scene;
} }
@ -253,24 +283,30 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
this.layer = layer; 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 { getLayer(): Layer {
return this.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 { protected positionChanged(): void {
if(this.hasPhysics){ if(this.hasPhysics){
this.collisionShape.center = this.position.clone().add(this.colliderOffset); this.collisionShape.center = this.position.clone().add(this.colliderOffset);
} }
}; };
/**
* Updates this GameNode
* @param deltaT The timestep of the update.
*/
update(deltaT: number): void { update(deltaT: number): void {
this.tweens.update(deltaT); this.tweens.update(deltaT);
} }
// @implemented
debugRender(): void { debugRender(): void {
let color = this.isColliding ? Color.RED : Color.GREEN; let color = this.isColliding ? Color.RED : Color.GREEN;
Debug.drawPoint(this.relativePosition, color); Debug.drawPoint(this.relativePosition, color);

View File

@ -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 * 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 { export default abstract class Graphic extends CanvasNode {
/** The color of the Graphic */
color: Color; color: Color;
constructor(){ constructor(){
@ -13,6 +13,11 @@ export default abstract class Graphic extends CanvasNode {
this.color = Color.RED; this.color = Color.RED;
} }
// @deprecated
/**
* Sets the color of the Graphic. DEPRECATED
* @param color The new color of the Graphic.
*/
setColor(color: Color){ setColor(color: Color){
this.color = color; this.color = color;
} }

View File

@ -1,6 +1,7 @@
import Graphic from "../Graphic"; import Graphic from "../Graphic";
import Vec2 from "../../DataTypes/Vec2"; import Vec2 from "../../DataTypes/Vec2";
/** A basic point to be drawn on the screen. */
export default class Point extends Graphic { export default class Point extends Graphic {
constructor(position: Vec2){ constructor(position: Vec2){

View File

@ -2,10 +2,14 @@ import Graphic from "../Graphic";
import Vec2 from "../../DataTypes/Vec2"; import Vec2 from "../../DataTypes/Vec2";
import Color from "../../Utils/Color"; import Color from "../../Utils/Color";
/** A basic rectangle to be drawn on the screen. */
export default class Rect extends Graphic { export default class Rect extends Graphic {
/** The border color of the Rect */
borderColor: Color; borderColor: Color;
protected borderWidth: number;
/** The width of the border */
borderWidth: number;
constructor(position: Vec2, size: Vec2){ constructor(position: Vec2, size: Vec2){
super(); super();
@ -19,16 +23,17 @@ export default class Rect extends Graphic {
* Sets the border color of this rectangle * Sets the border color of this rectangle
* @param color The border color * @param color The border color
*/ */
setBorderColor(color: Color){ setBorderColor(color: Color): void {
this.borderColor = color; this.borderColor = color;
} }
// @deprecated
getBorderColor(): Color { getBorderColor(): Color {
return this.borderColor; 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 * @param width The width of the rectangle in pixels
*/ */
setBorderWidth(width: number){ setBorderWidth(width: number){

View File

@ -3,6 +3,7 @@ import AnimationManager from "../../Rendering/Animations/AnimationManager";
import Spritesheet from "../../DataTypes/Spritesheet"; import Spritesheet from "../../DataTypes/Spritesheet";
import Vec2 from "../../DataTypes/Vec2"; import Vec2 from "../../DataTypes/Vec2";
/** An sprite with specified animation frames. */
export default class AnimatedSprite extends Sprite { export default class AnimatedSprite extends Sprite {
/** The number of columns in this sprite sheet */ /** The number of columns in this sprite sheet */
protected numCols: number; 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 { getAnimationOffset(index: number): Vec2 {
return new Vec2((index % this.numCols) * this.size.x, Math.floor(index / this.numCols) * this.size.y); return new Vec2((index % this.numCols) * this.size.x, Math.floor(index / this.numCols) * this.size.y);
} }

View File

@ -6,9 +6,13 @@ import Vec2 from "../../DataTypes/Vec2";
* The representation of a sprite - an in-game image * The representation of a sprite - an in-game image
*/ */
export default class Sprite extends CanvasNode { export default class Sprite extends CanvasNode {
/** The id of the image from the resourceManager */
imageId: string; imageId: string;
/** The offset of the sprite in an atlas image */
imageOffset: Vec2; imageOffset: Vec2;
/** Whether or not the x-axis should be inverted on render */
invertX: boolean; invertX: boolean;
/** Whether or not the y-axis should be inverted on render */
invertY: boolean; invertY: boolean;
constructor(imageId: string){ 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 * 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 { setImageOffset(offset: Vec2): void {
this.imageOffset = offset; this.imageOffset = offset;

View File

@ -7,10 +7,19 @@ import CanvasNode from "./CanvasNode";
* The representation of a tilemap - this can consist of a combination of tilesets in one layer * The representation of a tilemap - this can consist of a combination of tilesets in one layer
*/ */
export default abstract class Tilemap extends CanvasNode { export default abstract class Tilemap extends CanvasNode {
/** An array of the tilesets that this tilemap uses */
protected tilesets: Array<Tileset>; protected tilesets: Array<Tileset>;
/** The size of a tile in this tilemap */
protected tileSize: Vec2; protected tileSize: Vec2;
/** An array of tile data */
protected data: Array<number>; protected data: Array<number>;
/** An array of tile collision data */
protected collisionMap: Array<boolean>; protected collisionMap: Array<boolean>;
/** The name of the tilemap */
name: string; name: string;
// TODO: Make this no longer be specific to Tiled // 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 the tilesets associated with this tilemap
* @returns An array of all of the tilesets assocaited with this tilemap.
*/ */
getTilesets(): Tileset[] { getTilesets(): Tileset[] {
return this.tilesets; 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 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 { getTileSize(): Vec2 {
return this.tileSize.scaled(this.scale.x, this.scale.y); 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 { getTileSizeWithZoom(): Vec2 {
let zoom = this.scene.getViewScale(); let zoom = this.scene.getViewScale();
return this.getTileSize().scale(zoom); return this.getTileSize().scale(zoom);
} }
/** 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);
} }
@ -63,18 +80,21 @@ export default abstract class Tilemap extends CanvasNode {
/** /**
* Returns the value of the tile at the specified position * Returns the value of the tile at the specified position
* @param worldCoords The position in world coordinates * @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; abstract getTileAtWorldPosition(worldCoords: Vec2): number;
/** /**
* Returns the world position of the top left corner of the tile at the specified index * 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; abstract getTileWorldPosition(index: number): Vec2;
/** /**
* Returns the value of the tile at the specified index * 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; abstract getTile(index: number): number;
@ -85,9 +105,11 @@ export default abstract class Tilemap extends CanvasNode {
*/ */
abstract setTile(index: number, type: number): void; 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 * 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; protected abstract parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void;
} }

View File

@ -8,15 +8,12 @@ import Color from "../../Utils/Color";
* The representation of an orthogonal tilemap - i.e. a top down or platformer tilemap * The representation of an orthogonal tilemap - i.e. a top down or platformer tilemap
*/ */
export default class OrthogonalTilemap extends Tilemap { export default class OrthogonalTilemap extends Tilemap {
/** The number of columns in the tilemap */
protected numCols: number; protected numCols: number;
/** The number of rows in the tilemap */
protected numRows: number; protected numRows: number;
/** // @override
* 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
*/
protected parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void { protected parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void {
// The size of the tilemap in local space // The size of the tilemap in local space
this.numCols = tilemapData.width; 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 { getDimensions(): Vec2 {
return new Vec2(this.numCols, this.numRows); 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 { getTileAtWorldPosition(worldCoords: Vec2): number {
let localCoords = this.getColRowAt(worldCoords); let localCoords = this.getColRowAt(worldCoords);
return this.getTileAtRowCol(localCoords); return this.getTileAtRowCol(localCoords);
@ -58,7 +64,8 @@ export default class OrthogonalTilemap extends Tilemap {
/** /**
* Get the tile at the specified row and column * 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 { getTileAtRowCol(rowCol: Vec2): number {
if(rowCol.x < 0 || rowCol.x >= this.numCols || rowCol.y < 0 || rowCol.y >= this.numRows){ 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]; 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 { getTileWorldPosition(index: number): Vec2 {
// Get the local position // Get the local position
let col = index % this.numCols; let col = index % this.numCols;
@ -80,14 +92,29 @@ export default class OrthogonalTilemap extends Tilemap {
return new Vec2(x, y); 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 { getTile(index: number): number {
return this.data[index]; 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 { setTile(index: number, type: number): void {
this.data[index] = type; 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 { setTileAtRowCol(rowCol: Vec2, type: number): void {
let index = rowCol.y * this.numCols + rowCol.x; let index = rowCol.y * this.numCols + rowCol.x;
this.setTile(index, type); 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 * Returns true if the tile at the specified row and column of the tilemap is collidable
* @param indexOrCol * @param indexOrCol The index of the tile or the column it is in
* @param row * @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 { isTileCollidable(indexOrCol: number, row?: number): boolean {
// The value of the tile // 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 * 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 { getColRowAt(worldCoords: Vec2): Vec2 {
let col = Math.floor(worldCoords.x / this.tileSize.x / this.scale.x); 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); return new Vec2(col, row);
} }
// @override
update(deltaT: number): void {} update(deltaT: number): void {}
// @override
debugRender(){ debugRender(){
let tileSize = this.getTileSizeWithZoom(); let tileSize = this.getTileSizeWithZoom();
let origin = this.relativePosition.sub(this.sizeWithZoom); let origin = this.relativePosition.sub(this.sizeWithZoom);

View File

@ -6,24 +6,39 @@ import Vec2 from "../DataTypes/Vec2";
* The representation of a UIElement - the parent class of things like buttons * The representation of a UIElement - the parent class of things like buttons
*/ */
export default abstract class UIElement extends CanvasNode { export default abstract class UIElement extends CanvasNode {
// Style attributes // Style attributes - TODO - abstract this into a style object/interface
/** The backgound color */
backgroundColor: Color; backgroundColor: Color;
/** The border color */
borderColor: Color; borderColor: Color;
/** The border radius */
borderRadius: number; borderRadius: number;
/** The border width */
borderWidth: number; borderWidth: number;
/** The padding */
padding: Vec2; padding: Vec2;
// EventAttributes // EventAttributes
/** The reaction of this UIElement on a click */
onClick: Function; onClick: Function;
/** The event propagated on click */
onClickEventId: string; onClickEventId: string;
/** The reaction to the release of a click */
onRelease: Function; onRelease: Function;
/** The event propagated on the release of a click */
onReleaseEventId: string; onReleaseEventId: string;
/** The reaction when a mouse enters this UIElement */
onEnter: Function; onEnter: Function;
/** The event propagated when a mouse enters this UIElement */
onEnterEventId: string; onEnterEventId: string;
/** The reaction when a mouse leaves this UIElement */
onLeave: Function; onLeave: Function;
/** The event propogated when a mouse leaves this UIElement */
onLeaveEventId: string; onLeaveEventId: string;
/** Whether or not this UIElement is currently clicked on */
protected isClicked: boolean; protected isClicked: boolean;
/** Whether or not this UIElement is currently hovered over */
protected isEntered: boolean; protected isEntered: boolean;
constructor(position: Vec2){ constructor(position: Vec2){
@ -50,10 +65,12 @@ export default abstract class UIElement extends CanvasNode {
this.isEntered = false; this.isEntered = false;
} }
// @deprecated
setBackgroundColor(color: Color): void { setBackgroundColor(color: Color): void {
this.backgroundColor = color; this.backgroundColor = color;
} }
// @deprecated
setPadding(padding: Vec2): void { setPadding(padding: Vec2): void {
this.padding.copy(padding); 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 * 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 { calculateBackgroundColor(): string {
return this.backgroundColor.toStringRGBA(); 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 * 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 { calculateBorderColor(): string {
return this.borderColor.toStringRGBA(); return this.borderColor.toStringRGBA();

View File

@ -2,6 +2,7 @@ import Label from "./Label";
import Color from "../../Utils/Color"; import Color from "../../Utils/Color";
import Vec2 from "../../DataTypes/Vec2"; import Vec2 from "../../DataTypes/Vec2";
/** A clickable button UIElement */
export default class Button extends Label { export default class Button extends Label {
constructor(position: Vec2, text: string){ constructor(position: Vec2, text: string){
@ -12,6 +13,7 @@ export default class Button extends Label {
this.textColor = new Color(255, 255, 255); this.textColor = new Color(255, 255, 255);
} }
// @override
calculateBackgroundColor(): string { calculateBackgroundColor(): string {
// Change the background color if clicked or hovered // Change the background color if clicked or hovered
if(this.isEntered && !this.isClicked){ if(this.isEntered && !this.isClicked){

View File

@ -2,12 +2,19 @@ import Vec2 from "../../DataTypes/Vec2";
import Color from "../../Utils/Color"; import Color from "../../Utils/Color";
import UIElement from "../UIElement"; import UIElement from "../UIElement";
/** A basic text-containing label */
export default class Label extends UIElement{ export default class Label extends UIElement{
/** The color of the text of this UIElement */
textColor: Color; textColor: Color;
/** The value of the text of this UIElement */
text: string; text: string;
/** The name of the font */
protected font: string; protected font: string;
/** The size of the font */
protected fontSize: number; protected fontSize: number;
/** The horizontal alignment of the text within the label */
protected hAlign: string; protected hAlign: string;
/** The vertical alignment of text within the label */
protected vAlign: string; protected vAlign: string;
/** A flag for if the width of the text has been measured on the canvas for auto width assignment */ /** 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; this.sizeAssigned = false;
} }
// @deprecated
setText(text: string): void { setText(text: string): void {
this.text = text; this.text = text;
} }
// @deprecated
setTextColor(color: Color): void { setTextColor(color: Color): void {
this.textColor = color; 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; 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 * 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 { calculateTextColor(): string {
return this.textColor.toStringRGBA(); 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 { protected calculateTextWidth(ctx: CanvasRenderingContext2D): number {
ctx.font = this.fontSize + "px " + this.font; ctx.font = this.fontSize + "px " + this.font;
return ctx.measureText(this.text).width; 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 { calculateTextOffset(ctx: CanvasRenderingContext2D): Vec2 {
let textWidth = this.calculateTextWidth(ctx); let textWidth = this.calculateTextWidth(ctx);
@ -83,6 +104,10 @@ export default class Label extends UIElement{
this.sizeAssigned = true; this.sizeAssigned = true;
} }
/**
* Automatically sizes the element to the text within it
* @param ctx The rendering context
*/
protected autoSize(ctx: CanvasRenderingContext2D): void { protected autoSize(ctx: CanvasRenderingContext2D): void {
let width = this.calculateTextWidth(ctx); let width = this.calculateTextWidth(ctx);
let height = this.fontSize; let height = this.fontSize;
@ -90,6 +115,10 @@ export default class Label extends UIElement{
this.sizeAssigned = true; this.sizeAssigned = true;
} }
/**
* Initially assigns a size to the UIElement if none is provided
* @param ctx The rendering context
*/
handleInitialSizing(ctx: CanvasRenderingContext2D): void { handleInitialSizing(ctx: CanvasRenderingContext2D): void {
if(!this.sizeAssigned){ if(!this.sizeAssigned){
this.autoSize(ctx); this.autoSize(ctx);

View File

@ -3,12 +3,17 @@ import Color from "../../Utils/Color";
import MathUtils from "../../Utils/MathUtils"; import MathUtils from "../../Utils/MathUtils";
import UIElement from "../UIElement"; import UIElement from "../UIElement";
/** A slider UIElement */
export default class Slider extends UIElement { export default class Slider extends UIElement {
/** The value of the slider from [0, 1] */ /** The value of the slider from [0, 1] */
protected value: number; protected value: number;
/** The color of the slider nib */
public nibColor: Color; public nibColor: Color;
/** The color of the slider track */
public sliderColor: Color; public sliderColor: Color;
/** The reaction of this UIElement to a value change */
public onValueChange: (value: number) => void; public onValueChange: (value: number) => void;
/** The event propagated by this UIElement when value changes */
public onValueChangeEventId: string; public onValueChangeEventId: string;
constructor(position: Vec2){ constructor(position: Vec2){
@ -24,10 +29,15 @@ export default class Slider extends UIElement {
this.size.set(200, 20); this.size.set(200, 20);
} }
/**
* Retrieves the value of the slider
* @returns The value of the slider
*/
getValue(): number { getValue(): number {
return this.value; return this.value;
} }
/** A method called in response to the value changing */
protected valueChanged(): void { protected valueChanged(): void {
if(this.onValueChange){ if(this.onValueChange){
this.onValueChange(this.value); this.onValueChange(this.value);

View File

@ -2,8 +2,11 @@ import Vec2 from "../../DataTypes/Vec2";
import Color from "../../Utils/Color"; import Color from "../../Utils/Color";
import Label from "./Label"; import Label from "./Label";
/** A text input UIElement */
export default class TextInput extends Label { export default class TextInput extends Label {
/** A flag the represents whether the user can type in this TextInput */
focused: boolean; focused: boolean;
/** The position of the cursor in this TextInput */
cursorCounter: number; cursorCounter: number;
constructor(position: Vec2){ constructor(position: Vec2){
@ -39,7 +42,6 @@ export default class TextInput extends Label {
let specialChars = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?"; let specialChars = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?";
let letters = "qwertyuiopasdfghjklzxcvbnm"; let letters = "qwertyuiopasdfghjklzxcvbnm";
let mask = nums + specialChars + letters; let mask = nums + specialChars + letters;
// THIS ISN'T ACTUALLY AN ERROR, DISREGARD
keys = keys.filter(key => mask.includes(key)); keys = keys.filter(key => mask.includes(key));
let shiftPressed = this.input.isPressed("shift"); let shiftPressed = this.input.isPressed("shift");
let backspacePressed = this.input.isJustPressed("backspace"); let backspacePressed = this.input.isJustPressed("backspace");