From bd961d04d319b6abe0280ca38a1a1aa6539ae9d6 Mon Sep 17 00:00:00 2001 From: Joe Weaver Date: Tue, 18 Aug 2020 14:48:47 -0400 Subject: [PATCH] fixed issues with tilemaps and physics --- src/DataTypes/Tilesets/TiledData.ts | 7 +++ src/GameState/Factories/TilemapFactory.ts | 21 ++++---- src/GameState/GameState.ts | 2 +- src/GameState/Scene.ts | 8 +-- src/Input/InputReceiver.ts | 6 ++- src/Nodes/Tilemap.ts | 10 ++++ src/Nodes/Tilemaps/OrthogonalTilemap.ts | 9 ++++ src/Physics/PhysicsManager.ts | 28 +++++++++- src/Physics/PhysicsNode.ts | 5 ++ src/Player.ts | 66 +++++++++++++++++++---- src/main.ts | 14 +---- 11 files changed, 137 insertions(+), 39 deletions(-) diff --git a/src/DataTypes/Tilesets/TiledData.ts b/src/DataTypes/Tilesets/TiledData.ts index 89fe3c2..fb5b36c 100644 --- a/src/DataTypes/Tilesets/TiledData.ts +++ b/src/DataTypes/Tilesets/TiledData.ts @@ -8,6 +8,12 @@ export class TiledTilemapData { tilesets: TiledTilesetData[]; } +export class TiledLayerProperty { + name: string; + type: string; + value: any; +} + export class TiledTilesetData { columns: number; tilewidth: number; @@ -31,5 +37,6 @@ export class TiledLayerData { name: string; opacity: number; visible: boolean; + properties: TiledLayerProperty[]; } diff --git a/src/GameState/Factories/TilemapFactory.ts b/src/GameState/Factories/TilemapFactory.ts index ea8a764..37dc194 100644 --- a/src/GameState/Factories/TilemapFactory.ts +++ b/src/GameState/Factories/TilemapFactory.ts @@ -27,16 +27,19 @@ export default class TilemapFactory { // Add to scene this.scene.addTilemap(tilemap); - let worldSize = tilemap.getWorldSize(); - let tileSize = tilemap.getTileSize(); + if(tilemap.isCollidable()){ + // Create colliders + let worldSize = tilemap.getWorldSize(); + let tileSize = tilemap.getTileSize(); - tilemap.forEachTile((tileIndex: number, i: number) => { - if(tileIndex !== 0){ - let x = (i % worldSize.x) * tileSize.x * 4; - let y = Math.floor(i / worldSize.x) * tileSize.y * 4; - this.scene.physics.add(StaticBody, new Vec2(x, y), new Vec2(tileSize.x * 4, tileSize.y * 4)); - } - }); + tilemap.forEachTile((tileIndex: number, i: number) => { + if(tileIndex !== 0){ + let x = (i % worldSize.x) * tileSize.x * 4; + let y = Math.floor(i / worldSize.x) * tileSize.y * 4; + this.scene.physics.add(StaticBody, new Vec2(x, y), new Vec2(tileSize.x * 4, tileSize.y * 4)); + } + }); + } // Load images for the tilesets tilemap.getTilesets().forEach(tileset => { diff --git a/src/GameState/GameState.ts b/src/GameState/GameState.ts index 01bfe92..8963baf 100644 --- a/src/GameState/GameState.ts +++ b/src/GameState/GameState.ts @@ -13,7 +13,7 @@ export default class GameState{ this.worldSize = new Vec2(1600, 1000); this.viewport = new Viewport(); this.viewport.setSize(800, 500); - this.viewport.setBounds(0, 0, 1600, 1000); + this.viewport.setBounds(0, 0, 2560, 1280); } createScene(): Scene{ diff --git a/src/GameState/Scene.ts b/src/GameState/Scene.ts index 9d1a95f..44fcaed 100644 --- a/src/GameState/Scene.ts +++ b/src/GameState/Scene.ts @@ -102,15 +102,15 @@ export default class Scene { let origin = new Vec2(viewportOrigin.x*this.parallax.x, viewportOrigin.y*this.parallax.y); let size = this.viewport.getSize(); - // Render visible set - visibleSet.forEach(node => node.render(ctx, origin)); - // Render tilemaps this.tilemaps.forEach(tilemap => { - if(tilemap.isReady()){ + if(tilemap.isReady() && tilemap.isVisible()){ tilemap.render(ctx, origin, size); } }); + + // Render visible set + visibleSet.forEach(node => node.render(ctx, origin)); } } } \ No newline at end of file diff --git a/src/Input/InputReceiver.ts b/src/Input/InputReceiver.ts index 37ff36c..4e060cf 100644 --- a/src/Input/InputReceiver.ts +++ b/src/Input/InputReceiver.ts @@ -58,8 +58,10 @@ export default class InputReceiver{ if(event.type === "key_down"){ let key = event.data.get("key") - this.keyJustPressed.set(key, true); - this.keyPressed.set(key, true); + if(!this.keyPressed.get(key)){ + this.keyJustPressed.set(key, true); + this.keyPressed.set(key, true); + } } if(event.type === "key_up"){ diff --git a/src/Nodes/Tilemap.ts b/src/Nodes/Tilemap.ts index bde88c0..4e60a3a 100644 --- a/src/Nodes/Tilemap.ts +++ b/src/Nodes/Tilemap.ts @@ -11,6 +11,8 @@ export default abstract class Tilemap extends GameNode { protected tilesets: Tileset[]; protected worldSize: Vec2; protected tileSize: Vec2; + protected visible: boolean; + protected collidable: boolean; constructor(tilemapData: TiledTilemapData, layerData: TiledLayerData){ super(); @@ -20,6 +22,14 @@ export default abstract class Tilemap extends GameNode { this.parseTilemapData(tilemapData, layerData); } + isCollidable(): boolean { + return this.collidable; + } + + isVisible(): boolean { + return this.visible; + } + getTilesets(): Tileset[] { return this.tilesets; } diff --git a/src/Nodes/Tilemaps/OrthogonalTilemap.ts b/src/Nodes/Tilemaps/OrthogonalTilemap.ts index 09ce47f..aab4268 100644 --- a/src/Nodes/Tilemaps/OrthogonalTilemap.ts +++ b/src/Nodes/Tilemaps/OrthogonalTilemap.ts @@ -5,10 +5,18 @@ import Tileset from "../../DataTypes/Tilesets/Tileset"; export default class OrthogonalTilemap extends Tilemap { + parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void { this.worldSize.set(tilemapData.width, tilemapData.height); this.tileSize.set(tilemapData.tilewidth, tilemapData.tileheight); this.data = layer.data; + this.visible = layer.visible; + this.collidable = false; + for(let item of layer.properties){ + if(item.name === "Collidable"){ + this.collidable = item.value; + } + } tilemapData.tilesets.forEach(tilesetData => this.tilesets.push(new Tileset(tilesetData))); } @@ -20,6 +28,7 @@ export default class OrthogonalTilemap extends Tilemap { update(deltaT: number): void {} + // TODO: Don't render tiles that aren't on screen render(ctx: CanvasRenderingContext2D, origin: Vec2, viewportSize: Vec2) { for(let i = 0; i < this.data.length; i++){ let tileIndex = this.data[i]; diff --git a/src/Physics/PhysicsManager.ts b/src/Physics/PhysicsManager.ts index 6b6e65e..0e0aea1 100644 --- a/src/Physics/PhysicsManager.ts +++ b/src/Physics/PhysicsManager.ts @@ -2,6 +2,7 @@ import PhysicsNode from "./PhysicsNode"; import Vec2 from "../DataTypes/Vec2"; import StaticBody from "./StaticBody"; import Debug from "../Debug/Debug"; +import MathUtils from "../Utils/MathUtils"; export default class PhysicsManager { @@ -41,6 +42,7 @@ export default class PhysicsManager { // For now, we will only have the moving player, don't bother checking for collisions with other moving things for(let movingNode of dynamicSet){ + movingNode.setIsGrounded(false); // Get velocity of node let velocity = null; for(let data of this.movements){ @@ -141,12 +143,34 @@ export default class PhysicsManager { collidingY = true; } + if(B.x < A.x){ + // Swap, because B is to the left of A + let temp: Vec2; + temp = sizeA; + sizeA = sizeB; + sizeB = temp; + + temp = A; + A = B; + B = temp; + + temp = velA; + velA = velB; + velB = temp; + } + if( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){ if(collidingX && collidingY){ // If we're already intersecting, freak out I guess? } else { - let contactTime = Math.min(firstContact.x, firstContact.y); - velocity.scale(contactTime); + // let contactTime = Math.min(firstContact.x, firstContact.y); + // velocity.scale(contactTime); + let xScale = MathUtils.clamp(firstContact.x, 0, 1); + let yScale = MathUtils.clamp(firstContact.y, 0, 1); + if(yScale !== 1){ + movingNode.setIsGrounded(true); + } + velocity.scale(xScale, yScale); } } } diff --git a/src/Physics/PhysicsNode.ts b/src/Physics/PhysicsNode.ts index a876615..06e9a23 100644 --- a/src/Physics/PhysicsNode.ts +++ b/src/Physics/PhysicsNode.ts @@ -9,6 +9,7 @@ export default abstract class PhysicsNode extends GameNode { protected children: Array; private manager: PhysicsManager; isMoving: boolean; + protected isGrounded: boolean; constructor(){ super(); @@ -16,6 +17,10 @@ export default abstract class PhysicsNode extends GameNode { this.isMoving = false; } + setIsGrounded(isGrounded: boolean): void { + this.isGrounded = isGrounded; + } + addManager(manager: PhysicsManager): void { this.manager = manager; } diff --git a/src/Player.ts b/src/Player.ts index dfb70f8..196c059 100644 --- a/src/Player.ts +++ b/src/Player.ts @@ -9,15 +9,21 @@ export default class Player extends PhysicsNode { speed: number; debug: Debug; size: Vec2; + gravity: number = 7000; + type: string; - constructor(){ + constructor(type: string){ super(); + this.type = type; this.velocity = new Vec2(0, 0); - this.speed = 300; + this.speed = 500; this.size = new Vec2(50, 50); this.collider = new AABB(); this.collider.setSize(this.size); this.position = new Vec2(0, 0); + if(this.type === "topdown"){ + this.position = new Vec2(100, 100); + } this.debug = Debug.getInstance(); } @@ -29,18 +35,60 @@ export default class Player extends PhysicsNode { } update(deltaT: number): void { + if(this.type === "topdown"){ + let dir = this.topdown_computeDirection(); + this.velocity = this.topdown_computeVelocity(dir, deltaT); + } else { + let dir = this.platformer_computeDirection(); + this.velocity = this.platformer_computeVelocity(dir, deltaT); + } + + this.move(new Vec2(this.velocity.x * deltaT, this.velocity.y * deltaT)); + + this.debug.log("player", "Player Pos: " + this.position.toFixed() + ", Player Vel: " + this.velocity.toFixed()); + } + + topdown_computeDirection(): Vec2 { let dir = new Vec2(0, 0); dir.x += this.input.isPressed('a') ? -1 : 0; - dir.x += this.input.isPressed('d') ? 1 : 0; - dir.y += this.input.isPressed('s') ? 1 : 0; - dir.y += this.input.isPressed('w') ? -1 : 0; + dir.x += this.input.isPressed('d') ? 1 : 0; + dir.y += this.input.isPressed('w') ? -1 : 0; + dir.y += this.input.isPressed('s') ? 1 : 0; - dir.normalize(); + dir.normalize(); - this.velocity = dir.scale(this.speed); - this.move(this.velocity.scale(deltaT)); + return dir; + } - this.debug.log("player", "Player Pos: " + this.position.toFixed() + " " + this.velocity.toFixed()); + topdown_computeVelocity(dir: Vec2, deltaT: number): Vec2 { + let vel = new Vec2(dir.x * this.speed, dir.y * this.speed); + return vel + } + + platformer_computeDirection(): Vec2 { + let dir = new Vec2(0, 0); + dir.x += this.input.isPressed('a') ? -1 : 0; + dir.x += this.input.isPressed('d') ? 1 : 0; + + if(this.isGrounded){ + dir.y += this.input.isJustPressed('w') ? -1 : 0; + } + + return dir; + } + + platformer_computeVelocity(dir: Vec2, deltaT: number): Vec2 { + let vel = new Vec2(0, this.velocity.y); + + if(this.isGrounded){ + vel.y = dir.y*1800; + } + + vel.y += this.gravity * deltaT; + + vel.x = dir.x * this.speed; + + return vel } } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 0a47ffc..56b284c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,17 +15,12 @@ function main(){ let backgroundScene = gameState.createScene(); backgroundScene.setParallax(0.5, 0.5); let mainScene = gameState.createScene(); - let foregroundLayer = gameState.createScene(); - foregroundLayer.setParallax(1.5, 1.5); let uiLayer = gameState.createScene(); uiLayer.setParallax(0, 0); let pauseMenu = gameState.createScene(); pauseMenu.setParallax(0, 0); // Initialize GameObjects - let player = mainScene.physics.add(Player); - mainScene.getViewport().follow(player); - let recordButton = uiLayer.canvasNode.add(Button); recordButton.setSize(100, 50); recordButton.setText("Record"); @@ -79,13 +74,8 @@ function main(){ } mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Platformer.json"); - - for(let i = 0; i < 30; i++){ - let cc = foregroundLayer.canvasNode.add(ColoredCircle); - cc.setSize(80, 80); - cc.setColor(cc.getColor().lighten().lighten()) - cc.getColor().a = 0.5; - } + let player = mainScene.physics.add(Player, "platformer"); + mainScene.getViewport().follow(player); pauseMenu.disable();