diff --git a/package.json b/package.json index 0cb87b9..082dd9d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "gameengine", + "name": "wolfie2d", "version": "1.0.0", "description": "A game engine written in TypeScript", "main": "./dist/main.js", diff --git a/src/BoidDemo.ts b/src/BoidDemo.ts deleted file mode 100644 index 5289092..0000000 --- a/src/BoidDemo.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Vec2 from "./DataTypes/Vec2"; -import Scene from "./Scene/Scene"; -import SceneGraphQuadTree from "./SceneGraph/SceneGraphQuadTree"; -import Color from "./Utils/Color"; -import Boid from "./_DemoClasses/Boids/Boid"; -import FlockBehavior from "./_DemoClasses/Boids/FlockBehavior"; -import Player from "./_DemoClasses/Player/Player"; -import PlayerController from "./_DemoClasses/Player/PlayerController"; - -/** - * This demo emphasizes an ai system for the game engine with component architecture - * Boids move around with components - * Boids have randomized affects (maybe?) - * Boids respond to player movement - */ -export default class BoidDemo extends Scene { - boids: Array; - - startScene(){ - // Set the world size - // this.worldSize = new Vec2(800, 600); - // this.sceneGraph = new SceneGraphQuadTree(this.viewport, this); - // this.viewport.setBounds(0, 0, 800, 600) - // this.viewport.setCenter(400, 300); - - // let layer = this.addLayer(); - // this.boids = new Array(); - - // // Add the player - // let player = this.add.graphic(Player, layer, new Vec2(0, 0)); - // player.addPhysics(); - // let ai = new PlayerController(player, "topdown"); - // player.update = (deltaT: number) => {ai.update(deltaT)} - // this.viewport.follow(player); - // this.viewport.enableZoom(); - - // // Create a bunch of boids - // for(let i = 0; i < 150; i++){ - // let boid = this.add.graphic(Boid, layer, new Vec2(this.worldSize.x*Math.random(), this.worldSize.y*Math.random())); - // boid.fb = new FlockBehavior(this, boid, this.boids, 75, 50); - // boid.size.set(5, 5); - // this.boids.push(boid); - // } - } - - updateScene(deltaT: number): void { - for(let boid of this.boids){ - boid.setColor(Color.RED); - } - - this.updateFlock(); - } - - updateFlock(): void { - for(let boid of this.boids){ - boid.fb.update(); - } - } - -} \ No newline at end of file diff --git a/src/DataTypes/Vec4.ts b/src/DataTypes/Vec4.ts deleted file mode 100644 index 66793f4..0000000 --- a/src/DataTypes/Vec4.ts +++ /dev/null @@ -1,52 +0,0 @@ -import Vec2 from "./Vec2"; - -// @ignorePage -export default class Vec4 { - - public vec: Float32Array; - - constructor(x : number = 0, y : number = 0, z : number = 0, w : number = 0) { - this.vec = new Float32Array(4); - this.vec[0] = x; - this.vec[1] = y; - this.vec[2] = z; - this.vec[3] = w; - } - - // Expose x and y with getters and setters - get x() { - return this.vec[0]; - } - - set x(x: number) { - this.vec[0] = x; - } - - get y() { - return this.vec[1]; - } - - set y(y: number) { - this.vec[1] = y; - } - - get z() { - return this.vec[2]; - } - - set z(x: number) { - this.vec[2] = x; - } - - get w() { - return this.vec[3]; - } - - set w(y: number) { - this.vec[3] = y; - } - - split() : [Vec2, Vec2] { - return [new Vec2(this.x, this.y), new Vec2(this.z, this.w)]; - } -} \ No newline at end of file diff --git a/src/Input/InputReceiver.ts b/src/Input/InputReceiver.ts deleted file mode 100644 index 5108115..0000000 --- a/src/Input/InputReceiver.ts +++ /dev/null @@ -1,239 +0,0 @@ -import Receiver from "../Events/Receiver"; -import Map from "../DataTypes/Map"; -import Vec2 from "../DataTypes/Vec2"; -import EventQueue from "../Events/EventQueue"; -import Viewport from "../SceneGraph/Viewport"; -import GameEvent from "../Events/GameEvent"; -import { GameEventType } from "../Events/GameEventType"; - -/** - * Receives input events from the @reference[EventQueue] and allows for easy access of information about input by other systems - */ -export default class InputReceiver{ - private static instance: InputReceiver = null; - - private mousePressed: boolean; - private mouseJustPressed: boolean; - - private keyJustPressed: Map; - private keyPressed: Map; - - private mousePosition: Vec2; - private mousePressPosition: Vec2; - - private scrollDirection: number; - private justScrolled: boolean; - - private eventQueue: EventQueue; - private receiver: Receiver; - private viewport: Viewport; - - private constructor(){ - this.mousePressed = false; - this.mouseJustPressed = false; - this.receiver = new Receiver(); - this.keyJustPressed = new Map(); - this.keyPressed = new Map(); - this.mousePosition = new Vec2(0, 0); - this.mousePressPosition = new Vec2(0, 0); - this.scrollDirection = 0; - this.justScrolled = false; - - this.eventQueue = EventQueue.getInstance(); - // Subscribe to all input events - this.eventQueue.subscribe(this.receiver, [GameEventType.MOUSE_DOWN, GameEventType.MOUSE_UP, GameEventType.MOUSE_MOVE, - GameEventType.KEY_DOWN, GameEventType.KEY_UP, GameEventType.CANVAS_BLUR, GameEventType.WHEEL_UP, GameEventType.WHEEL_DOWN]); - } - - /** - * Gets the statc instance of the Singleton InputReceiver - * @returns The InputReceiver instance - */ - static getInstance(): InputReceiver{ - if(this.instance === null){ - this.instance = new InputReceiver(); - } - return this.instance; - } - - update(deltaT: number): void { - // Reset the justPressed values to false - this.mouseJustPressed = false; - this.keyJustPressed.forEach((key: string) => this.keyJustPressed.set(key, false)); - this.justScrolled = false; - this.scrollDirection = 0; - - while(this.receiver.hasNextEvent()){ - let event = this.receiver.getNextEvent(); - - // Handle each event type - if(event.type === GameEventType.MOUSE_DOWN){ - this.mouseJustPressed = true; - this.mousePressed = true; - this.mousePressPosition = event.data.get("position"); - } - - if(event.type === GameEventType.MOUSE_UP){ - this.mousePressed = false; - } - - if(event.type === GameEventType.MOUSE_MOVE){ - this.mousePosition = event.data.get("position"); - } - - if(event.type === GameEventType.KEY_DOWN){ - let key = event.data.get("key"); - // Handle space bar - if(key === " "){ - key = "space"; - } - if(!this.keyPressed.get(key)){ - this.keyJustPressed.set(key, true); - this.keyPressed.set(key, true); - } - } - - if(event.type === GameEventType.KEY_UP){ - let key = event.data.get("key"); - // Handle space bar - if(key === " "){ - key = "space"; - } - this.keyPressed.set(key, false); - } - - if(event.type === GameEventType.CANVAS_BLUR){ - this.clearKeyPresses() - } - - if(event.type === GameEventType.WHEEL_UP){ - this.scrollDirection = -1; - this.justScrolled = true; - } else if(event.type === GameEventType.WHEEL_DOWN){ - this.scrollDirection = 1; - this.justScrolled = true; - } - } - } - - private clearKeyPresses(): void { - this.keyJustPressed.forEach((key: string) => this.keyJustPressed.set(key, false)); - this.keyPressed.forEach((key: string) => this.keyPressed.set(key, false)); - } - - /** - * Returns whether or not a key was newly pressed this frame. - * If the key is still pressed from last frame and wasn't re-pressed, this will return false. - * @param key The key - * @returns True if the key was just pressed, false otherwise - */ - isJustPressed(key: string): boolean { - if(this.keyJustPressed.has(key)){ - return this.keyJustPressed.get(key) - } else { - return false; - } - } - - /** - * Returns an array of all of the keys that are newly pressed this frame. - * If a key is still pressed from last frame and wasn't re-pressed, it will not be in this list. - * @returns An array of all of the newly pressed keys. - */ - getKeysJustPressed(): Array { - let keys = Array(); - this.keyJustPressed.forEach(key => { - if(this.keyJustPressed.get(key)){ - keys.push(key); - } - }); - return keys; - } - - /** - * Returns whether or not a key is being pressed. - * @param key The key - * @returns True if the key is currently pressed, false otherwise - */ - isPressed(key: string): boolean { - if(this.keyPressed.has(key)){ - return this.keyPressed.get(key) - } else { - return false; - } - } - - /** - * Returns whether or not the mouse was newly pressed this frame - * @returns True if the mouse was just pressed, false otherwise - */ - isMouseJustPressed(): boolean { - return this.mouseJustPressed; - } - - /** - * Returns whether or not the mouse is currently pressed - * @returns True if the mouse is currently pressed, false otherwise - */ - isMousePressed(): boolean { - return this.mousePressed; - } - - /** - * Returns whether the user scrolled or not - * @returns True if the user just scrolled this frame, false otherwise - */ - didJustScroll(): boolean { - return this.justScrolled; - } - - /** - * Gets the direction of the scroll - * @returns -1 if the user scrolled up, 1 if they scrolled down - */ - getScrollDirection(): number { - return this.scrollDirection; - } - - /** - * Gets the position of the player's mouse - * @returns The mouse position stored as a Vec2 - */ - getMousePosition(): Vec2 { - return this.mousePosition; - } - - /** - * Gets the position of the player's mouse in the game world, - * taking into consideration the scrolling of the viewport - * @returns The mouse position stored as a Vec2 - */ - getGlobalMousePosition(): Vec2 { - return this.mousePosition.clone().add(this.viewport.getOrigin()); - } - - /** - * Gets the position of the last mouse press - * @returns The mouse position stored as a Vec2 - */ - getMousePressPosition(): Vec2 { - return this.mousePressPosition; - } - - /** - * Gets the position of the last mouse press in the game world, - * taking into consideration the scrolling of the viewport - * @returns The mouse position stored as a Vec2 - */ - getGlobalMousePressPosition(): Vec2 { - return this.mousePressPosition.clone().add(this.viewport.getOrigin()); - } - - /** - * Gives the input receiver a reference to the viewport - * @param viewport The viewport - */ - setViewport(viewport: Viewport): void { - this.viewport = viewport; - } -} \ No newline at end of file diff --git a/src/MainScene.ts b/src/MainScene.ts deleted file mode 100644 index 982a7cb..0000000 --- a/src/MainScene.ts +++ /dev/null @@ -1,136 +0,0 @@ -import Scene from "./Scene/Scene"; -import Rect from "./Nodes/Graphics/Rect"; -import Color from "./Utils/Color"; -import Vec2 from "./DataTypes/Vec2"; -import UIElement from "./Nodes/UIElement"; -import Button from "./Nodes/UIElements/Button"; -import Layer from "./Scene/Layer"; -import SecondScene from "./SecondScene"; -import { GameEventType } from "./Events/GameEventType"; -import SceneGraphQuadTree from "./SceneGraph/SceneGraphQuadTree"; -import PlayerController from "./_DemoClasses/Player/PlayerStates/Platformer/PlayerController"; - -export default class MainScene extends Scene { - - loadScene(){ - this.load.tilemap("platformer", "assets/tilemaps/Platformer.json"); - this.load.tilemap("background", "assets/tilemaps/Background.json"); - this.load.image("player", "assets/sprites/player.png"); - this.load.audio("player_jump", "assets/sounds/jump-3.wav"); - //this.load.audio("level_music", "assets/sounds/level.wav"); - - let loadingScreen = this.addLayer(); - let box = this.add.graphic(Rect, loadingScreen, new Vec2(200, 300), new Vec2(400, 60)); - box.setColor(new Color(0, 0, 0)); - let bar = this.add.graphic(Rect, loadingScreen, new Vec2(205, 305), new Vec2(0, 50)); - bar.setColor(new Color(0, 200, 200)); - - this.load.onLoadProgress = (percentProgress: number) => { - bar.size.x = 295 * percentProgress; - } - - this.load.onLoadComplete = () => { - loadingScreen.disable(); - } - } - - startScene(){ - // Set world size - this.worldSize = new Vec2(2560, 1280) - - // Use a quadtree - this.sceneGraph = new SceneGraphQuadTree(this.viewport, this); - - // Add the background tilemap - let backgroundTilemapLayer = this.add.tilemap("background", new Vec2(4, 4))[0]; - // ...and make it have parallax - backgroundTilemapLayer.setParallax(0.5, 0.8); - backgroundTilemapLayer.setAlpha(0.5); - - // Add the music and start playing it on a loop - //this.emitter.fireEvent(GameEventType.PLAY_SOUND, {key: "level_music", loop: true, holdReference: true}); - - // Add the tilemap - this.add.tilemap("platformer", new Vec2(4, 4)); - - // Create the main game layer - let mainLayer = this.addLayer(); - - // Add a player - let playerSprite = this.add.sprite("player", mainLayer) - playerSprite.position.set(0, 0); - playerSprite.size.set(64, 64); - - this.viewport.follow(playerSprite); - - // Initialize UI - let uiLayer = this.addLayer(); - uiLayer.setParallax(0, 0); - - let recordButton = this.add.uiElement(Button, uiLayer); - recordButton.size.set(100, 50); - recordButton.setText("Record"); - recordButton.position.set(400, 30); - recordButton.onClickEventId = GameEventType.START_RECORDING; - - let stopButton = this.add.uiElement(Button, uiLayer); - stopButton.size.set(100, 50); - stopButton.setText("Stop"); - stopButton.position.set(550, 30); - stopButton.onClickEventId = GameEventType.STOP_RECORDING; - - let playButton = this.add.uiElement(Button, uiLayer); - playButton.size.set(100, 50); - playButton.setText("Play"); - playButton.position.set(700, 30); - playButton.onClickEventId = GameEventType.PLAY_RECORDING; - - let cycleFramerateButton = this.add.uiElement(Button, uiLayer); - cycleFramerateButton.size.set(150, 50); - cycleFramerateButton.setText("Cycle FPS"); - cycleFramerateButton.position.set(5, 400); - let i = 0; - let fps = [15, 30, 60]; - cycleFramerateButton.onClick = () => { - this.game.setMaxUpdateFPS(fps[i]); - i = (i + 1) % 3; - } - - // Pause Menu - let pauseLayer = this.addLayer(); - pauseLayer.setParallax(0, 0); - pauseLayer.disable(); - - let pauseButton = this.add.uiElement(Button, uiLayer); - pauseButton.size.set(100, 50); - pauseButton.setText("Pause"); - pauseButton.position.set(700, 400); - pauseButton.onClick = () => { - this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(true)); - pauseLayer.enable(); - } - - let modalBackground = this.add.uiElement(UIElement, pauseLayer); - modalBackground.size.set(400, 200); - modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4)); - modalBackground.position.set(200, 100); - - let resumeButton = this.add.uiElement(Button, pauseLayer); - resumeButton.size.set(100, 50); - resumeButton.setText("Resume"); - resumeButton.position.set(360, 150); - resumeButton.onClick = () => { - this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(false)); - pauseLayer.disable(); - } - - let switchButton = this.add.uiElement(Button, pauseLayer); - switchButton.size.set(140, 50); - switchButton.setText("Change Scene"); - switchButton.position.set(340, 190); - switchButton.onClick = () => { - this.emitter.fireEvent(GameEventType.STOP_SOUND, {key: "level_music"}); - this.sceneManager.changeScene(SecondScene); - } - } -} \ No newline at end of file diff --git a/src/Physics/BasicPhysicsManager.ts b/src/Physics/BasicPhysicsManager.ts deleted file mode 100644 index 8e1fc9c..0000000 --- a/src/Physics/BasicPhysicsManager.ts +++ /dev/null @@ -1,353 +0,0 @@ -import Physical from "../DataTypes/Interfaces/Physical"; -import Vec2 from "../DataTypes/Vec2"; -import GameNode from "../Nodes/GameNode"; -import Tilemap from "../Nodes/Tilemap"; -import PhysicsManager from "./PhysicsManager"; -import BroadPhase from "./BroadPhaseAlgorithms/BroadPhase"; -import SweepAndPrune from "./BroadPhaseAlgorithms/SweepAndPrune"; -import Shape from "../DataTypes/Shapes/Shape"; -import MathUtils from "../Utils/MathUtils"; -import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap"; -import AABB from "../DataTypes/Shapes/AABB"; -import Debug from "../Debug/Debug"; - -// @ignorePage - -export default class BasicPhysicsManager extends PhysicsManager { - - /** The array of static nodes */ - protected staticNodes: Array; - - /** The array of dynamic nodes */ - protected dynamicNodes: Array; - - /** The array of tilemaps */ - protected tilemaps: Array; - - /** The broad phase collision detection algorithm used by this physics system */ - protected broadPhase: BroadPhase; - - /** A 2D array that contains information about which layers interact with each other */ - protected layerMask: number[][]; - - constructor(physicsOptions: Record){ - super(); - this.staticNodes = new Array(); - this.dynamicNodes = new Array(); - this.tilemaps = new Array(); - this.broadPhase = new SweepAndPrune(); - - let i = 0; - if(physicsOptions.physicsLayerNames !== null){ - for(let layer of physicsOptions.physicsLayerNames){ - if(i >= physicsOptions.numPhysicsLayers){ - // If we have too many string layers, don't add extras - } - - this.layerNames[i] = layer; - this.layerMap.add(layer, i); - i += 1; - } - } - - for(i; i < physicsOptions.numPhysicsLayers; i++){ - this.layerNames[i] = "" + i; - this.layerMap.add("" + i, i); - } - - this.layerMask = physicsOptions.physicsLayerCollisions; - } - - /** - * Add a new physics object to be updated with the physics system - * @param node The node to be added to the physics system - */ - registerObject(node: GameNode): void { - if(node.isStatic){ - // Static and not collidable - this.staticNodes.push(node); - } else { - // Dynamic and not collidable - this.dynamicNodes.push(node); - } - this.broadPhase.addNode(node); - } - - /** - * Add a new tilemap to be updated with the physics system - * @param tilemap The tilemap to be added to the physics system - */ - registerTilemap(tilemap: Tilemap): void { - this.tilemaps.push(tilemap); - } - - /** - * Resolves a collision between two nodes, adjusting their velocities accordingly. - * @param node1 - * @param node2 - * @param firstContact - * @param lastContact - * @param collidingX - * @param collidingY - */ - resolveCollision(node1: Physical, node2: Physical, firstContact: Vec2, lastContact: Vec2, collidingX: boolean, collidingY: boolean): void { - // Handle collision - if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){ - if(node1.isPlayer){ - node1.isColliding = true; - } else if(node2.isPlayer){ - node2.isColliding = true; - } - - // We are colliding. Check for any triggers - let group1 = node1.group; - let group2 = node2.group; - - // TODO - This is problematic if a collision happens, but it is later learned that another collision happens before it - if(node1.triggers.has(group2)){ - // Node1 should send an event - let eventType = node1.triggers.get(group2); - this.emitter.fireEvent(eventType, {node: node1, other: node2, collision: {firstContact: firstContact}}); - } - - if(node2.triggers.has(group1)){ - // Node2 should send an event - let eventType = node2.triggers.get(group1); - this.emitter.fireEvent(eventType, {node: node2, other: node1, collision: {firstContact: firstContact}}); - } - - if(collidingX && collidingY){ - // If we're already intersecting, resolve the current collision - } else if(node1.isCollidable && node2.isCollidable) { - // We aren't already colliding, and both nodes can collide, so this is a new collision. - - // Get the amount to scale x and y based on their initial collision times - let xScale = MathUtils.clamp(firstContact.x, 0, 1); - let yScale = MathUtils.clamp(firstContact.y, 0, 1); - - MathUtils.floorToPlace(xScale, 4); - MathUtils.floorToPlace(yScale, 4); - - // Handle special case of stickiness on perfect corner to corner collisions - if(xScale === yScale){ - xScale = 1; - } - - // Handle being stopped moving in the y-direction - if(yScale !== 1){ - // Figure out which node is on top - let node1onTop = node1.collisionShape.center.y < node2.collisionShape.center.y; - - // If either is moving, set their onFloor and onCeiling appropriately - if(!node1.isStatic && node1.moving){ - node1.onGround = node1onTop; - node1.onCeiling = !node1onTop; - } - - if(!node2.isStatic && node2.moving){ - node2.onGround = !node1onTop; - node2.onCeiling = node1onTop; - } - } - - // Handle being stopped moving in the x-direction - if(xScale !== 1){ - // If either node is non-static and moving, set its onWall to true - if(!node1.isStatic && node1.moving){ - node1.onWall = true; - } - - if(!node2.isStatic && node2.moving){ - node2.onWall = true; - } - } - - // Scale velocity for either node if it is moving - node1._velocity.scale(xScale, yScale); - - node2._velocity.scale(xScale, yScale); - } - } - } - - collideWithTilemap(node: Physical, tilemap: Tilemap, velocity: Vec2): void { - if(tilemap instanceof OrthogonalTilemap){ - this.collideWithOrthogonalTilemap(node, tilemap, velocity); - } - } - - collideWithOrthogonalTilemap(node: Physical, tilemap: OrthogonalTilemap, velocity: Vec2): void { - // Get the starting position, ending position, and size of the node - let startPos = node.collisionShape.center; - let endPos = startPos.clone().add(velocity); - let size = node.collisionShape.halfSize; - - // Get the min and max x and y coordinates of the moving node - let min = new Vec2(Math.min(startPos.x - size.x, endPos.x - size.x), Math.min(startPos.y - size.y, endPos.y - size.y)); - let max = new Vec2(Math.max(startPos.x + size.x, endPos.x + size.x), Math.max(startPos.y + size.y, endPos.y + size.y)); - - // Convert the min/max x/y to the min and max row/col in the tilemap array - let minIndex = tilemap.getColRowAt(min); - let maxIndex = tilemap.getColRowAt(max); - - // Create an empty set of tilemap collisions (We'll handle all of them at the end) - let tilemapCollisions = new Array(); - let tileSize = tilemap.getTileSize(); - - // Loop over all possible tiles (which isn't many in the scope of the velocity per frame) - for(let col = minIndex.x; col <= maxIndex.x; col++){ - for(let row = minIndex.y; row <= maxIndex.y; row++){ - if(tilemap.isTileCollidable(col, row)){ - - // Get the position of this tile - let tilePos = new Vec2(col * tileSize.x + tileSize.x/2, row * tileSize.y + tileSize.y/2); - - // Create a new collider for this tile - let collider = new AABB(tilePos, tileSize.scaled(1/2)); - - // Calculate collision area between the node and the tile - let dx = Math.min(startPos.x, tilePos.x) - Math.max(startPos.x + size.x, tilePos.x + size.x); - let dy = Math.min(startPos.y, tilePos.y) - Math.max(startPos.y + size.y, tilePos.y + size.y); - - // If we overlap, how much do we overlap by? - let overlap = 0; - if(dx * dy > 0){ - overlap = dx * dy; - } - - tilemapCollisions.push(new TileCollisionData(collider, overlap)); - } - } - } - - // Now that we have all collisions, sort by collision area highest to lowest - tilemapCollisions = tilemapCollisions.sort((a, b) => a.overlapArea - b.overlapArea); - - // Resolve the collisions in order of collision area (i.e. "closest" tiles are collided with first, so we can slide along a surface of tiles) - tilemapCollisions.forEach(collision => { - let [firstContact, _, collidingX, collidingY] = Shape.getTimeOfCollision(node.collisionShape, velocity, collision.collider, Vec2.ZERO); - - // Handle collision - if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){ - // We are definitely colliding, so add to this node's tilemap collision list - node.collidedWithTilemap = true; - - if(collidingX && collidingY){ - // If we're already intersecting, freak out I guess? Probably should handle this in some way for if nodes get spawned inside of tiles - } else { - // Get the amount to scale x and y based on their initial collision times - let xScale = MathUtils.clamp(firstContact.x, 0, 1); - let yScale = MathUtils.clamp(firstContact.y, 0, 1); - - // Handle special case of stickiness on perfect corner to corner collisions - if(xScale === yScale){ - xScale = 1; - } - - if(yScale !== 1){ - // If the tile is below us - if(collision.collider.y > node.collisionShape.center.y){ - node.onGround = true; - } else { - node.onCeiling = true; - } - } - - if(xScale !== 1){ - node.onWall = true; - } - - // Scale the velocity of the node - velocity.scale(xScale, yScale); - } - } - }) - } - - update(deltaT: number): void { - /*---------- INITIALIZATION PHASE ----------*/ - for(let node of this.dynamicNodes){ - // Clear frame dependent boolean values for each node - node.onGround = false; - node.onCeiling = false; - node.onWall = false; - node.collidedWithTilemap = false; - node.isColliding = false; - - if(node.isPlayer){ - Debug.log("pvel", "Player Velocity:", node._velocity.toString()); - } - - // Update the swept shapes of each node - if(node.moving){ - // Round Velocity - node._velocity.x = Math.round(node._velocity.x*1000)/1000; - node._velocity.y = Math.round(node._velocity.y*1000)/1000; - - // If moving, reflect that in the swept shape - node.sweptRect.sweep(node._velocity, node.collisionShape.center, node.collisionShape.halfSize); - } else { - node.sweptRect.sweep(Vec2.ZERO_STATIC, node.collisionShape.center, node.collisionShape.halfSize); - } - } - - /*---------- BROAD PHASE ----------*/ - // Get a potentially colliding set - let potentialCollidingPairs = this.broadPhase.runAlgorithm(); - - - // TODO - Should I be getting all collisions first, sorting by the time they happen, the resolving them? - /*---------- NARROW PHASE ----------*/ - for(let pair of potentialCollidingPairs){ - let node1 = pair[0]; - let node2 = pair[1]; - - // Make sure both nodes are active - if(!node1.active || !node2.active){ - continue; - } - - // Make sure both nodes can collide with each other based on their physics layer - if(!(node1.physicsLayer === -1 || node2.physicsLayer === -1 || this.layerMask[node1.physicsLayer][node2.physicsLayer] === 1)){ - // Nodes do not collide. Continue onto the next pair - continue; - } - - // Get Collision (which may or may not happen) - let [firstContact, lastContact, collidingX, collidingY] = Shape.getTimeOfCollision(node1.collisionShape, node1._velocity, node2.collisionShape, node2._velocity); - - this.resolveCollision(node1, node2, firstContact, lastContact, collidingX, collidingY); - } - - /*---------- TILEMAP PHASE ----------*/ - for(let node of this.dynamicNodes){ - if(node.moving && node.isCollidable){ - // If a node is moving and can collide, check it against every tilemap - for(let tilemap of this.tilemaps){ - // Check if there could even be a collision - if(node.sweptRect.overlaps(tilemap.boundary)){ - this.collideWithTilemap(node, tilemap, node._velocity); - } - } - } - } - - /*---------- ENDING PHASE ----------*/ - for(let node of this.dynamicNodes){ - if(node.moving){ - node.finishMove(); - } - } - } -} - -// Collision data objects for tilemaps -class TileCollisionData { - collider: AABB; - overlapArea: number; - - constructor(collider: AABB, overlapArea: number){ - this.collider = collider; - this.overlapArea = overlapArea; - } -} \ No newline at end of file diff --git a/src/Physics/BroadPhaseAlgorithms/BroadPhase.ts b/src/Physics/BroadPhaseAlgorithms/BroadPhase.ts deleted file mode 100644 index fbace68..0000000 --- a/src/Physics/BroadPhaseAlgorithms/BroadPhase.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Physical from "../../DataTypes/Interfaces/Physical"; -import GameNode from "../../Nodes/GameNode"; - -// @ignorePage - -export default abstract class BroadPhase { - /** - * Runs the algorithm and returns an array of possible collision pairs. - */ - abstract runAlgorithm(): Array; - - abstract addNode(node: GameNode): void; -} \ No newline at end of file diff --git a/src/Physics/BroadPhaseAlgorithms/SweepAndPrune.ts b/src/Physics/BroadPhaseAlgorithms/SweepAndPrune.ts deleted file mode 100644 index ac0e898..0000000 --- a/src/Physics/BroadPhaseAlgorithms/SweepAndPrune.ts +++ /dev/null @@ -1,70 +0,0 @@ -import Physical from "../../DataTypes/Interfaces/Physical"; -import SortingUtils from "../../Utils/SortingUtils"; -import BroadPhase from "./BroadPhase"; - -// @ignorePage - -export default class SweepAndPrune extends BroadPhase { - protected xList: Array; - protected yList: Array; - - constructor(){ - super(); - this.xList = new Array(); - this.yList = new Array(); - } - - addNode(node: Physical): void { - this.xList.push(node); - this.yList.push(node); - } - - // TODO - Can optimize further by doing a callback whenever a swap occurs - // TODO - And by using better pair management - runAlgorithm(): Array { - // Sort the xList - SortingUtils.insertionSort(this.xList, (a, b) => (a.sweptRect.left - b.sweptRect.left) ); - - let xCollisions = []; - for(let i = 0; i < this.xList.length; i++){ - let node = this.xList[i]; - - let index = 1; - while(i + index < this.xList.length && node.sweptRect.right >= this.xList[i + index].sweptRect.left){ - // Colliding pair in x-axis - xCollisions.push([node, this.xList[i + index]]); - index++; - } - } - - // Sort the y-list - SortingUtils.insertionSort(this.yList, (a, b) => (a.sweptRect.top - b.sweptRect.top) ); - - let yCollisions = []; - for(let i = 0; i < this.yList.length; i++){ - let node = this.yList[i]; - - let index = 1; - while(i + index < this.yList.length && node.sweptRect.bottom >= this.yList[i + index].sweptRect.top){ - // Colliding pair in y-axis - yCollisions.push([node, this.yList[i + index]]); - index++; - } - } - - // Check the pairs - let collisions = [] - for(let xPair of xCollisions){ - for(let yPair of yCollisions){ - if((xPair[0] === yPair[0] && xPair[1] === yPair[1]) - ||(xPair[0] === yPair[1] && xPair[1] === yPair[0])){ - // Colliding in both axes, add to set - collisions.push(xPair); - } - } - } - - return collisions; - } - -} \ No newline at end of file diff --git a/src/Physics/Collisions.ts b/src/Physics/Collisions.ts deleted file mode 100644 index df3339c..0000000 --- a/src/Physics/Collisions.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Physical from "../DataTypes/Interfaces/Physical"; -import Vec2 from "../DataTypes/Vec2"; - -// @ignorePage - -export class Collision { - firstContact: Vec2; - lastContact: Vec2; - collidingX: boolean; - collidingY: boolean; - node1: Physical; - node2: Physical; -} \ No newline at end of file diff --git a/src/QuadTreeScene.ts b/src/QuadTreeScene.ts deleted file mode 100644 index 4ffcc41..0000000 --- a/src/QuadTreeScene.ts +++ /dev/null @@ -1,63 +0,0 @@ -import Scene from "./Scene/Scene"; -import Point from "./Nodes/Graphics/Point"; -import Rect from "./Nodes/Graphics/Rect"; -import Layer from "./Scene/Layer"; -import SceneGraphQuadTree from "./SceneGraph/SceneGraphQuadTree" -import Vec2 from "./DataTypes/Vec2"; -import InputReceiver from "./Input/InputReceiver"; -import Color from "./Utils/Color"; - -export default class QuadTreeScene extends Scene { - - mainLayer: Layer; - view: Rect; - points: Array; - - loadScene(){} - - startScene(){ - // Make the scene graph a quadtree scenegraph - this.sceneGraph = new SceneGraphQuadTree(this.viewport, this); - - // Make a main layer - this.mainLayer = this.sceneGraph.addLayer(); - - // Generate a bunch of random points - this.points = []; - for(let i = 0; i < 1000; i++){ - let pos = new Vec2(500/3*(Math.random() + Math.random() + Math.random()), 500/3*(Math.random() + Math.random() + Math.random())); - let point = this.add.graphic(Point, this.mainLayer, pos); - point.setColor(Color.RED); - this.points.push(point); - } - - this.view = this.add.graphic(Rect, this.mainLayer, Vec2.ZERO, new Vec2(150, 100)); - this.view.setColor(Color.TRANSPARENT); - this.view.setBorderColor(Color.ORANGE); - } - - updateScene(deltaT: number): void { - this.view.position.copy(InputReceiver.getInstance().getGlobalMousePosition()); - for(let point of this.points){ - point.setColor(Color.RED); - - point.position.add(Vec2.UP.rotateCCW(Math.random()*2*Math.PI).add(point.position.vecTo(this.view.position).normalize().scale(0.1))); - } - - let results = this.sceneGraph.getNodesInRegion(this.view.boundary); - - for(let result of results){ - if(result instanceof Point){ - result.setColor(Color.GREEN); - } - } - - results = this.sceneGraph.getNodesAt(this.view.position); - - for(let result of results){ - if(result instanceof Point){ - result.setColor(Color.YELLOW); - } - } - } -} \ No newline at end of file diff --git a/src/SecondScene.ts b/src/SecondScene.ts deleted file mode 100644 index f7a16f2..0000000 --- a/src/SecondScene.ts +++ /dev/null @@ -1,105 +0,0 @@ -import Scene from "./Scene/Scene"; -import Rect from "./Nodes/Graphics/Rect"; -import Color from "./Utils/Color"; -import Vec2 from "./DataTypes/Vec2"; -import UIElement from "./Nodes/UIElement"; -import Button from "./Nodes/UIElements/Button"; -import Layer from "./Scene/Layer"; -import { GameEventType } from "./Events/GameEventType"; - -export default class SecondScene extends Scene { - - loadScene(){ - this.load.tilemap("level2", "assets/tilemaps/TopDown2.json"); - this.load.image("player", "assets/sprites/player.png"); - this.load.audio("music", "assets/sounds/level.wav") - - let loadingScreen = this.addLayer(); - let box = this.add.graphic(Rect, loadingScreen, new Vec2(200, 300), new Vec2(400, 60)); - box.setColor(new Color(0, 0, 0)); - let bar = this.add.graphic(Rect, loadingScreen, new Vec2(205, 305), new Vec2(0, 50)); - bar.setColor(new Color(255, 100, 0)); - - this.load.onLoadProgress = (percentProgress: number) => { - //bar.setSize(295 * percentProgress, bar.getSize().y); - } - - this.load.onLoadComplete = () => { - loadingScreen.disable(); - } - } - - startScene(){ - // // Add the tilemap - // let mainLayer = this.add.tilemap("level2")[1]; - // mainLayer.setYSort(true); - - // // Add a player - // let player = this.add.physics(Player, mainLayer, "topdown"); - // let playerSprite = this.add.sprite("player", mainLayer); - // player.setSprite(playerSprite); - - // this.viewport.follow(player); - - // // Initialize UI - // let uiLayer = this.addLayer(); - // uiLayer.setParallax(0, 0); - - // let recordButton = this.add.uiElement(Button, uiLayer); - // recordButton.setSize(100, 50); - // recordButton.setText("Record"); - // recordButton.setPosition(400, 30); - // recordButton.onClickEventId = GameEventType.START_RECORDING; - - // let stopButton = this.add.uiElement(Button, uiLayer); - // stopButton.setSize(100, 50); - // stopButton.setText("Stop"); - // stopButton.setPosition(550, 30); - // stopButton.onClickEventId = GameEventType.STOP_RECORDING; - - // let playButton = this.add.uiElement(Button, uiLayer); - // playButton.setSize(100, 50); - // playButton.setText("Play"); - // playButton.setPosition(700, 30); - // playButton.onClickEventId = GameEventType.PLAY_RECORDING; - - // let cycleFramerateButton = this.add.uiElement(Button, uiLayer); - // cycleFramerateButton.setSize(150, 50); - // cycleFramerateButton.setText("Cycle FPS"); - // cycleFramerateButton.setPosition(5, 400); - // let i = 0; - // let fps = [15, 30, 60]; - // cycleFramerateButton.onClick = () => { - // this.game.setMaxUpdateFPS(fps[i]); - // i = (i + 1) % 3; - // } - - // // Pause Menu - // let pauseLayer = this.addLayer(); - // pauseLayer.setParallax(0, 0); - // pauseLayer.disable(); - - // let pauseButton = this.add.uiElement(Button, uiLayer); - // pauseButton.setSize(100, 50); - // pauseButton.setText("Pause"); - // pauseButton.setPosition(700, 400); - // pauseButton.onClick = () => { - // this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(true)); - // pauseLayer.enable(); - // } - - // let modalBackground = this.add.uiElement(UIElement, pauseLayer); - // modalBackground.setSize(400, 200); - // modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4)); - // modalBackground.setPosition(200, 100); - - // let resumeButton = this.add.uiElement(Button, pauseLayer); - // resumeButton.setSize(100, 50); - // resumeButton.setText("Resume"); - // resumeButton.setPosition(400, 200); - // resumeButton.onClick = () => { - // this.sceneGraph.getLayers().forEach((layer: Layer) => layer.setPaused(false)); - // pauseLayer.disable(); - // } - } -} \ No newline at end of file diff --git a/src/AI/AIManager.ts b/src/Wolfie2D/AI/AIManager.ts similarity index 100% rename from src/AI/AIManager.ts rename to src/Wolfie2D/AI/AIManager.ts diff --git a/src/AI/StateMachineAI.ts b/src/Wolfie2D/AI/StateMachineAI.ts similarity index 100% rename from src/AI/StateMachineAI.ts rename to src/Wolfie2D/AI/StateMachineAI.ts diff --git a/src/DataTypes/Collection.ts b/src/Wolfie2D/DataTypes/Collection.ts similarity index 100% rename from src/DataTypes/Collection.ts rename to src/Wolfie2D/DataTypes/Collection.ts diff --git a/src/DataTypes/Functions/NullFunc.ts b/src/Wolfie2D/DataTypes/Functions/NullFunc.ts similarity index 87% rename from src/DataTypes/Functions/NullFunc.ts rename to src/Wolfie2D/DataTypes/Functions/NullFunc.ts index bb13b22..9808300 100644 --- a/src/DataTypes/Functions/NullFunc.ts +++ b/src/Wolfie2D/DataTypes/Functions/NullFunc.ts @@ -1,3 +1,5 @@ +// @ignorePage + /** * A placeholder function for No Operation. Does nothing */ diff --git a/src/DataTypes/Graphs/EdgeNode.ts b/src/Wolfie2D/DataTypes/Graphs/EdgeNode.ts similarity index 100% rename from src/DataTypes/Graphs/EdgeNode.ts rename to src/Wolfie2D/DataTypes/Graphs/EdgeNode.ts diff --git a/src/DataTypes/Graphs/Graph.ts b/src/Wolfie2D/DataTypes/Graphs/Graph.ts similarity index 100% rename from src/DataTypes/Graphs/Graph.ts rename to src/Wolfie2D/DataTypes/Graphs/Graph.ts diff --git a/src/DataTypes/Graphs/PositionGraph.ts b/src/Wolfie2D/DataTypes/Graphs/PositionGraph.ts similarity index 100% rename from src/DataTypes/Graphs/PositionGraph.ts rename to src/Wolfie2D/DataTypes/Graphs/PositionGraph.ts diff --git a/src/DataTypes/Interfaces/AI.ts b/src/Wolfie2D/DataTypes/Interfaces/AI.ts similarity index 100% rename from src/DataTypes/Interfaces/AI.ts rename to src/Wolfie2D/DataTypes/Interfaces/AI.ts diff --git a/src/DataTypes/Interfaces/Actor.ts b/src/Wolfie2D/DataTypes/Interfaces/Actor.ts similarity index 100% rename from src/DataTypes/Interfaces/Actor.ts rename to src/Wolfie2D/DataTypes/Interfaces/Actor.ts diff --git a/src/DataTypes/Interfaces/DebugRenderable.ts b/src/Wolfie2D/DataTypes/Interfaces/DebugRenderable.ts similarity index 100% rename from src/DataTypes/Interfaces/DebugRenderable.ts rename to src/Wolfie2D/DataTypes/Interfaces/DebugRenderable.ts diff --git a/src/DataTypes/Interfaces/Navigable.ts b/src/Wolfie2D/DataTypes/Interfaces/Navigable.ts similarity index 100% rename from src/DataTypes/Interfaces/Navigable.ts rename to src/Wolfie2D/DataTypes/Interfaces/Navigable.ts diff --git a/src/DataTypes/Interfaces/Physical.ts b/src/Wolfie2D/DataTypes/Interfaces/Physical.ts similarity index 100% rename from src/DataTypes/Interfaces/Physical.ts rename to src/Wolfie2D/DataTypes/Interfaces/Physical.ts diff --git a/src/DataTypes/Interfaces/Positioned.ts b/src/Wolfie2D/DataTypes/Interfaces/Positioned.ts similarity index 100% rename from src/DataTypes/Interfaces/Positioned.ts rename to src/Wolfie2D/DataTypes/Interfaces/Positioned.ts diff --git a/src/DataTypes/Interfaces/Region.ts b/src/Wolfie2D/DataTypes/Interfaces/Region.ts similarity index 100% rename from src/DataTypes/Interfaces/Region.ts rename to src/Wolfie2D/DataTypes/Interfaces/Region.ts diff --git a/src/DataTypes/Interfaces/Unique.ts b/src/Wolfie2D/DataTypes/Interfaces/Unique.ts similarity index 100% rename from src/DataTypes/Interfaces/Unique.ts rename to src/Wolfie2D/DataTypes/Interfaces/Unique.ts diff --git a/src/DataTypes/Interfaces/Updateable.ts b/src/Wolfie2D/DataTypes/Interfaces/Updateable.ts similarity index 100% rename from src/DataTypes/Interfaces/Updateable.ts rename to src/Wolfie2D/DataTypes/Interfaces/Updateable.ts diff --git a/src/DataTypes/Map.ts b/src/Wolfie2D/DataTypes/Map.ts similarity index 100% rename from src/DataTypes/Map.ts rename to src/Wolfie2D/DataTypes/Map.ts diff --git a/src/DataTypes/Physics/AreaCollision.ts b/src/Wolfie2D/DataTypes/Physics/AreaCollision.ts similarity index 100% rename from src/DataTypes/Physics/AreaCollision.ts rename to src/Wolfie2D/DataTypes/Physics/AreaCollision.ts diff --git a/src/DataTypes/Physics/Hit.ts b/src/Wolfie2D/DataTypes/Physics/Hit.ts similarity index 100% rename from src/DataTypes/Physics/Hit.ts rename to src/Wolfie2D/DataTypes/Physics/Hit.ts diff --git a/src/DataTypes/QuadTree.ts b/src/Wolfie2D/DataTypes/QuadTree.ts similarity index 100% rename from src/DataTypes/QuadTree.ts rename to src/Wolfie2D/DataTypes/QuadTree.ts diff --git a/src/DataTypes/Queue.ts b/src/Wolfie2D/DataTypes/Queue.ts similarity index 100% rename from src/DataTypes/Queue.ts rename to src/Wolfie2D/DataTypes/Queue.ts diff --git a/src/DataTypes/RegionQuadTree.ts b/src/Wolfie2D/DataTypes/RegionQuadTree.ts similarity index 100% rename from src/DataTypes/RegionQuadTree.ts rename to src/Wolfie2D/DataTypes/RegionQuadTree.ts diff --git a/src/DataTypes/Shapes/AABB.ts b/src/Wolfie2D/DataTypes/Shapes/AABB.ts similarity index 100% rename from src/DataTypes/Shapes/AABB.ts rename to src/Wolfie2D/DataTypes/Shapes/AABB.ts diff --git a/src/DataTypes/Shapes/Circle.ts b/src/Wolfie2D/DataTypes/Shapes/Circle.ts similarity index 100% rename from src/DataTypes/Shapes/Circle.ts rename to src/Wolfie2D/DataTypes/Shapes/Circle.ts diff --git a/src/DataTypes/Shapes/Shape.ts b/src/Wolfie2D/DataTypes/Shapes/Shape.ts similarity index 100% rename from src/DataTypes/Shapes/Shape.ts rename to src/Wolfie2D/DataTypes/Shapes/Shape.ts diff --git a/src/DataTypes/Spritesheet.ts b/src/Wolfie2D/DataTypes/Spritesheet.ts similarity index 100% rename from src/DataTypes/Spritesheet.ts rename to src/Wolfie2D/DataTypes/Spritesheet.ts diff --git a/src/DataTypes/Stack.ts b/src/Wolfie2D/DataTypes/Stack.ts similarity index 100% rename from src/DataTypes/Stack.ts rename to src/Wolfie2D/DataTypes/Stack.ts diff --git a/src/DataTypes/State/State.ts b/src/Wolfie2D/DataTypes/State/State.ts similarity index 100% rename from src/DataTypes/State/State.ts rename to src/Wolfie2D/DataTypes/State/State.ts diff --git a/src/DataTypes/State/StateMachine.ts b/src/Wolfie2D/DataTypes/State/StateMachine.ts similarity index 100% rename from src/DataTypes/State/StateMachine.ts rename to src/Wolfie2D/DataTypes/State/StateMachine.ts diff --git a/src/DataTypes/Tilesets/TiledData.ts b/src/Wolfie2D/DataTypes/Tilesets/TiledData.ts similarity index 100% rename from src/DataTypes/Tilesets/TiledData.ts rename to src/Wolfie2D/DataTypes/Tilesets/TiledData.ts diff --git a/src/DataTypes/Tilesets/Tileset.ts b/src/Wolfie2D/DataTypes/Tilesets/Tileset.ts similarity index 100% rename from src/DataTypes/Tilesets/Tileset.ts rename to src/Wolfie2D/DataTypes/Tilesets/Tileset.ts diff --git a/src/DataTypes/Vec2.ts b/src/Wolfie2D/DataTypes/Vec2.ts similarity index 100% rename from src/DataTypes/Vec2.ts rename to src/Wolfie2D/DataTypes/Vec2.ts diff --git a/src/Debug/Debug.ts b/src/Wolfie2D/Debug/Debug.ts similarity index 100% rename from src/Debug/Debug.ts rename to src/Wolfie2D/Debug/Debug.ts diff --git a/src/Debug/Stats.ts b/src/Wolfie2D/Debug/Stats.ts similarity index 100% rename from src/Debug/Stats.ts rename to src/Wolfie2D/Debug/Stats.ts diff --git a/src/Events/Emitter.ts b/src/Wolfie2D/Events/Emitter.ts similarity index 100% rename from src/Events/Emitter.ts rename to src/Wolfie2D/Events/Emitter.ts diff --git a/src/Events/EventQueue.ts b/src/Wolfie2D/Events/EventQueue.ts similarity index 100% rename from src/Events/EventQueue.ts rename to src/Wolfie2D/Events/EventQueue.ts diff --git a/src/Events/GameEvent.ts b/src/Wolfie2D/Events/GameEvent.ts similarity index 100% rename from src/Events/GameEvent.ts rename to src/Wolfie2D/Events/GameEvent.ts diff --git a/src/Events/GameEventType.ts b/src/Wolfie2D/Events/GameEventType.ts similarity index 100% rename from src/Events/GameEventType.ts rename to src/Wolfie2D/Events/GameEventType.ts diff --git a/src/Events/Receiver.ts b/src/Wolfie2D/Events/Receiver.ts similarity index 100% rename from src/Events/Receiver.ts rename to src/Wolfie2D/Events/Receiver.ts diff --git a/src/Wolfie2D/Input/Input.ts b/src/Wolfie2D/Input/Input.ts new file mode 100644 index 0000000..54e251b --- /dev/null +++ b/src/Wolfie2D/Input/Input.ts @@ -0,0 +1,291 @@ +import Receiver from "../Events/Receiver"; +import Map from "../DataTypes/Map"; +import Vec2 from "../DataTypes/Vec2"; +import EventQueue from "../Events/EventQueue"; +import Viewport from "../SceneGraph/Viewport"; +import GameEvent from "../Events/GameEvent"; +import { GameEventType } from "../Events/GameEventType"; + +/** + * Receives input events from the @reference[EventQueue] and allows for easy access of information about input by other systems + */ +export default class Input { + private static mousePressed: boolean; + private static mouseJustPressed: boolean; + + private static keyJustPressed: Map; + private static keyPressed: Map; + + private static mousePosition: Vec2; + private static mousePressPosition: Vec2; + + private static scrollDirection: number; + private static justScrolled: boolean; + + private static eventQueue: EventQueue; + private static receiver: Receiver; + private static viewport: Viewport; + + private static keyMap: Map>; + + /** + * Initializes the Input object + * @param viewport A reference to the viewport of the game + */ + static initialize(viewport: Viewport, keyMap: Array>){ + Input.viewport = viewport; + Input.mousePressed = false; + Input.mouseJustPressed = false; + Input.receiver = new Receiver(); + Input.keyJustPressed = new Map(); + Input.keyPressed = new Map(); + Input.mousePosition = new Vec2(0, 0); + Input.mousePressPosition = new Vec2(0, 0); + Input.scrollDirection = 0; + Input.justScrolled = false; + + // Initialize the keymap + Input.keyMap = new Map(); + + // Add all keys to the keymap + for(let entry in keyMap){ + let name = keyMap[entry].name; + let keys = keyMap[entry].keys; + Input.keyMap.add(name, keys); + } + + Input.eventQueue = EventQueue.getInstance(); + // Subscribe to all input events + Input.eventQueue.subscribe(Input.receiver, [GameEventType.MOUSE_DOWN, GameEventType.MOUSE_UP, GameEventType.MOUSE_MOVE, + GameEventType.KEY_DOWN, GameEventType.KEY_UP, GameEventType.CANVAS_BLUR, GameEventType.WHEEL_UP, GameEventType.WHEEL_DOWN]); + } + + static update(deltaT: number): void { + // Reset the justPressed values to false + Input.mouseJustPressed = false; + Input.keyJustPressed.forEach((key: string) => Input.keyJustPressed.set(key, false)); + Input.justScrolled = false; + Input.scrollDirection = 0; + + while(Input.receiver.hasNextEvent()){ + let event = Input.receiver.getNextEvent(); + + // Handle each event type + if(event.type === GameEventType.MOUSE_DOWN){ + Input.mouseJustPressed = true; + Input.mousePressed = true; + Input.mousePressPosition = event.data.get("position"); + } + + if(event.type === GameEventType.MOUSE_UP){ + Input.mousePressed = false; + } + + if(event.type === GameEventType.MOUSE_MOVE){ + Input.mousePosition = event.data.get("position"); + } + + if(event.type === GameEventType.KEY_DOWN){ + let key = event.data.get("key"); + // Handle space bar + if(key === " "){ + key = "space"; + } + if(!Input.keyPressed.get(key)){ + Input.keyJustPressed.set(key, true); + Input.keyPressed.set(key, true); + } + } + + if(event.type === GameEventType.KEY_UP){ + let key = event.data.get("key"); + // Handle space bar + if(key === " "){ + key = "space"; + } + Input.keyPressed.set(key, false); + } + + if(event.type === GameEventType.CANVAS_BLUR){ + Input.clearKeyPresses() + } + + if(event.type === GameEventType.WHEEL_UP){ + Input.scrollDirection = -1; + Input.justScrolled = true; + } else if(event.type === GameEventType.WHEEL_DOWN){ + Input.scrollDirection = 1; + Input.justScrolled = true; + } + } + } + + private static clearKeyPresses(): void { + Input.keyJustPressed.forEach((key: string) => Input.keyJustPressed.set(key, false)); + Input.keyPressed.forEach((key: string) => Input.keyPressed.set(key, false)); + } + + /** + * Returns whether or not a key was newly pressed Input frame. + * If the key is still pressed from last frame and wasn't re-pressed, Input will return false. + * @param key The key + * @returns True if the key was just pressed, false otherwise + */ + static isKeyJustPressed(key: string): boolean { + if(Input.keyJustPressed.has(key)){ + return Input.keyJustPressed.get(key) + } else { + return false; + } + } + + /** + * Returns an array of all of the keys that are newly pressed Input frame. + * If a key is still pressed from last frame and wasn't re-pressed, it will not be in Input list. + * @returns An array of all of the newly pressed keys. + */ + static getKeysJustPressed(): Array { + let keys = Array(); + Input.keyJustPressed.forEach(key => { + if(Input.keyJustPressed.get(key)){ + keys.push(key); + } + }); + return keys; + } + + /** + * Returns whether or not a key is being pressed. + * @param key The key + * @returns True if the key is currently pressed, false otherwise + */ + static isKeyPressed(key: string): boolean { + if(Input.keyPressed.has(key)){ + return Input.keyPressed.get(key) + } else { + return false; + } + } + + /** + * Changes the binding of an input name to keys + * @param inputName The name of the input + * @param keys The corresponding keys + */ + static changeKeyBinding(inputName: string, keys: Array): void { + Input.keyMap.set(inputName, keys); + } + + /** + * Clears all key bindings + */ + static clearAllKeyBindings(): void { + Input.keyMap.clear(); + } + + /** + * Returns whether or not an input was just pressed this frame + * @param inputName The name of the input + * @returns True if the input was just pressed, false otherwise + */ + static isJustPressed(inputName: string): boolean { + if(Input.keyMap.has(inputName)){ + const keys = Input.keyMap.get(inputName); + let justPressed = false; + + for(let key of keys){ + justPressed = justPressed || Input.isKeyJustPressed(key); + } + + return justPressed; + } else { + return false; + } + } + + /** + * Returns whether or not an input is currently pressed + * @param inputName The name of the input + * @returns True if the input is pressed, false otherwise + */ + static isPressed(inputName: string): boolean { + if(Input.keyMap.has(inputName)){ + const keys = Input.keyMap.get(inputName); + let pressed = false; + + for(let key of keys){ + pressed = pressed || Input.isKeyPressed(key); + } + + return pressed; + } else { + return false; + } + } + + /** + * Returns whether or not the mouse was newly pressed Input frame + * @returns True if the mouse was just pressed, false otherwise + */ + static isMouseJustPressed(): boolean { + return Input.mouseJustPressed; + } + + /** + * Returns whether or not the mouse is currently pressed + * @returns True if the mouse is currently pressed, false otherwise + */ + static isMousePressed(): boolean { + return Input.mousePressed; + } + + /** + * Returns whether the user scrolled or not + * @returns True if the user just scrolled Input frame, false otherwise + */ + static didJustScroll(): boolean { + return Input.justScrolled; + } + + /** + * Gets the direction of the scroll + * @returns -1 if the user scrolled up, 1 if they scrolled down + */ + static getScrollDirection(): number { + return Input.scrollDirection; + } + + /** + * Gets the position of the player's mouse + * @returns The mouse position stored as a Vec2 + */ + static getMousePosition(): Vec2 { + return Input.mousePosition; + } + + /** + * Gets the position of the player's mouse in the game world, + * taking into consideration the scrolling of the viewport + * @returns The mouse position stored as a Vec2 + */ + static getGlobalMousePosition(): Vec2 { + return Input.mousePosition.clone().add(Input.viewport.getOrigin()); + } + + /** + * Gets the position of the last mouse press + * @returns The mouse position stored as a Vec2 + */ + static getMousePressPosition(): Vec2 { + return Input.mousePressPosition; + } + + /** + * Gets the position of the last mouse press in the game world, + * taking into consideration the scrolling of the viewport + * @returns The mouse position stored as a Vec2 + */ + static getGlobalMousePressPosition(): Vec2 { + return Input.mousePressPosition.clone().add(Input.viewport.getOrigin()); + } +} \ No newline at end of file diff --git a/src/Input/InputHandler.ts b/src/Wolfie2D/Input/InputHandler.ts similarity index 100% rename from src/Input/InputHandler.ts rename to src/Wolfie2D/Input/InputHandler.ts diff --git a/src/Wolfie2D/Loop/EnvironmentInitializer.ts b/src/Wolfie2D/Loop/EnvironmentInitializer.ts new file mode 100644 index 0000000..ff73726 --- /dev/null +++ b/src/Wolfie2D/Loop/EnvironmentInitializer.ts @@ -0,0 +1,47 @@ +import {} from "../../index"; // This import allows us to modify the CanvasRenderingContext2D to add extra functionality +// @ignorePage + +/** + * Sets up the environment of the game engine + */ +export default class EnvironmentInitializer { + static setup(){ + CanvasRenderingContext2D.prototype.roundedRect = function(x: number, y: number, w: number, h: number, r: number): void { + // Clamp the radius between 0 and the min of the width or height + if(r < 0) r = 0; + if(r > Math.min(w, h)) r = Math.min(w, h); + + // Draw the rounded rect + this.beginPath(); + + // Top + this.moveTo(x + r, y); + this.lineTo(x + w - r, y); + this.arcTo(x + w, y, x + w, y + r, r); + + // Right + this.lineTo(x + w, y + h - r); + this.arcTo(x + w, y + h, x + w - r, y + h, r); + + // Bottom + this.lineTo(x + r, y + h); + this.arcTo(x, y + h, x, y + h - r, r); + + // Left + this.lineTo(x, y + r); + this.arcTo(x, y, x + r, y, r) + + this.closePath(); + } + + CanvasRenderingContext2D.prototype.strokeRoundedRect = function(x, y, w, h, r){ + this.roundedRect(x, y, w, h, r); + this.stroke(); + } + + CanvasRenderingContext2D.prototype.fillRoundedRect = function(x, y, w, h, r){ + this.roundedRect(x, y, w, h, r); + this.fill(); + } + } +} \ No newline at end of file diff --git a/src/Loop/FixedUpdateGameLoop.ts b/src/Wolfie2D/Loop/FixedUpdateGameLoop.ts similarity index 100% rename from src/Loop/FixedUpdateGameLoop.ts rename to src/Wolfie2D/Loop/FixedUpdateGameLoop.ts diff --git a/src/Loop/Game.ts b/src/Wolfie2D/Loop/Game.ts similarity index 85% rename from src/Loop/Game.ts rename to src/Wolfie2D/Loop/Game.ts index 5e873f6..db3036e 100644 --- a/src/Loop/Game.ts +++ b/src/Wolfie2D/Loop/Game.ts @@ -1,5 +1,5 @@ import EventQueue from "../Events/EventQueue"; -import InputReceiver from "../Input/InputReceiver"; +import Input from "../Input/Input"; import InputHandler from "../Input/InputHandler"; import Recorder from "../Playback/Recorder"; import Debug from "../Debug/Debug"; @@ -14,6 +14,8 @@ import Color from "../Utils/Color"; import GameOptions from "./GameOptions"; import GameLoop from "./GameLoop"; import FixedUpdateGameLoop from "./FixedUpdateGameLoop"; +import EnvironmentInitializer from "./EnvironmentInitializer"; +import Vec2 from "../DataTypes/Vec2"; /** * The main loop of the game engine. @@ -22,6 +24,8 @@ import FixedUpdateGameLoop from "./FixedUpdateGameLoop"; */ export default class Game { gameOptions: GameOptions; + private showDebug: boolean; + private showStats: boolean; // The game loop private loop: GameLoop; @@ -38,7 +42,6 @@ export default class Game { // All of the necessary subsystems that need to run here private eventQueue: EventQueue; private inputHandler: InputHandler; - private inputReceiver: InputReceiver; private recorder: Recorder; private resourceManager: ResourceManager; private sceneManager: SceneManager; @@ -50,9 +53,15 @@ export default class Game { * @param options The options for Game initialization */ constructor(options?: Record){ + // Before anything else, build the environment + EnvironmentInitializer.setup(); + // Typecast the config object to a GameConfig object this.gameOptions = GameOptions.parse(options); + this.showDebug = this.gameOptions.showDebug; + this.showStats = this.gameOptions.showStats; + // Create an instance of a game loop this.loop = new FixedUpdateGameLoop(); @@ -74,22 +83,23 @@ export default class Game { Debug.initializeDebugCanvas(this.DEBUG_CANVAS, this.WIDTH, this.HEIGHT); Stats.initStats(); + if(this.gameOptions.showStats) { + // Find the stats output and make it no longer hidden + document.getElementById("stats").hidden = false; + } + // Size the viewport to the game canvas - this.viewport = new Viewport(); - this.viewport.setCanvasSize(this.WIDTH, this.HEIGHT); - this.viewport.setSize(this.WIDTH, this.HEIGHT); + const viewportSize = new Vec2(this.WIDTH, this.HEIGHT); + this.viewport = new Viewport(viewportSize.scaled(0.5), viewportSize); // Initialize all necessary game subsystems this.eventQueue = EventQueue.getInstance(); this.inputHandler = new InputHandler(this.GAME_CANVAS); - this.inputReceiver = InputReceiver.getInstance(); - this.inputReceiver.setViewport(this.viewport); + Input.initialize(this.viewport, this.gameOptions.inputs); this.recorder = new Recorder(); this.resourceManager = ResourceManager.getInstance(); this.sceneManager = new SceneManager(this.viewport, this.renderingManager); this.audioManager = AudioManager.getInstance(); - - } /** @@ -134,7 +144,7 @@ export default class Game { this.eventQueue.update(deltaT); // Update the input data structures so game objects can see the input - this.inputReceiver.update(deltaT); + Input.update(deltaT); // Update the recording of the game this.recorder.update(deltaT); @@ -163,7 +173,12 @@ export default class Game { this.sceneManager.render(); // Debug render - Debug.render(); - Stats.render(); + if(this.showDebug){ + Debug.render(); + } + + if(this.showStats){ + Stats.render(); + } } } \ No newline at end of file diff --git a/src/Loop/GameLoop.ts b/src/Wolfie2D/Loop/GameLoop.ts similarity index 100% rename from src/Loop/GameLoop.ts rename to src/Wolfie2D/Loop/GameLoop.ts diff --git a/src/Loop/GameOptions.ts b/src/Wolfie2D/Loop/GameOptions.ts similarity index 58% rename from src/Loop/GameOptions.ts rename to src/Wolfie2D/Loop/GameOptions.ts index f4a5614..f6bb9e8 100644 --- a/src/Loop/GameOptions.ts +++ b/src/Wolfie2D/Loop/GameOptions.ts @@ -8,6 +8,15 @@ export default class GameOptions { /** The color to clear the canvas to each frame */ clearColor: {r: number, g: number, b: number} + /* A list of input bindings */ + inputs: Array<{name: string, keys: Array}>; + + /* Whether or not the debug rendering should occur */ + showDebug: boolean; + + /* Whether or not the stats rendering should occur */ + showStats: boolean; + /** * Parses the data in the raw options object * @param options The game options as a Record @@ -17,7 +26,10 @@ export default class GameOptions { let gOpt = new GameOptions(); gOpt.viewportSize = options.viewportSize ? options.viewportSize : {x: 800, y: 600}; - gOpt.clearColor = options.clearColor ? options.clearColor : {r: 255, g: 255, b: 255}; + gOpt.clearColor = options.clearColor ? options.clearColor : {r: 255, g: 255, b: 255}; + gOpt.inputs = options.inputs ? options.inputs : []; + gOpt.showDebug = !!options.showDebug; + gOpt.showStats = !!options.showStats; return gOpt; } diff --git a/src/Nodes/CanvasNode.ts b/src/Wolfie2D/Nodes/CanvasNode.ts similarity index 100% rename from src/Nodes/CanvasNode.ts rename to src/Wolfie2D/Nodes/CanvasNode.ts diff --git a/src/Nodes/GameNode.ts b/src/Wolfie2D/Nodes/GameNode.ts similarity index 97% rename from src/Nodes/GameNode.ts rename to src/Wolfie2D/Nodes/GameNode.ts index c674ab9..25cb554 100644 --- a/src/Nodes/GameNode.ts +++ b/src/Wolfie2D/Nodes/GameNode.ts @@ -1,4 +1,3 @@ -import InputReceiver from "../Input/InputReceiver"; import Vec2 from "../DataTypes/Vec2"; import Receiver from "../Events/Receiver"; import Emitter from "../Events/Emitter"; @@ -60,8 +59,6 @@ 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. */ @@ -79,7 +76,6 @@ export default abstract class GameNode implements Positioned, Unique, Updateable // 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); this._position.setOnChange(() => this.positionChanged()); this.receiver = new Receiver(); @@ -136,6 +132,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable * @param velocity The velocity with which the object will move. */ finishMove(): void { + console.log("finish"); this.moving = false; this.position.add(this._velocity); if(this.pathfinding){ diff --git a/src/Nodes/Graphic.ts b/src/Wolfie2D/Nodes/Graphic.ts similarity index 100% rename from src/Nodes/Graphic.ts rename to src/Wolfie2D/Nodes/Graphic.ts diff --git a/src/Nodes/Graphics/GraphicTypes.ts b/src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts similarity index 100% rename from src/Nodes/Graphics/GraphicTypes.ts rename to src/Wolfie2D/Nodes/Graphics/GraphicTypes.ts diff --git a/src/Nodes/Graphics/Point.ts b/src/Wolfie2D/Nodes/Graphics/Point.ts similarity index 100% rename from src/Nodes/Graphics/Point.ts rename to src/Wolfie2D/Nodes/Graphics/Point.ts diff --git a/src/Nodes/Graphics/Rect.ts b/src/Wolfie2D/Nodes/Graphics/Rect.ts similarity index 100% rename from src/Nodes/Graphics/Rect.ts rename to src/Wolfie2D/Nodes/Graphics/Rect.ts diff --git a/src/Nodes/Sprites/AnimatedSprite.ts b/src/Wolfie2D/Nodes/Sprites/AnimatedSprite.ts similarity index 100% rename from src/Nodes/Sprites/AnimatedSprite.ts rename to src/Wolfie2D/Nodes/Sprites/AnimatedSprite.ts diff --git a/src/Nodes/Sprites/Sprite.ts b/src/Wolfie2D/Nodes/Sprites/Sprite.ts similarity index 100% rename from src/Nodes/Sprites/Sprite.ts rename to src/Wolfie2D/Nodes/Sprites/Sprite.ts diff --git a/src/Nodes/Tilemap.ts b/src/Wolfie2D/Nodes/Tilemap.ts similarity index 100% rename from src/Nodes/Tilemap.ts rename to src/Wolfie2D/Nodes/Tilemap.ts diff --git a/src/Nodes/Tilemaps/OrthogonalTilemap.ts b/src/Wolfie2D/Nodes/Tilemaps/OrthogonalTilemap.ts similarity index 100% rename from src/Nodes/Tilemaps/OrthogonalTilemap.ts rename to src/Wolfie2D/Nodes/Tilemaps/OrthogonalTilemap.ts diff --git a/src/Nodes/UIElement.ts b/src/Wolfie2D/Nodes/UIElement.ts similarity index 95% rename from src/Nodes/UIElement.ts rename to src/Wolfie2D/Nodes/UIElement.ts index fb5fafb..35bd508 100644 --- a/src/Nodes/UIElement.ts +++ b/src/Wolfie2D/Nodes/UIElement.ts @@ -1,6 +1,7 @@ import CanvasNode from "./CanvasNode"; import Color from "../Utils/Color"; import Vec2 from "../DataTypes/Vec2"; +import Input from "../Input/Input"; /** * The representation of a UIElement - the parent class of things like buttons @@ -79,8 +80,8 @@ export default abstract class UIElement extends CanvasNode { super.update(deltaT); // See of this object was just clicked - if(this.input.isMouseJustPressed()){ - let clickPos = this.input.getMousePressPosition(); + if(Input.isMouseJustPressed()){ + let clickPos = Input.getMousePressPosition(); if(this.contains(clickPos.x, clickPos.y)){ this.isClicked = true; @@ -96,14 +97,14 @@ export default abstract class UIElement extends CanvasNode { } // If the mouse wasn't just pressed, then we definitely weren't clicked - if(!this.input.isMousePressed()){ + if(!Input.isMousePressed()){ if(this.isClicked){ this.isClicked = false; } } // Check if the mouse is hovering over this element - let mousePos = this.input.getMousePosition(); + let mousePos = Input.getMousePosition(); if(mousePos && this.contains(mousePos.x, mousePos.y)){ this.isEntered = true; diff --git a/src/Nodes/UIElements/Button.ts b/src/Wolfie2D/Nodes/UIElements/Button.ts similarity index 100% rename from src/Nodes/UIElements/Button.ts rename to src/Wolfie2D/Nodes/UIElements/Button.ts diff --git a/src/Nodes/UIElements/Label.ts b/src/Wolfie2D/Nodes/UIElements/Label.ts similarity index 100% rename from src/Nodes/UIElements/Label.ts rename to src/Wolfie2D/Nodes/UIElements/Label.ts diff --git a/src/Nodes/UIElements/Slider.ts b/src/Wolfie2D/Nodes/UIElements/Slider.ts similarity index 94% rename from src/Nodes/UIElements/Slider.ts rename to src/Wolfie2D/Nodes/UIElements/Slider.ts index 605de19..a97d2a1 100644 --- a/src/Nodes/UIElements/Slider.ts +++ b/src/Wolfie2D/Nodes/UIElements/Slider.ts @@ -1,4 +1,5 @@ import Vec2 from "../../DataTypes/Vec2"; +import Input from "../../Input/Input"; import Color from "../../Utils/Color"; import MathUtils from "../../Utils/MathUtils"; import UIElement from "../UIElement"; @@ -52,7 +53,7 @@ export default class Slider extends UIElement { 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); + let val = MathUtils.invLerp(this.position.x - this.size.x/2, this.position.x + this.size.x/2, Input.getMousePosition().x); this.value = MathUtils.clamp01(val); this.valueChanged(); } diff --git a/src/Nodes/UIElements/TextInput.ts b/src/Wolfie2D/Nodes/UIElements/TextInput.ts similarity index 82% rename from src/Nodes/UIElements/TextInput.ts rename to src/Wolfie2D/Nodes/UIElements/TextInput.ts index 9ba8104..0bae845 100644 --- a/src/Nodes/UIElements/TextInput.ts +++ b/src/Wolfie2D/Nodes/UIElements/TextInput.ts @@ -1,6 +1,7 @@ import Vec2 from "../../DataTypes/Vec2"; import Color from "../../Utils/Color"; import Label from "./Label"; +import Input from "../../Input/Input"; /** A text input UIElement */ export default class TextInput extends Label { @@ -26,8 +27,8 @@ export default class TextInput extends Label { update(deltaT: number): void { super.update(deltaT); - if(this.input.isMouseJustPressed()){ - let clickPos = this.input.getMousePressPosition(); + if(Input.isMouseJustPressed()){ + let clickPos = Input.getMousePressPosition(); if(this.contains(clickPos.x, clickPos.y)){ this.focused = true; this.cursorCounter = 30; @@ -37,15 +38,15 @@ export default class TextInput extends Label { } if(this.focused){ - let keys = this.input.getKeysJustPressed(); + let keys = Input.getKeysJustPressed(); let nums = "1234567890"; let specialChars = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?"; let letters = "qwertyuiopasdfghjklzxcvbnm"; let mask = nums + specialChars + letters; 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"); + let shiftPressed = Input.isKeyPressed("shift"); + let backspacePressed = Input.isKeyJustPressed("backspace"); + let spacePressed = Input.isKeyJustPressed("space"); if(backspacePressed){ this.text = this.text.substring(0, this.text.length - 1); diff --git a/src/Nodes/UIElements/UIElementTypes.ts b/src/Wolfie2D/Nodes/UIElements/UIElementTypes.ts similarity index 100% rename from src/Nodes/UIElements/UIElementTypes.ts rename to src/Wolfie2D/Nodes/UIElements/UIElementTypes.ts diff --git a/src/Pathfinding/NavigationManager.ts b/src/Wolfie2D/Pathfinding/NavigationManager.ts similarity index 100% rename from src/Pathfinding/NavigationManager.ts rename to src/Wolfie2D/Pathfinding/NavigationManager.ts diff --git a/src/Pathfinding/NavigationPath.ts b/src/Wolfie2D/Pathfinding/NavigationPath.ts similarity index 100% rename from src/Pathfinding/NavigationPath.ts rename to src/Wolfie2D/Pathfinding/NavigationPath.ts diff --git a/src/Pathfinding/Navmesh.ts b/src/Wolfie2D/Pathfinding/Navmesh.ts similarity index 100% rename from src/Pathfinding/Navmesh.ts rename to src/Wolfie2D/Pathfinding/Navmesh.ts diff --git a/src/Physics/TestPhysicsManager.ts b/src/Wolfie2D/Physics/BasicPhysicsManager.ts similarity index 98% rename from src/Physics/TestPhysicsManager.ts rename to src/Wolfie2D/Physics/BasicPhysicsManager.ts index dc32486..8aec56b 100644 --- a/src/Physics/TestPhysicsManager.ts +++ b/src/Wolfie2D/Physics/BasicPhysicsManager.ts @@ -41,7 +41,7 @@ import AreaCollision from "../DataTypes/Physics/AreaCollision"; * Cons: * - Nodes that are processed early have movement priority over other nodes. This can lead to some undesirable interactions. */ -export default class TestPhysicsManager extends PhysicsManager { +export default class BasicPhysicsManager extends PhysicsManager { /** The array of static nodes */ protected staticNodes: Array; @@ -52,7 +52,7 @@ export default class TestPhysicsManager extends PhysicsManager { /** The array of tilemaps */ protected tilemaps: Array; - constructor(){ + constructor(options: Record){ super(); this.staticNodes = new Array(); this.dynamicNodes = new Array(); diff --git a/src/Physics/PhysicsManager.ts b/src/Wolfie2D/Physics/PhysicsManager.ts similarity index 100% rename from src/Physics/PhysicsManager.ts rename to src/Wolfie2D/Physics/PhysicsManager.ts diff --git a/src/Playback/Recorder.ts b/src/Wolfie2D/Playback/Recorder.ts similarity index 100% rename from src/Playback/Recorder.ts rename to src/Wolfie2D/Playback/Recorder.ts diff --git a/src/Rendering/Animations/AnimationManager.ts b/src/Wolfie2D/Rendering/Animations/AnimationManager.ts similarity index 100% rename from src/Rendering/Animations/AnimationManager.ts rename to src/Wolfie2D/Rendering/Animations/AnimationManager.ts diff --git a/src/Rendering/Animations/AnimationTypes.ts b/src/Wolfie2D/Rendering/Animations/AnimationTypes.ts similarity index 100% rename from src/Rendering/Animations/AnimationTypes.ts rename to src/Wolfie2D/Rendering/Animations/AnimationTypes.ts diff --git a/src/Rendering/Animations/TweenManager.ts b/src/Wolfie2D/Rendering/Animations/TweenManager.ts similarity index 100% rename from src/Rendering/Animations/TweenManager.ts rename to src/Wolfie2D/Rendering/Animations/TweenManager.ts diff --git a/src/Rendering/CanvasRenderer.ts b/src/Wolfie2D/Rendering/CanvasRenderer.ts similarity index 100% rename from src/Rendering/CanvasRenderer.ts rename to src/Wolfie2D/Rendering/CanvasRenderer.ts diff --git a/src/Rendering/CanvasRendering/GraphicRenderer.ts b/src/Wolfie2D/Rendering/CanvasRendering/GraphicRenderer.ts similarity index 100% rename from src/Rendering/CanvasRendering/GraphicRenderer.ts rename to src/Wolfie2D/Rendering/CanvasRendering/GraphicRenderer.ts diff --git a/src/Rendering/CanvasRendering/TilemapRenderer.ts b/src/Wolfie2D/Rendering/CanvasRendering/TilemapRenderer.ts similarity index 100% rename from src/Rendering/CanvasRendering/TilemapRenderer.ts rename to src/Wolfie2D/Rendering/CanvasRendering/TilemapRenderer.ts diff --git a/src/Rendering/CanvasRendering/UIElementRenderer.ts b/src/Wolfie2D/Rendering/CanvasRendering/UIElementRenderer.ts similarity index 100% rename from src/Rendering/CanvasRendering/UIElementRenderer.ts rename to src/Wolfie2D/Rendering/CanvasRendering/UIElementRenderer.ts diff --git a/src/Rendering/RenderingManager.ts b/src/Wolfie2D/Rendering/RenderingManager.ts similarity index 100% rename from src/Rendering/RenderingManager.ts rename to src/Wolfie2D/Rendering/RenderingManager.ts diff --git a/src/ResourceManager/ResourceManager.ts b/src/Wolfie2D/ResourceManager/ResourceManager.ts similarity index 100% rename from src/ResourceManager/ResourceManager.ts rename to src/Wolfie2D/ResourceManager/ResourceManager.ts diff --git a/src/Scene/Factories/CanvasNodeFactory.ts b/src/Wolfie2D/Scene/Factories/CanvasNodeFactory.ts similarity index 100% rename from src/Scene/Factories/CanvasNodeFactory.ts rename to src/Wolfie2D/Scene/Factories/CanvasNodeFactory.ts diff --git a/src/Scene/Factories/FactoryManager.ts b/src/Wolfie2D/Scene/Factories/FactoryManager.ts similarity index 100% rename from src/Scene/Factories/FactoryManager.ts rename to src/Wolfie2D/Scene/Factories/FactoryManager.ts diff --git a/src/Scene/Factories/TilemapFactory.ts b/src/Wolfie2D/Scene/Factories/TilemapFactory.ts similarity index 100% rename from src/Scene/Factories/TilemapFactory.ts rename to src/Wolfie2D/Scene/Factories/TilemapFactory.ts diff --git a/src/Scene/Layer.ts b/src/Wolfie2D/Scene/Layer.ts similarity index 100% rename from src/Scene/Layer.ts rename to src/Wolfie2D/Scene/Layer.ts diff --git a/src/Scene/Layers/ParallaxLayer.ts b/src/Wolfie2D/Scene/Layers/ParallaxLayer.ts similarity index 100% rename from src/Scene/Layers/ParallaxLayer.ts rename to src/Wolfie2D/Scene/Layers/ParallaxLayer.ts diff --git a/src/Scene/Layers/UILayer.ts b/src/Wolfie2D/Scene/Layers/UILayer.ts similarity index 100% rename from src/Scene/Layers/UILayer.ts rename to src/Wolfie2D/Scene/Layers/UILayer.ts diff --git a/src/Scene/Scene.ts b/src/Wolfie2D/Scene/Scene.ts similarity index 100% rename from src/Scene/Scene.ts rename to src/Wolfie2D/Scene/Scene.ts diff --git a/src/Scene/SceneManager.ts b/src/Wolfie2D/Scene/SceneManager.ts similarity index 100% rename from src/Scene/SceneManager.ts rename to src/Wolfie2D/Scene/SceneManager.ts diff --git a/src/Scene/SceneOptions.ts b/src/Wolfie2D/Scene/SceneOptions.ts similarity index 100% rename from src/Scene/SceneOptions.ts rename to src/Wolfie2D/Scene/SceneOptions.ts diff --git a/src/SceneGraph/SceneGraph.ts b/src/Wolfie2D/SceneGraph/SceneGraph.ts similarity index 100% rename from src/SceneGraph/SceneGraph.ts rename to src/Wolfie2D/SceneGraph/SceneGraph.ts diff --git a/src/SceneGraph/SceneGraphArray.ts b/src/Wolfie2D/SceneGraph/SceneGraphArray.ts similarity index 100% rename from src/SceneGraph/SceneGraphArray.ts rename to src/Wolfie2D/SceneGraph/SceneGraphArray.ts diff --git a/src/SceneGraph/SceneGraphQuadTree.ts b/src/Wolfie2D/SceneGraph/SceneGraphQuadTree.ts similarity index 100% rename from src/SceneGraph/SceneGraphQuadTree.ts rename to src/Wolfie2D/SceneGraph/SceneGraphQuadTree.ts diff --git a/src/SceneGraph/Viewport.ts b/src/Wolfie2D/SceneGraph/Viewport.ts similarity index 80% rename from src/SceneGraph/Viewport.ts rename to src/Wolfie2D/SceneGraph/Viewport.ts index 6cbf0d7..c09e679 100644 --- a/src/SceneGraph/Viewport.ts +++ b/src/Wolfie2D/SceneGraph/Viewport.ts @@ -4,7 +4,7 @@ import CanvasNode from "../Nodes/CanvasNode"; import MathUtils from "../Utils/MathUtils"; import Queue from "../DataTypes/Queue"; import AABB from "../DataTypes/Shapes/AABB"; -import InputReceiver from "../Input/InputReceiver"; +import Input from "../Input/Input"; import ParallaxLayer from "../Scene/Layers/ParallaxLayer"; import UILayer from "../Scene/Layers/UILayer"; @@ -37,7 +37,7 @@ export default class Viewport { /** The size of the canvas */ private canvasSize: Vec2; - constructor(){ + constructor(initialPosition: Vec2, canvasSize: Vec2){ this.view = new AABB(Vec2.ZERO, Vec2.ZERO); this.boundary = new AABB(Vec2.ZERO, Vec2.ZERO); this.lastPositions = new Queue(); @@ -45,6 +45,14 @@ export default class Viewport { this.scrollZoomEnabled = false; this.canvasSize = Vec2.ZERO; this.focus = Vec2.ZERO; + + // Set the center (and make the viewport stay there) + this.setCenter(initialPosition); + this.setFocus(initialPosition); + + // Set the size of the viewport + this.setSize(canvasSize); + this.setCanvasSize(canvasSize); } /** Enables the viewport to zoom in and out */ @@ -89,8 +97,7 @@ export default class Viewport { pos = new Vec2(vecOrX, y); } - this.lastPositions.clear(); - this.lastPositions.enqueue(pos); + this.view.center = pos; } /** @@ -213,13 +220,33 @@ export default class Viewport { this.following = node; } + updateView(): void { + if(this.lastPositions.getSize() > this.smoothingFactor){ + this.lastPositions.dequeue(); + } + + // Get the average of the last 10 positions + let pos = Vec2.ZERO; + this.lastPositions.forEach(position => pos.add(position)); + pos.scale(1/this.lastPositions.getSize()); + + // Set this position either to the object or to its bounds + pos.x = MathUtils.clamp(pos.x, this.boundary.left + this.view.hw, this.boundary.right - this.view.hw); + pos.y = MathUtils.clamp(pos.y, this.boundary.top + this.view.hh, this.boundary.bottom - this.view.hh); + + // Assure there are no lines in the tilemap + pos.x = Math.floor(pos.x); + pos.y = Math.floor(pos.y); + + this.view.center.copy(pos); + } + update(deltaT: number): void { // If zoom is enabled if(this.scrollZoomEnabled){ - let input = InputReceiver.getInstance(); - if(input.didJustScroll()){ + if(Input.didJustScroll()){ let currentSize = this.view.getHalfSize().clone(); - if(input.getScrollDirection() < 0){ + if(Input.getScrollDirection() < 0){ // Zoom in currentSize.scale(1/this.ZOOM_FACTOR); } else { @@ -247,43 +274,10 @@ export default class Viewport { if(this.following){ // Update our list of previous positions this.lastPositions.enqueue(this.following.position.clone()); - if(this.lastPositions.getSize() > this.smoothingFactor){ - this.lastPositions.dequeue(); - } - - // Get the average of the last 10 positions - let pos = Vec2.ZERO; - this.lastPositions.forEach(position => pos.add(position)); - pos.scale(1/this.lastPositions.getSize()); - - // Set this position either to the object or to its bounds - pos.x = MathUtils.clamp(pos.x, this.boundary.left + this.view.hw, this.boundary.right - this.view.hw); - pos.y = MathUtils.clamp(pos.y, this.boundary.top + this.view.hh, this.boundary.bottom - this.view.hh); - - // Assure there are no lines in the tilemap - pos.x = Math.floor(pos.x); - pos.y = Math.floor(pos.y); - - this.view.center.copy(pos); } else { this.lastPositions.enqueue(this.focus); - if(this.lastPositions.getSize() > this.smoothingFactor){ - this.lastPositions.dequeue(); - } - - let pos = Vec2.ZERO; - this.lastPositions.forEach(position => pos.add(position)); - pos.scale(1/this.lastPositions.getSize()); - - // Set this position either to the object or to its bounds - pos.x = MathUtils.clamp(pos.x, this.boundary.left + this.view.hw, this.boundary.right - this.view.hw); - pos.y = MathUtils.clamp(pos.y, this.boundary.top + this.view.hh, this.boundary.bottom - this.view.hh); - - // Assure there are no lines in the tilemap - pos.x = Math.floor(pos.x); - pos.y = Math.floor(pos.y); - - this.view.center.copy(pos); } + + this.updateView(); } } \ No newline at end of file diff --git a/src/Sound/AudioManager.ts b/src/Wolfie2D/Sound/AudioManager.ts similarity index 100% rename from src/Sound/AudioManager.ts rename to src/Wolfie2D/Sound/AudioManager.ts diff --git a/src/Utils/ArrayUtils.ts b/src/Wolfie2D/Utils/ArrayUtils.ts similarity index 100% rename from src/Utils/ArrayUtils.ts rename to src/Wolfie2D/Utils/ArrayUtils.ts diff --git a/src/Utils/Color.ts b/src/Wolfie2D/Utils/Color.ts similarity index 100% rename from src/Utils/Color.ts rename to src/Wolfie2D/Utils/Color.ts diff --git a/src/Utils/EaseFunctions.ts b/src/Wolfie2D/Utils/EaseFunctions.ts similarity index 100% rename from src/Utils/EaseFunctions.ts rename to src/Wolfie2D/Utils/EaseFunctions.ts diff --git a/src/Utils/GraphUtils.ts b/src/Wolfie2D/Utils/GraphUtils.ts similarity index 100% rename from src/Utils/GraphUtils.ts rename to src/Wolfie2D/Utils/GraphUtils.ts diff --git a/src/Utils/MathUtils.ts b/src/Wolfie2D/Utils/MathUtils.ts similarity index 100% rename from src/Utils/MathUtils.ts rename to src/Wolfie2D/Utils/MathUtils.ts diff --git a/src/Utils/Rand/Perlin.ts b/src/Wolfie2D/Utils/Rand/Perlin.ts similarity index 100% rename from src/Utils/Rand/Perlin.ts rename to src/Wolfie2D/Utils/Rand/Perlin.ts diff --git a/src/Utils/RandUtils.ts b/src/Wolfie2D/Utils/RandUtils.ts similarity index 100% rename from src/Utils/RandUtils.ts rename to src/Wolfie2D/Utils/RandUtils.ts diff --git a/src/Utils/SortingUtils.ts b/src/Wolfie2D/Utils/SortingUtils.ts similarity index 100% rename from src/Utils/SortingUtils.ts rename to src/Wolfie2D/Utils/SortingUtils.ts diff --git a/src/Utils/StringUtils.ts b/src/Wolfie2D/Utils/StringUtils.ts similarity index 100% rename from src/Utils/StringUtils.ts rename to src/Wolfie2D/Utils/StringUtils.ts diff --git a/src/_DemoClasses/Boids/Boid.ts b/src/_DemoClasses/Boids/Boid.ts deleted file mode 100644 index 3d892ca..0000000 --- a/src/_DemoClasses/Boids/Boid.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Vec2 from "../../DataTypes/Vec2"; -import Graphic from "../../Nodes/Graphic"; -import BoidController from "./BoidController"; -import FlockBehavior from "./FlockBehavior"; - -export default class Boid extends Graphic { - direction: Vec2 = Vec2.UP.rotateCCW(Math.random()*2*Math.PI); - acceleration: Vec2 = Vec2.ZERO; - velocity: Vec2 = Vec2.ZERO; - - //ai: BoidController; - fb: FlockBehavior; - - constructor(position: Vec2){ - super(); - this.position = position; - //this.ai = new BoidController(this); - } - - update(deltaT: number){ - this.ai.update(deltaT); - } - - render(ctx: CanvasRenderingContext2D): void { - let origin = this.scene.getViewTranslation(this); - let zoom = this.scene.getViewScale(); - - let dirVec = this.direction.scaled(this.size.x, this.size.y); - let finVec1 = this.direction.clone().rotateCCW(Math.PI/2).scale(this.size.x/2, this.size.y/2).sub(this.direction.scaled(this.size.x/1.5, this.size.y/1.5)); - let finVec2 = this.direction.clone().rotateCCW(-Math.PI/2).scale(this.size.x/2, this.size.y/2).sub(this.direction.scaled(this.size.x/1.5, this.size.y/1.5)); - - ctx.lineWidth = 1; - ctx.fillStyle = this.color.toString(); - ctx.beginPath(); - ctx.moveTo((this.position.x - origin.x + dirVec.x)*zoom, (this.position.y - origin.y + dirVec.y)*zoom); - ctx.lineTo((this.position.x - origin.x + finVec1.x)*zoom, (this.position.y - origin.y + finVec1.y)*zoom); - ctx.lineTo((this.position.x - origin.x - dirVec.x/3)*zoom, (this.position.y - origin.y - dirVec.y/3)*zoom); - ctx.lineTo((this.position.x - origin.x + finVec2.x)*zoom, (this.position.y - origin.y + finVec2.y)*zoom); - ctx.lineTo((this.position.x - origin.x + dirVec.x)*zoom, (this.position.y - origin.y + dirVec.y)*zoom); - ctx.fill(); - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Boids/BoidController.ts b/src/_DemoClasses/Boids/BoidController.ts deleted file mode 100644 index b5d1c45..0000000 --- a/src/_DemoClasses/Boids/BoidController.ts +++ /dev/null @@ -1,32 +0,0 @@ -import StateMachine from "../../DataTypes/State/StateMachine"; -import { CustomGameEventType } from "../CustomGameEventType"; -import Boid from "./Boid"; -import BoidBehavior from "./BoidStates/BoidBehavior"; -import RunAwayFromPlayer from "./BoidStates/RunAwayFromPlayer"; - -export default class BoidController extends StateMachine { - constructor(boid: Boid){ - super(); - - // Normal Boid Behavior - let normalBehavior = new BoidBehavior(this, boid, 3, 1, 3); - this.addState("normal", normalBehavior); - - // Run away from player behavior - let runAway = new RunAwayFromPlayer(this, boid); - this.addState("runAway", runAway); - - // Sign up to be warned of player movement - this.receiver.subscribe(CustomGameEventType.PLAYER_MOVE); - - this.initialize("normal"); - } - - changeState(stateName: string): void { - if(stateName === "runAway"){ - this.stack.push(this.stateMap.get(stateName)); - } - - super.changeState(stateName); - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Boids/BoidStates/BoidBehavior.ts b/src/_DemoClasses/Boids/BoidStates/BoidBehavior.ts deleted file mode 100644 index 834f8b2..0000000 --- a/src/_DemoClasses/Boids/BoidStates/BoidBehavior.ts +++ /dev/null @@ -1,83 +0,0 @@ -import State from "../../../DataTypes/State/State"; -import StateMachine from "../../../DataTypes/State/StateMachine"; -import Vec2 from "../../../DataTypes/Vec2"; -import GameEvent from "../../../Events/GameEvent"; -import MathUtils from "../../../Utils/MathUtils"; -import { CustomGameEventType } from "../../CustomGameEventType"; -import Boid from "../Boid"; - -export default class BoidBehavior extends State { - actor: Boid; - separationFactor: number; - alignmentFactor: number; - cohesionFactor: number; - - static MIN_SPEED: number = 80; - static START_SPEED: number = 90; - static MAX_SPEED: number = 100; - static MAX_STEER_FORCE: number = 300; - - constructor(parent: StateMachine, actor: Boid, separationFactor: number, alignmentFactor: number, cohesionFactor: number){ - super(parent); - this.actor = actor; - this.separationFactor = separationFactor; - this.alignmentFactor = alignmentFactor; - this.cohesionFactor = cohesionFactor; - } - - onEnter(): void { - // Do nothing special - } - - handleInput(event: GameEvent): void { - if(event.type === CustomGameEventType.PLAYER_MOVE){ - if(this.actor.position.distanceSqTo(event.data.get("position")) < 50*50){ - // If player moved and we're close, change state - this.finished("runAway"); - } - } - } - - onExit(): void { - // Do nothing special - } - - update(deltaT: number): void { - if(this.actor.velocity.x === 0 && this.actor.velocity.y === 0){ - this.actor.velocity = this.actor.direction.scaled(BoidBehavior.START_SPEED); - } - - // Only update as boid if it has neighbors - if(this.actor.fb.hasNeighbors){ - let flockCenter = this.actor.fb.flockCenter; - let flockHeading = this.actor.fb.flockHeading; - let separationHeading = this.actor.fb.separationHeading; - - let offsetToFlockmateCenter = flockCenter.sub(this.actor.position); - - let separationForce = this.steerTowards(separationHeading).scale(this.separationFactor); - let alignmentForce = this.steerTowards(flockHeading).scale(this.alignmentFactor); - let cohesionForce = this.steerTowards(offsetToFlockmateCenter).scale(this.cohesionFactor); - - this.actor.acceleration = Vec2.ZERO; - this.actor.acceleration.add(separationForce).add(alignmentForce).add(cohesionForce); - this.actor.velocity.add(this.actor.acceleration.scaled(deltaT)); - let speed = this.actor.velocity.mag(); - this.actor.velocity.normalize(); - this.actor.direction = this.actor.velocity.clone(); - speed = MathUtils.clamp(speed, BoidBehavior.MIN_SPEED, BoidBehavior.MAX_SPEED); - this.actor.velocity.scale(speed); - } - - // Update the position - this.actor.position.add(this.actor.velocity.scaled(deltaT)); - this.actor.position.x = (this.actor.position.x + this.actor.getScene().getWorldSize().x)%this.actor.getScene().getWorldSize().x; - this.actor.position.y = (this.actor.position.y + this.actor.getScene().getWorldSize().y)%this.actor.getScene().getWorldSize().y; - } - - steerTowards(vec: Vec2){ - let v = vec.normalize().scale(BoidBehavior.MAX_SPEED).sub(this.actor.velocity); - return MathUtils.clampMagnitude(v, BoidBehavior.MAX_STEER_FORCE); - } - -} \ No newline at end of file diff --git a/src/_DemoClasses/Boids/BoidStates/RunAwayFromPlayer.ts b/src/_DemoClasses/Boids/BoidStates/RunAwayFromPlayer.ts deleted file mode 100644 index 1c01b72..0000000 --- a/src/_DemoClasses/Boids/BoidStates/RunAwayFromPlayer.ts +++ /dev/null @@ -1,81 +0,0 @@ -import State from "../../../DataTypes/State/State"; -import StateMachine from "../../../DataTypes/State/StateMachine"; -import Vec2 from "../../../DataTypes/Vec2"; -import GameEvent from "../../../Events/GameEvent"; -import MathUtils from "../../../Utils/MathUtils"; -import { CustomGameEventType } from "../../CustomGameEventType"; -import Boid from "../Boid"; - -export default class RunAwayFromPlayer extends State { - actor: Boid; - runAwayDirection: Vec2; - lastPlayerPosition: Vec2; - - timeElapsed: number; - - static RUN_AWAY_SPEED: number = 120; - static MAX_STEER_FORCE: number = 300; - static FEAR_RADIUS: number = 75; - - constructor(parent: StateMachine, actor: Boid){ - super(parent); - this.actor = actor; - } - - onEnter(): void { - this.runAwayDirection = Vec2.ZERO; - this.lastPlayerPosition = Vec2.INF; - this.timeElapsed = 0; - } - - handleInput(event: GameEvent): void { - if(event.type === CustomGameEventType.PLAYER_MOVE){ - this.lastPlayerPosition.copy(event.data.get("position")); - - if(this.actor.position.distanceSqTo(this.lastPlayerPosition) - < RunAwayFromPlayer.FEAR_RADIUS*RunAwayFromPlayer.FEAR_RADIUS){ - // Reset our run away timer - this.timeElapsed = 0; - - // Update the run away direction - this.runAwayDirection.copy(this.actor.position).sub(event.data.get("position")).normalize(); - } - } - } - - update(deltaT: number): void { - this.timeElapsed += deltaT; - - // Run away for at least 500 ms - if(this.timeElapsed > 0.5){ - // If it's been long enough, go back to what we were doing before - this.finished("previous"); - } - - // Move away from the player - let force = this.steerTowards(this.runAwayDirection.clone()).scaled(10); - - this.actor.acceleration = force; - this.actor.velocity.add(this.actor.acceleration.scaled(deltaT)); - let speed = this.actor.velocity.mag(); - this.actor.velocity.normalize(); - this.actor.direction = this.actor.velocity.clone(); - speed = MathUtils.clamp(speed, RunAwayFromPlayer.RUN_AWAY_SPEED, RunAwayFromPlayer.RUN_AWAY_SPEED); - this.actor.velocity.scale(speed); - - // Update the position - this.actor.position.add(this.actor.velocity.scaled(deltaT)); - this.actor.position.x = (this.actor.position.x + this.actor.getScene().getWorldSize().x)%this.actor.getScene().getWorldSize().x; - this.actor.position.y = (this.actor.position.y + this.actor.getScene().getWorldSize().y)%this.actor.getScene().getWorldSize().y; - } - - onExit(): void { - - } - - steerTowards(vec: Vec2){ - let v = vec.normalize().scale(RunAwayFromPlayer.RUN_AWAY_SPEED).sub(this.actor.velocity); - return MathUtils.clampMagnitude(v, RunAwayFromPlayer.MAX_STEER_FORCE); - } - -} \ No newline at end of file diff --git a/src/_DemoClasses/Boids/FlockBehavior.ts b/src/_DemoClasses/Boids/FlockBehavior.ts deleted file mode 100644 index 58fbf21..0000000 --- a/src/_DemoClasses/Boids/FlockBehavior.ts +++ /dev/null @@ -1,80 +0,0 @@ -import AABB from "../../DataTypes/Shapes/AABB"; -import Vec2 from "../../DataTypes/Vec2"; -import Point from "../../Nodes/Graphics/Point"; -import Scene from "../../Scene/Scene"; -import Color from "../../Utils/Color"; -import Boid from "./Boid"; - -export default class FlockBehavior { - scene: Scene; - actor: Boid; - flock: Array; - visibleRegion: AABB; - avoidRadius: number; - hasNeighbors: boolean; - flockCenter: Vec2; - flockHeading: Vec2; - separationHeading: Vec2; - - constructor(scene: Scene, actor: Boid, flock: Array, visionRange: number, avoidRadius: number) { - this.scene = scene; - this.actor = actor; - this.flock = flock; - - this.visibleRegion = new AABB(this.actor.position.clone(), new Vec2(visionRange, visionRange)); - this.avoidRadius = avoidRadius; - } - - update(): void { - - // Update the visible region - this.visibleRegion.center.copy(this.actor.position); - - let neighbors = this.scene.getSceneGraph().getNodesInRegion(this.visibleRegion); - - neighbors = neighbors.filter(neighbor => { - return (neighbor instanceof Boid) - && (neighbor !== this.actor) - && this.actor.direction.dot(neighbor.position.clone().sub(this.actor.position).normalize()) > -0.866; - }); - - if(neighbors.length <= 0){ - this.hasNeighbors = false; - return; - } else { - this.hasNeighbors = true; - } - - // Draw a group - if(this.actor.id < 1){ - this.actor.setColor(Color.GREEN); - for(let neighbor of neighbors){ - if(neighbor === this.actor) continue; - (neighbor).setColor(Color.BLUE) - } - } - - let flockCenter = Vec2.ZERO; - let flockHeading = Vec2.ZERO; - let separationHeading = Vec2.ZERO; - - for(let neighbor of neighbors){ - let neighborPos = neighbor.position; - flockCenter.add(neighborPos); - - flockHeading.add((neighbor).direction); - - let dist = this.actor.position.distanceSqTo(neighborPos); - if(dist < this.avoidRadius*this.avoidRadius){ - separationHeading.add(this.actor.position.clone().sub(neighborPos).scale(1/dist)); - } - } - - flockCenter.scale(1/neighbors.length); - - this.flockCenter = flockCenter; - this.flockHeading = flockHeading; - this.separationHeading = separationHeading; - } - -} \ No newline at end of file diff --git a/src/_DemoClasses/CustomGameEventType.ts b/src/_DemoClasses/CustomGameEventType.ts deleted file mode 100644 index d53efbb..0000000 --- a/src/_DemoClasses/CustomGameEventType.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum CustomGameEventType { - PLAYER_MOVE = "player_move", - PLAYER_JUMP = "player_jump", -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/Afraid.ts b/src/_DemoClasses/Enemies/Afraid.ts deleted file mode 100644 index ca68dd2..0000000 --- a/src/_DemoClasses/Enemies/Afraid.ts +++ /dev/null @@ -1,31 +0,0 @@ -import GameEvent from "../../Events/GameEvent"; -import MathUtils from "../../Utils/MathUtils"; -import { CustomGameEventType } from "../CustomGameEventType"; -import { GoombaStates } from "./GoombaController"; -import OnGround from "./OnGround"; - -export default class Afraid extends OnGround { - - onEnter(): void {} - - handleInput(event: GameEvent): void { - if(event.type === CustomGameEventType.PLAYER_MOVE){ - let pos = event.data.get("position"); - this.parent.direction.x = MathUtils.sign(this.owner.position.x - pos.x); - } else if(event.type === "playerHitCoinBlock") { - if(event.data.get("collision").firstContact.y < 1 && event.data.get("node").collisionShape.center.y > event.data.get("other").collisionShape.center.y){ - this.finished("previous"); - } - } - } - - update(deltaT: number): void { - super.update(deltaT); - - this.parent.velocity.x = this.parent.direction.x * this.parent.speed * 1.2; - - this.owner.move(this.parent.velocity.scaled(deltaT)); - } - - onExit(): void {} -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/GoombaController.ts b/src/_DemoClasses/Enemies/GoombaController.ts deleted file mode 100644 index 5933eca..0000000 --- a/src/_DemoClasses/Enemies/GoombaController.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { CustomGameEventType } from "../CustomGameEventType"; -import Idle from "../Enemies/Idle"; -import Jump from "../Enemies/Jump"; -import Walk from "../Enemies/Walk"; -import Afraid from "../Enemies/Afraid"; -import GameNode from "../../Nodes/GameNode"; -import Vec2 from "../../DataTypes/Vec2"; -import StateMachineAI from "../../AI/StateMachineAI"; -import GoombaState from "./GoombaState"; - -export enum GoombaStates { - IDLE = "idle", - WALK = "walk", - JUMP = "jump", - PREVIOUS = "previous", - AFRAID = "afraid" -} - -export default class GoombaController extends StateMachineAI { - owner: GameNode; - jumpy: boolean; - direction: Vec2 = Vec2.ZERO; - velocity: Vec2 = Vec2.ZERO; - speed: number = 200; - - initializeAI(owner: GameNode, options: Record){ - this.owner = owner; - this.jumpy = options.jumpy ? options.jumpy : false; - - this.receiver.subscribe(CustomGameEventType.PLAYER_MOVE); - this.receiver.subscribe("playerHitCoinBlock"); - if(this.jumpy){ - this.receiver.subscribe(CustomGameEventType.PLAYER_JUMP); - this.speed = 100; - } - - let idle = new Idle(this, owner); - this.addState(GoombaStates.IDLE, idle); - let walk = new Walk(this, owner); - this.addState(GoombaStates.WALK, walk); - let jump = new Jump(this, owner); - this.addState(GoombaStates.JUMP, jump); - - this.initialize(GoombaStates.IDLE); - } - - changeState(stateName: string): void { - - if(stateName === GoombaStates.JUMP || stateName === GoombaStates.AFRAID){ - this.stack.push(this.stateMap.get(stateName)); - } - super.changeState(stateName); - } - - update(deltaT: number): void { - super.update(deltaT); - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/GoombaState.ts b/src/_DemoClasses/Enemies/GoombaState.ts deleted file mode 100644 index 82c82eb..0000000 --- a/src/_DemoClasses/Enemies/GoombaState.ts +++ /dev/null @@ -1,24 +0,0 @@ -import State from "../../DataTypes/State/State"; -import StateMachine from "../../DataTypes/State/StateMachine"; -import GameEvent from "../../Events/GameEvent"; -import GameNode from "../../Nodes/GameNode"; -import GoombaController, { GoombaStates } from "./GoombaController"; - -export default abstract class GoombaState extends State { - owner: GameNode; - gravity: number = 1000; - parent: GoombaController - - constructor(parent: StateMachine, owner: GameNode){ - super(parent); - - this.owner = owner; - } - - handleInput(event: GameEvent): void {} - - update(deltaT: number): void { - // Do gravity - this.parent.velocity.y += this.gravity*deltaT; - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/Idle.ts b/src/_DemoClasses/Enemies/Idle.ts deleted file mode 100644 index a908361..0000000 --- a/src/_DemoClasses/Enemies/Idle.ts +++ /dev/null @@ -1,34 +0,0 @@ -import Vec2 from "../../DataTypes/Vec2"; -import GameEvent from "../../Events/GameEvent"; -import AnimatedSprite from "../../Nodes/Sprites/AnimatedSprite"; -import { CustomGameEventType } from "../CustomGameEventType"; -import { GoombaStates } from "./GoombaController"; -import OnGround from "./OnGround"; - -export default class Idle extends OnGround { - onEnter(): void { - this.parent.speed = this.parent.speed; - (this.owner).animation.play("IDLE", true); - } - - onExit(): void { - (this.owner).animation.stop(); - } - - handleInput(event: GameEvent) { - if(event.type === CustomGameEventType.PLAYER_MOVE){ - let pos = event.data.get("position"); - if(this.owner.position.x - pos.x < (64*10)){ - this.finished(GoombaStates.WALK); - } - } - } - - update(deltaT: number): void { - super.update(deltaT); - - this.parent.velocity.x = 0; - - this.owner.move(this.parent.velocity.scaled(deltaT)); - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/Jump.ts b/src/_DemoClasses/Enemies/Jump.ts deleted file mode 100644 index d4258fb..0000000 --- a/src/_DemoClasses/Enemies/Jump.ts +++ /dev/null @@ -1,34 +0,0 @@ -import GameEvent from "../../Events/GameEvent"; -import AnimatedSprite from "../../Nodes/Sprites/AnimatedSprite"; -import { GoombaStates } from "./GoombaController"; -import GoombaState from "./GoombaState"; - -export default class Jump extends GoombaState { - - onEnter(): void { - (this.owner).animation.play("JUMP", true); - (this.owner).tweens.play("jump", true); - this.gravity = 500; - } - - update(deltaT: number): void { - super.update(deltaT); - - if(this.owner.onGround){ - this.finished(GoombaStates.PREVIOUS); - } - - if(this.owner.onCeiling){ - this.parent.velocity.y = 0; - } - - this.parent.velocity.x += this.parent.direction.x * this.parent.speed/3.5 - 0.3*this.parent.velocity.x; - - this.owner.move(this.parent.velocity.scaled(deltaT)); - } - - onExit(): void { - (this.owner).animation.stop(); - (this.owner).tweens.stop("jump"); - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/OnGround.ts b/src/_DemoClasses/Enemies/OnGround.ts deleted file mode 100644 index 17be81c..0000000 --- a/src/_DemoClasses/Enemies/OnGround.ts +++ /dev/null @@ -1,24 +0,0 @@ -import GameEvent from "../../Events/GameEvent"; -import { GoombaStates } from "./GoombaController"; -import GoombaState from "./GoombaState"; - -export default class OnGround extends GoombaState { - onEnter(): void {} - - handleInput(event: GameEvent): void { - super.handleInput(event); - } - - update(deltaT: number): void { - if(this.parent.velocity.y > 0){ - this.parent.velocity.y = 0; - } - super.update(deltaT); - - if(!this.owner.onGround){ - this.finished(GoombaStates.JUMP); - } - } - - onExit(): void {} -} \ No newline at end of file diff --git a/src/_DemoClasses/Enemies/Walk.ts b/src/_DemoClasses/Enemies/Walk.ts deleted file mode 100644 index 2b3469d..0000000 --- a/src/_DemoClasses/Enemies/Walk.ts +++ /dev/null @@ -1,43 +0,0 @@ -import Vec2 from "../../DataTypes/Vec2"; -import AnimatedSprite from "../../Nodes/Sprites/AnimatedSprite"; -import { GoombaStates } from "./GoombaController"; -import OnGround from "./OnGround"; - -export default class Walk extends OnGround { - time: number; - - onEnter(): void { - if(this.parent.direction.isZero()){ - this.parent.direction = new Vec2(-1, 0); - (this.owner).invertX = true; - } - - (this.owner).animation.play("WALK", true); - - this.time = Date.now(); - } - - update(deltaT: number): void { - super.update(deltaT); - - if(this.owner.onWall){ - // Flip around - this.parent.direction.x *= -1; - (this.owner).invertX = !(this.owner).invertX; - } - - if(this.parent.jumpy && (Date.now() - this.time > 500)){ - this.finished(GoombaStates.JUMP); - this.parent.velocity.y = -300; - } - - this.parent.velocity.x = this.parent.direction.x * this.parent.speed; - - this.owner.move(this.parent.velocity.scaled(deltaT)); - } - - onExit(): void { - (this.owner).animation.stop(); - - } -} \ No newline at end of file diff --git a/src/_DemoClasses/Mario/Level1.ts b/src/_DemoClasses/Mario/Level1.ts deleted file mode 100644 index 6148519..0000000 --- a/src/_DemoClasses/Mario/Level1.ts +++ /dev/null @@ -1,164 +0,0 @@ -import Vec2 from "../../DataTypes/Vec2"; -import GameNode from "../../Nodes/GameNode"; -import { GraphicType } from "../../Nodes/Graphics/GraphicTypes"; -import Label from "../../Nodes/UIElements/Label"; -import { UIElementType } from "../../Nodes/UIElements/UIElementTypes"; -import ParallaxLayer from "../../Scene/Layers/ParallaxLayer"; -import Scene from "../../Scene/Scene"; -import PlayerController from "../Player/PlayerController"; -import GoombaController from "../Enemies/GoombaController"; -import OrthogonalTilemap from "../../Nodes/Tilemaps/OrthogonalTilemap"; -import AnimatedSprite from "../../Nodes/Sprites/AnimatedSprite"; -import Debug from "../../Debug/Debug"; -import { EaseFunctionType } from "../../Utils/EaseFunctions"; -import Sprite from "../../Nodes/Sprites/Sprite"; - -export enum MarioEvents { - PLAYER_HIT_COIN = "PlayerHitCoin", - PLAYER_HIT_COIN_BLOCK = "PlayerHitCoinBlock" -} - -export default class Level1 extends Scene { - player: AnimatedSprite; - coinCount: number = 0; - coinCountLabel: Label; - livesCount: number = 3; - livesCountLabel: Label; - bg: Sprite; - - loadScene(): void { - this.load.image("background", "/assets/sprites/2bitbackground.png"); - this.load.image("coin", "/assets/sprites/coin.png"); - this.load.tilemap("level1", "/assets/tilemaps/2bitlevel1.json"); - this.load.spritesheet("player", "assets/spritesheets/walking.json"); - this.load.spritesheet("hopper", "assets/spritesheets/hopper.json"); - this.load.spritesheet("bunny", "assets/spritesheets/ghostBunny.json"); - } - - startScene(): void { - // Add a background layer and set the background image on it - this.addParallaxLayer("bg", new Vec2(0.25, 0), -100); - let bg = this.add.sprite("background", "bg"); - bg.scale.set(2, 2); - bg.position.set(bg.boundary.halfSize.x, 16); - this.bg = bg; - this.bg.toString = () => "BackgroundImage"; - - let tilemap = this.add.tilemap("level1", new Vec2(2, 2))[0].getItems()[0]; - //tilemap.position.set(tilemap.size.x*tilemap.scale.x/2, tilemap.size.y*tilemap.scale.y/2); - tilemap.position.set(0, 0); - this.viewport.setBounds(0, 0, 128*32, 20*32); - - // Add a layer behind the tilemap for coin animation - this.addLayer("coinLayer", -50); - - // Add the player (a rect for now) - // this.player = this.add.graphic(GraphicType.RECT, "Main", {position: new Vec2(192, 1152), size: new Vec2(64, 64)}); - this.player = this.add.animatedSprite("player", "Main"); - this.player.scale.set(2, 2); - this.player.position.set(5*32, 18*32); - this.player.addPhysics(); - this.player.addAI(PlayerController, {playerType: "platformer", tilemap: "Main"}); - - // Add triggers on colliding with coins or coinBlocks - this.player.addTrigger("coin", MarioEvents.PLAYER_HIT_COIN); - this.player.addTrigger("coinBlock", MarioEvents.PLAYER_HIT_COIN_BLOCK); - this.player.setPhysicsLayer("player"); - - this.player.tweens.add("flip", { - startDelay: 0, - duration: 500, - effects: [ - { - property: "rotation", - start: 0, - end: 2*Math.PI, - ease: EaseFunctionType.IN_OUT_QUAD - } - ] - }); - - this.receiver.subscribe([MarioEvents.PLAYER_HIT_COIN, MarioEvents.PLAYER_HIT_COIN_BLOCK]); - - this.viewport.follow(this.player); - this.viewport.enableZoom(); - this.viewport.setZoomLevel(2); - - // Add enemies - for(let pos of [{x: 21, y: 18}]){//, {x: 30, y: 18}, {x: 37, y: 18}, {x: 41, y: 18}, {x: 105, y: 8}, {x: 107, y: 8}, {x: 125, y: 18}]){ - let bunny = this.add.animatedSprite("bunny", "Main"); - bunny.position.set(pos.x*32, pos.y*32); - bunny.scale.set(2, 2); - bunny.addPhysics(); - bunny.addAI(GoombaController, {jumpy: false}); - bunny.setPhysicsLayer("enemy"); - } - - for(let pos of [{x: 67, y: 18}]){//, {x: 86, y: 21}, {x: 128, y: 18}]){ - let hopper = this.add.animatedSprite("hopper", "Main"); - hopper.position.set(pos.x*32, pos.y*32); - hopper.scale.set(2, 2); - hopper.addPhysics(); - hopper.addAI(GoombaController, {jumpy: true}); - hopper.setPhysicsLayer("enemy"); - hopper.tweens.add("jump", { - startDelay: 0, - duration: 300, - effects: [ - { - property: "rotation", - resetOnComplete: true, - start: -3.14/8, - end: 3.14/8, - ease: EaseFunctionType.IN_OUT_SINE - } - ], - reverseOnComplete: true, - }); - } - - // Add UI - this.addUILayer("UI"); - - this.coinCountLabel =