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 {
/** 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: <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 {
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 {

View File

@ -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));
}

View File

@ -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;

View File

@ -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<T extends AI>(ai: string | (new () => T), options?: Record<string, any>): 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);

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
*/
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;
}

View File

@ -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){

View File

@ -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){

View File

@ -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);
}

View File

@ -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;

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
*/
export default abstract class Tilemap extends CanvasNode {
/** An array of the tilesets that this tilemap uses */
protected tilesets: Array<Tileset>;
/** The size of a tile in this tilemap */
protected tileSize: Vec2;
/** An array of tile data */
protected data: Array<number>;
/** An array of tile collision data */
protected collisionMap: Array<boolean>;
/** 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;
}

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
*/
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);

View File

@ -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();

View File

@ -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){

View File

@ -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);

View File

@ -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);

View File

@ -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");