added slider and textInput uiElements
This commit is contained in:
parent
fff1ac4907
commit
3d275ba7f9
|
@ -125,6 +125,16 @@ export default class InputReceiver{
|
|||
}
|
||||
}
|
||||
|
||||
getKeysJustPressed(): Array<string> {
|
||||
let keys = Array<string>();
|
||||
this.keyJustPressed.forEach(key => {
|
||||
if(this.keyJustPressed.get(key)){
|
||||
keys.push(key);
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
isPressed(key: string): boolean {
|
||||
if(this.keyPressed.has(key)){
|
||||
return this.keyPressed.get(key)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import GameNode from "./GameNode";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
import { Region } from "../DataTypes/Interfaces/Descriptors";
|
||||
import { Region, Renderable } from "../DataTypes/Interfaces/Descriptors";
|
||||
import AABB from "../DataTypes/Shapes/AABB";
|
||||
|
||||
/**
|
||||
* The representation of an object in the game world that can be drawn to the screen
|
||||
*/
|
||||
export default abstract class CanvasNode extends GameNode implements Region {
|
||||
export default abstract class CanvasNode extends GameNode implements Region, Renderable {
|
||||
private _size: Vec2;
|
||||
private _scale: Vec2;
|
||||
private _boundary: AABB;
|
||||
|
@ -21,8 +21,6 @@ export default abstract class CanvasNode extends GameNode implements Region {
|
|||
this._scale.setOnChange(() => this.scaleChanged());
|
||||
this._boundary = new AABB();
|
||||
this.updateBoundary();
|
||||
|
||||
this.size.set(101, 101);
|
||||
}
|
||||
|
||||
get size(): Vec2 {
|
||||
|
|
|
@ -4,7 +4,7 @@ import Receiver from "../Events/Receiver";
|
|||
import Emitter from "../Events/Emitter";
|
||||
import Scene from "../Scene/Scene";
|
||||
import Layer from "../Scene/Layer";
|
||||
import { Physical, Positioned, isRegion, Unique, Updateable, Actor, AI } from "../DataTypes/Interfaces/Descriptors"
|
||||
import { Physical, Positioned, isRegion, Unique, Updateable, Actor, AI, Debug_Renderable } from "../DataTypes/Interfaces/Descriptors"
|
||||
import Shape from "../DataTypes/Shapes/Shape";
|
||||
import Map from "../DataTypes/Map";
|
||||
import AABB from "../DataTypes/Shapes/AABB";
|
||||
|
@ -13,7 +13,7 @@ import NavigationPath from "../Pathfinding/NavigationPath";
|
|||
/**
|
||||
* The representation of an object in the game world
|
||||
*/
|
||||
export default abstract class GameNode implements Positioned, Unique, Updateable, Physical, Actor {
|
||||
export default abstract class GameNode implements Positioned, Unique, Updateable, Physical, Actor, Debug_Renderable {
|
||||
/*---------- POSITIONED ----------*/
|
||||
private _position: Vec2;
|
||||
|
||||
|
@ -227,4 +227,6 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
};
|
||||
|
||||
abstract update(deltaT: number): void;
|
||||
|
||||
debug_render = (ctx: CanvasRenderingContext2D): void => {};
|
||||
}
|
|
@ -100,7 +100,6 @@ export default class Label extends UIElement{
|
|||
|
||||
// Grab the global alpha so we can adjust it for this render
|
||||
let previousAlpha = ctx.globalAlpha;
|
||||
ctx.globalAlpha = this.getLayer().getAlpha();
|
||||
|
||||
let origin = this.scene.getViewTranslation(this);
|
||||
|
||||
|
@ -108,23 +107,30 @@ export default class Label extends UIElement{
|
|||
let offset = this.calculateTextOffset(ctx);
|
||||
|
||||
// Stroke and fill a rounded rect and give it text
|
||||
ctx.globalAlpha = this.backgroundColor.a;
|
||||
ctx.fillStyle = this.calculateBackgroundColor();
|
||||
ctx.fillRoundedRect(this.position.x - origin.x - this.size.x/2, this.position.y - origin.y - this.size.y/2,
|
||||
this.size.x, this.size.y, this.borderRadius);
|
||||
|
||||
ctx.strokeStyle = this.calculateBorderColor();
|
||||
ctx.globalAlpha = this.borderColor.a;
|
||||
ctx.lineWidth = this.borderWidth;
|
||||
ctx.strokeRoundedRect(this.position.x - origin.x - this.size.x/2, this.position.y - origin.y - this.size.y/2,
|
||||
this.size.x, this.size.y, this.borderRadius);
|
||||
|
||||
ctx.fillStyle = this.calculateTextColor();
|
||||
ctx.globalAlpha = this.textColor.a;
|
||||
ctx.fillText(this.text, this.position.x + offset.x - origin.x - this.size.x/2, this.position.y + offset.y - origin.y - this.size.y/2);
|
||||
|
||||
ctx.globalAlpha = previousAlpha;
|
||||
}
|
||||
|
||||
debug_render = (ctx: CanvasRenderingContext2D): void => {
|
||||
let origin = this.scene.getViewTranslation(this);
|
||||
|
||||
ctx.lineWidth = 4;
|
||||
ctx.strokeStyle = "#00FF00"
|
||||
let b = this.boundary;
|
||||
ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2, b.hh*2);
|
||||
}
|
||||
};
|
||||
}
|
76
src/Nodes/UIElements/Slider.ts
Normal file
76
src/Nodes/UIElements/Slider.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import Vec2 from "../../DataTypes/Vec2";
|
||||
import Color from "../../Utils/Color";
|
||||
import MathUtils from "../../Utils/MathUtils";
|
||||
import UIElement from "../UIElement";
|
||||
|
||||
export default class Slider extends UIElement {
|
||||
/** The value of the slider from [0, 1] */
|
||||
protected value: number;
|
||||
protected nibColor: Color;
|
||||
protected sliderColor: Color;
|
||||
public onValueChange: (value: number) => void;
|
||||
public onValueChangeEventId: string;
|
||||
|
||||
constructor(position: Vec2){
|
||||
super(position);
|
||||
|
||||
this.value = 0;
|
||||
this.nibColor = Color.RED;
|
||||
this.sliderColor = Color.BLACK;
|
||||
this.backgroundColor = Color.TRANSPARENT;
|
||||
this.borderColor = Color.TRANSPARENT;
|
||||
|
||||
// Set a default size
|
||||
this.size.set(200, 20);
|
||||
}
|
||||
|
||||
protected valueChanged(): void {
|
||||
if(this.onValueChange){
|
||||
this.onValueChange(this.value);
|
||||
}
|
||||
|
||||
if(this.onValueChangeEventId){
|
||||
this.emitter.fireEvent(this.onValueChangeEventId, {target: this, value: this.value});
|
||||
}
|
||||
}
|
||||
|
||||
update(deltaT: number): void {
|
||||
super.update(deltaT);
|
||||
|
||||
if(this.isClicked){
|
||||
let val = MathUtils.invLerp(this.position.x - this.size.x/2, this.position.x + this.size.x/2, this.input.getMousePosition().x);
|
||||
this.value = MathUtils.clamp01(val);
|
||||
this.valueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
render(ctx: CanvasRenderingContext2D): void {
|
||||
// Grab the global alpha so we can adjust it for this render
|
||||
let previousAlpha = ctx.globalAlpha;
|
||||
ctx.globalAlpha = this.getLayer().getAlpha();
|
||||
|
||||
let origin = this.scene.getViewTranslation(this);
|
||||
|
||||
// Calcualate the slider size
|
||||
let sliderSize = new Vec2(this.size.x, 2);
|
||||
|
||||
// Draw the slider
|
||||
ctx.fillStyle = this.sliderColor.toString();
|
||||
ctx.fillRoundedRect(this.position.x - origin.x - sliderSize.x/2, this.position.y - origin.y - sliderSize.y/2,
|
||||
sliderSize.x, sliderSize.y, this.borderRadius);
|
||||
|
||||
// Calculate the nib size and position
|
||||
let nibSize = new Vec2(10, this.size.y);
|
||||
let x = MathUtils.lerp(this.position.x - this.size.x/2, this.position.x + this.size.x/2, this.value);
|
||||
let nibPosition = new Vec2(x, this.position.y);
|
||||
|
||||
// Draw the nib
|
||||
ctx.fillStyle = this.nibColor.toString();
|
||||
ctx.fillRoundedRect(nibPosition.x - origin.x - nibSize.x/2, nibPosition.y - origin.y - nibSize.y/2,
|
||||
nibSize.x, nibSize.y, this.borderRadius);
|
||||
|
||||
|
||||
// Reset the alpha
|
||||
ctx.globalAlpha = previousAlpha;
|
||||
}
|
||||
}
|
81
src/Nodes/UIElements/TextInput.ts
Normal file
81
src/Nodes/UIElements/TextInput.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import Vec2 from "../../DataTypes/Vec2";
|
||||
import Color from "../../Utils/Color";
|
||||
import Label from "./Label";
|
||||
|
||||
export default class TextInput extends Label {
|
||||
focused: boolean;
|
||||
cursorCounter: number;
|
||||
|
||||
constructor(position: Vec2){
|
||||
super(position, "");
|
||||
|
||||
this.focused = false;
|
||||
this.cursorCounter = 0;
|
||||
|
||||
// Give a default size to the x only
|
||||
this.size.set(200, this.fontSize);
|
||||
this.hAlign = "left";
|
||||
|
||||
this.borderColor = Color.BLACK;
|
||||
this.backgroundColor = Color.WHITE;
|
||||
}
|
||||
|
||||
update(deltaT: number): void {
|
||||
super.update(deltaT);
|
||||
|
||||
if(this.input.isMouseJustPressed()){
|
||||
let clickPos = this.input.getMousePressPosition();
|
||||
if(this.contains(clickPos.x, clickPos.y)){
|
||||
this.focused = true;
|
||||
this.cursorCounter = 30;
|
||||
} else {
|
||||
this.focused = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.focused){
|
||||
let keys = this.input.getKeysJustPressed();
|
||||
let nums = "1234567890";
|
||||
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");
|
||||
let spacePressed = this.input.isJustPressed("space");
|
||||
|
||||
if(backspacePressed){
|
||||
this.text = this.text.substring(0, this.text.length - 1);
|
||||
} else if(spacePressed){
|
||||
this.text += " ";
|
||||
} else if(keys.length > 0) {
|
||||
if(shiftPressed){
|
||||
this.text += keys[0].toUpperCase();
|
||||
} else {
|
||||
this.text += keys[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(ctx: CanvasRenderingContext2D): void {
|
||||
// Show a cursor sometimes
|
||||
if(this.focused && this.cursorCounter % 60 > 30){
|
||||
this.text += "|";
|
||||
}
|
||||
|
||||
super.render(ctx);
|
||||
|
||||
if(this.focused){
|
||||
if(this.cursorCounter % 60 > 30){
|
||||
this.text = this.text.substring(0, this.text.length - 1);
|
||||
}
|
||||
|
||||
this.cursorCounter += 1;
|
||||
if(this.cursorCounter >= 60){
|
||||
this.cursorCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
export enum UIElementType {
|
||||
BUTTON = "BUTTON",
|
||||
LABEL = "LABEL",
|
||||
SLIDER = "SLIDER",
|
||||
TEXT_INPUT = "TEXTINPUT"
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
import Scene from "../Scene";
|
||||
import UIElement from "../../Nodes/UIElement";
|
||||
import Layer from "../Layer";
|
||||
import Graphic from "../../Nodes/Graphic";
|
||||
import Sprite from "../../Nodes/Sprites/Sprite";
|
||||
import { GraphicType } from "../../Nodes/Graphics/GraphicTypes";
|
||||
import { UIElementType } from "../../Nodes/UIElements/UIElementTypes";
|
||||
import Point from "../../Nodes/Graphics/Point";
|
||||
import Vec2 from "../../DataTypes/Vec2";
|
||||
import Shape from "../../DataTypes/Shapes/Shape";
|
||||
import Button from "../../Nodes/UIElements/Button";
|
||||
import Label from "../../Nodes/UIElements/Label";
|
||||
import Slider from "../../Nodes/UIElements/Slider";
|
||||
import TextInput from "../../Nodes/UIElements/TextInput";
|
||||
import Rect from "../../Nodes/Graphics/Rect";
|
||||
|
||||
export default class CanvasNodeFactory {
|
||||
|
@ -38,6 +38,12 @@ export default class CanvasNodeFactory {
|
|||
case UIElementType.LABEL:
|
||||
instance = this.buildLabel(options);
|
||||
break;
|
||||
case UIElementType.SLIDER:
|
||||
instance = this.buildSlider(options);
|
||||
break;
|
||||
case UIElementType.TEXT_INPUT:
|
||||
instance = this.buildTextInput(options);
|
||||
break;
|
||||
default:
|
||||
throw `UIElementType '${type}' does not exist, or is registered incorrectly.`
|
||||
}
|
||||
|
@ -126,6 +132,18 @@ export default class CanvasNodeFactory {
|
|||
return new Label(options.position, options.text)
|
||||
}
|
||||
|
||||
buildSlider(options: Record<string, any>): Slider {
|
||||
this.checkIfPropExists("Slider", options, "position", Vec2, "Vec2");
|
||||
|
||||
return new Slider(options.position);
|
||||
}
|
||||
|
||||
buildTextInput(options: Record<string, any>): TextInput {
|
||||
this.checkIfPropExists("TextInput", options, "position", Vec2, "Vec2");
|
||||
|
||||
return new TextInput(options.position);
|
||||
}
|
||||
|
||||
buildPoint(options?: Record<string, any>): Point {
|
||||
this.checkIfPropExists("Point", options, "position", Vec2, "Vec2");
|
||||
|
||||
|
@ -142,7 +160,7 @@ export default class CanvasNodeFactory {
|
|||
/* ---------- ERROR HANDLING ---------- */
|
||||
|
||||
checkIfPropExists<T>(objectName: string, options: Record<string, any>, prop: string, type: (new (...args: any) => T) | string, typeName?: string){
|
||||
if(!options || !options[prop]){
|
||||
if(!options || options[prop] === undefined){
|
||||
// Check that the options object has the property
|
||||
throw `${objectName} object requires argument ${prop} of type ${typeName}, but none was provided.`;
|
||||
} else {
|
||||
|
|
|
@ -42,6 +42,10 @@ export default class Layer {
|
|||
this.depth = 0;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
setPaused(pauseValue: boolean): void {
|
||||
this.paused = pauseValue;
|
||||
}
|
||||
|
|
|
@ -75,8 +75,8 @@ export default class Level1 extends Scene {
|
|||
// Add UI
|
||||
this.addUILayer("UI");
|
||||
|
||||
this.coinCountLabel = this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(80, 30), text: "Coins: 0"});
|
||||
this.livesCountLabel = this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(600, 30), text: "Lives: 3"});
|
||||
this.coinCountLabel = <Label>this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(80, 30), text: "Coins: 0"});
|
||||
this.livesCountLabel = <Label>this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(600, 30), text: "Lives: 3"});
|
||||
}
|
||||
|
||||
updateScene(deltaT: number): void {
|
||||
|
@ -101,8 +101,8 @@ export default class Level1 extends Scene {
|
|||
this.coinCountLabel.setText("Coins: " + this.coinCount);
|
||||
|
||||
} else if(event.type === MarioEvents.PLAYER_HIT_COIN_BLOCK){
|
||||
console.log("Hit Coin Block")
|
||||
console.log(event.data.get("node") === this.player);
|
||||
this.coinCount += 1;
|
||||
this.coinCountLabel.setText("Coins: " + this.coinCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,34 +1,47 @@
|
|||
import Vec2 from "../../DataTypes/Vec2";
|
||||
import Debug from "../../Debug/Debug";
|
||||
import InputHandler from "../../Input/InputHandler";
|
||||
import InputReceiver from "../../Input/InputReceiver";
|
||||
import { GraphicType } from "../../Nodes/Graphics/GraphicTypes";
|
||||
import Button from "../../Nodes/UIElements/Button";
|
||||
import Label from "../../Nodes/UIElements/Label";
|
||||
import Slider from "../../Nodes/UIElements/Slider";
|
||||
import { UIElementType } from "../../Nodes/UIElements/UIElementTypes";
|
||||
import Scene from "../../Scene/Scene";
|
||||
import Color from "../../Utils/Color";
|
||||
import Level1 from "./Level1";
|
||||
|
||||
export default class MainMenu extends Scene {
|
||||
|
||||
playBtn: Button;
|
||||
|
||||
loadScene(): void {
|
||||
|
||||
}
|
||||
|
||||
startScene(): void {
|
||||
this.addUILayer("Main");
|
||||
|
||||
let size = this.viewport.getHalfSize();
|
||||
this.viewport.setFocus(size);
|
||||
|
||||
this.playBtn = <Button>this.add.uiElement(UIElementType.BUTTON, "Main", {position: new Vec2(size.x, size.y), text: "Play Game"});
|
||||
this.playBtn.setBackgroundColor(Color.GREEN);
|
||||
this.playBtn.setPadding(new Vec2(50, 10));
|
||||
this.playBtn.onClick = () => {
|
||||
console.log("Play");
|
||||
let playBtn = <Button>this.add.uiElement(UIElementType.BUTTON, "Main", {position: new Vec2(size.x, size.y), text: "Play Game"});
|
||||
playBtn.setBackgroundColor(Color.GREEN);
|
||||
playBtn.setPadding(new Vec2(50, 10));
|
||||
playBtn.onClick = () => {
|
||||
let sceneOptions = {
|
||||
physics: {
|
||||
physicsLayerNames: ["ground", "player", "enemy", "coin"],
|
||||
numPhyiscsLayers: 4,
|
||||
physicsLayerCollisions:
|
||||
[
|
||||
[0, 1, 1, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 1, 1, 0]
|
||||
]
|
||||
}
|
||||
}
|
||||
this.sceneManager.changeScene(Level1, sceneOptions);
|
||||
}
|
||||
|
||||
let slider = <Slider>this.add.uiElement(UIElementType.SLIDER, "Main", {position: new Vec2(size.x, size.y*1.5)});
|
||||
let label = this.add.uiElement(UIElementType.LABEL, "Main", {position: new Vec2(size.x + 150, size.y*1.5), text: ""});
|
||||
slider.onValueChange = (value) => (<Label>label).setText(value.toString());
|
||||
this.add.uiElement(UIElementType.TEXT_INPUT, "Main", {position: new Vec2(size.x, size.y*1.7)});
|
||||
}
|
||||
|
||||
updateScene(): void {
|
||||
Debug.log("mp", InputReceiver.getInstance().getMousePosition().toString());
|
||||
|
|
|
@ -2,6 +2,7 @@ import Vec2 from "../../../../DataTypes/Vec2";
|
|||
import GameEvent from "../../../../Events/GameEvent";
|
||||
import MathUtils from "../../../../Utils/MathUtils";
|
||||
import { CustomGameEventType } from "../../../CustomGameEventType";
|
||||
import Level1, { MarioEvents } from "../../../Mario/Level1";
|
||||
import { PlayerStates } from "./PlayerController";
|
||||
import PlayerState from "./PlayerState";
|
||||
|
||||
|
@ -28,6 +29,7 @@ export default class Jump extends PlayerState {
|
|||
// If coin block, change to empty coin block
|
||||
if(tile === 4){
|
||||
this.parent.tilemap.setTileAtRowCol(pos, 12);
|
||||
this.emitter.fireEvent(MarioEvents.PLAYER_HIT_COIN_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
src/main.ts
14
src/main.ts
|
@ -12,20 +12,6 @@ function main(){
|
|||
let game = new GameLoop(options);
|
||||
game.start();
|
||||
|
||||
let sceneOptions = {
|
||||
physics: {
|
||||
physicsLayerNames: ["ground", "player", "enemy", "coin"],
|
||||
numPhyiscsLayers: 4,
|
||||
physicsLayerCollisions:
|
||||
[
|
||||
[0, 1, 1, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 1, 1, 0]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
let sm = game.getSceneManager();
|
||||
sm.addScene(MainMenu, {});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user