fixed issues with tilemaps and physics

This commit is contained in:
Joe Weaver 2020-08-18 14:48:47 -04:00
parent 033680cf52
commit bd961d04d3
11 changed files with 137 additions and 39 deletions

View File

@ -8,6 +8,12 @@ export class TiledTilemapData {
tilesets: TiledTilesetData[]; tilesets: TiledTilesetData[];
} }
export class TiledLayerProperty {
name: string;
type: string;
value: any;
}
export class TiledTilesetData { export class TiledTilesetData {
columns: number; columns: number;
tilewidth: number; tilewidth: number;
@ -31,5 +37,6 @@ export class TiledLayerData {
name: string; name: string;
opacity: number; opacity: number;
visible: boolean; visible: boolean;
properties: TiledLayerProperty[];
} }

View File

@ -27,16 +27,19 @@ export default class TilemapFactory {
// Add to scene // Add to scene
this.scene.addTilemap(tilemap); this.scene.addTilemap(tilemap);
let worldSize = tilemap.getWorldSize(); if(tilemap.isCollidable()){
let tileSize = tilemap.getTileSize(); // Create colliders
let worldSize = tilemap.getWorldSize();
let tileSize = tilemap.getTileSize();
tilemap.forEachTile((tileIndex: number, i: number) => { tilemap.forEachTile((tileIndex: number, i: number) => {
if(tileIndex !== 0){ if(tileIndex !== 0){
let x = (i % worldSize.x) * tileSize.x * 4; let x = (i % worldSize.x) * tileSize.x * 4;
let y = Math.floor(i / worldSize.x) * tileSize.y * 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)); this.scene.physics.add(StaticBody, new Vec2(x, y), new Vec2(tileSize.x * 4, tileSize.y * 4));
} }
}); });
}
// Load images for the tilesets // Load images for the tilesets
tilemap.getTilesets().forEach(tileset => { tilemap.getTilesets().forEach(tileset => {

View File

@ -13,7 +13,7 @@ export default class GameState{
this.worldSize = new Vec2(1600, 1000); this.worldSize = new Vec2(1600, 1000);
this.viewport = new Viewport(); this.viewport = new Viewport();
this.viewport.setSize(800, 500); this.viewport.setSize(800, 500);
this.viewport.setBounds(0, 0, 1600, 1000); this.viewport.setBounds(0, 0, 2560, 1280);
} }
createScene(): Scene{ createScene(): Scene{

View File

@ -102,15 +102,15 @@ export default class Scene {
let origin = new Vec2(viewportOrigin.x*this.parallax.x, viewportOrigin.y*this.parallax.y); let origin = new Vec2(viewportOrigin.x*this.parallax.x, viewportOrigin.y*this.parallax.y);
let size = this.viewport.getSize(); let size = this.viewport.getSize();
// Render visible set
visibleSet.forEach(node => node.render(ctx, origin));
// Render tilemaps // Render tilemaps
this.tilemaps.forEach(tilemap => { this.tilemaps.forEach(tilemap => {
if(tilemap.isReady()){ if(tilemap.isReady() && tilemap.isVisible()){
tilemap.render(ctx, origin, size); tilemap.render(ctx, origin, size);
} }
}); });
// Render visible set
visibleSet.forEach(node => node.render(ctx, origin));
} }
} }
} }

View File

@ -58,8 +58,10 @@ export default class InputReceiver{
if(event.type === "key_down"){ if(event.type === "key_down"){
let key = event.data.get("key") let key = event.data.get("key")
this.keyJustPressed.set(key, true); if(!this.keyPressed.get(key)){
this.keyPressed.set(key, true); this.keyJustPressed.set(key, true);
this.keyPressed.set(key, true);
}
} }
if(event.type === "key_up"){ if(event.type === "key_up"){

View File

@ -11,6 +11,8 @@ export default abstract class Tilemap extends GameNode {
protected tilesets: Tileset[]; protected tilesets: Tileset[];
protected worldSize: Vec2; protected worldSize: Vec2;
protected tileSize: Vec2; protected tileSize: Vec2;
protected visible: boolean;
protected collidable: boolean;
constructor(tilemapData: TiledTilemapData, layerData: TiledLayerData){ constructor(tilemapData: TiledTilemapData, layerData: TiledLayerData){
super(); super();
@ -20,6 +22,14 @@ export default abstract class Tilemap extends GameNode {
this.parseTilemapData(tilemapData, layerData); this.parseTilemapData(tilemapData, layerData);
} }
isCollidable(): boolean {
return this.collidable;
}
isVisible(): boolean {
return this.visible;
}
getTilesets(): Tileset[] { getTilesets(): Tileset[] {
return this.tilesets; return this.tilesets;
} }

View File

@ -5,10 +5,18 @@ import Tileset from "../../DataTypes/Tilesets/Tileset";
export default class OrthogonalTilemap extends Tilemap { export default class OrthogonalTilemap extends Tilemap {
parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void { parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void {
this.worldSize.set(tilemapData.width, tilemapData.height); this.worldSize.set(tilemapData.width, tilemapData.height);
this.tileSize.set(tilemapData.tilewidth, tilemapData.tileheight); this.tileSize.set(tilemapData.tilewidth, tilemapData.tileheight);
this.data = layer.data; 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))); tilemapData.tilesets.forEach(tilesetData => this.tilesets.push(new Tileset(tilesetData)));
} }
@ -20,6 +28,7 @@ export default class OrthogonalTilemap extends Tilemap {
update(deltaT: number): void {} update(deltaT: number): void {}
// TODO: Don't render tiles that aren't on screen
render(ctx: CanvasRenderingContext2D, origin: Vec2, viewportSize: Vec2) { render(ctx: CanvasRenderingContext2D, origin: Vec2, viewportSize: Vec2) {
for(let i = 0; i < this.data.length; i++){ for(let i = 0; i < this.data.length; i++){
let tileIndex = this.data[i]; let tileIndex = this.data[i];

View File

@ -2,6 +2,7 @@ import PhysicsNode from "./PhysicsNode";
import Vec2 from "../DataTypes/Vec2"; import Vec2 from "../DataTypes/Vec2";
import StaticBody from "./StaticBody"; import StaticBody from "./StaticBody";
import Debug from "../Debug/Debug"; import Debug from "../Debug/Debug";
import MathUtils from "../Utils/MathUtils";
export default class PhysicsManager { 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 now, we will only have the moving player, don't bother checking for collisions with other moving things
for(let movingNode of dynamicSet){ for(let movingNode of dynamicSet){
movingNode.setIsGrounded(false);
// Get velocity of node // Get velocity of node
let velocity = null; let velocity = null;
for(let data of this.movements){ for(let data of this.movements){
@ -141,12 +143,34 @@ export default class PhysicsManager {
collidingY = true; 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( (firstContact.x < 1 || collidingX) && (firstContact.y < 1 || collidingY)){
if(collidingX && collidingY){ if(collidingX && collidingY){
// If we're already intersecting, freak out I guess? // If we're already intersecting, freak out I guess?
} else { } else {
let contactTime = Math.min(firstContact.x, firstContact.y); // let contactTime = Math.min(firstContact.x, firstContact.y);
velocity.scale(contactTime); // 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);
} }
} }
} }

View File

@ -9,6 +9,7 @@ export default abstract class PhysicsNode extends GameNode {
protected children: Array<GameNode>; protected children: Array<GameNode>;
private manager: PhysicsManager; private manager: PhysicsManager;
isMoving: boolean; isMoving: boolean;
protected isGrounded: boolean;
constructor(){ constructor(){
super(); super();
@ -16,6 +17,10 @@ export default abstract class PhysicsNode extends GameNode {
this.isMoving = false; this.isMoving = false;
} }
setIsGrounded(isGrounded: boolean): void {
this.isGrounded = isGrounded;
}
addManager(manager: PhysicsManager): void { addManager(manager: PhysicsManager): void {
this.manager = manager; this.manager = manager;
} }

View File

@ -9,15 +9,21 @@ export default class Player extends PhysicsNode {
speed: number; speed: number;
debug: Debug; debug: Debug;
size: Vec2; size: Vec2;
gravity: number = 7000;
type: string;
constructor(){ constructor(type: string){
super(); super();
this.type = type;
this.velocity = new Vec2(0, 0); this.velocity = new Vec2(0, 0);
this.speed = 300; this.speed = 500;
this.size = new Vec2(50, 50); this.size = new Vec2(50, 50);
this.collider = new AABB(); this.collider = new AABB();
this.collider.setSize(this.size); this.collider.setSize(this.size);
this.position = new Vec2(0, 0); this.position = new Vec2(0, 0);
if(this.type === "topdown"){
this.position = new Vec2(100, 100);
}
this.debug = Debug.getInstance(); this.debug = Debug.getInstance();
} }
@ -29,18 +35,60 @@ export default class Player extends PhysicsNode {
} }
update(deltaT: number): void { 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); let dir = new Vec2(0, 0);
dir.x += this.input.isPressed('a') ? -1 : 0; dir.x += this.input.isPressed('a') ? -1 : 0;
dir.x += this.input.isPressed('d') ? 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.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); return dir;
this.move(this.velocity.scale(deltaT)); }
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
} }
} }

View File

@ -15,17 +15,12 @@ function main(){
let backgroundScene = gameState.createScene(); let backgroundScene = gameState.createScene();
backgroundScene.setParallax(0.5, 0.5); backgroundScene.setParallax(0.5, 0.5);
let mainScene = gameState.createScene(); let mainScene = gameState.createScene();
let foregroundLayer = gameState.createScene();
foregroundLayer.setParallax(1.5, 1.5);
let uiLayer = gameState.createScene(); let uiLayer = gameState.createScene();
uiLayer.setParallax(0, 0); uiLayer.setParallax(0, 0);
let pauseMenu = gameState.createScene(); let pauseMenu = gameState.createScene();
pauseMenu.setParallax(0, 0); pauseMenu.setParallax(0, 0);
// Initialize GameObjects // Initialize GameObjects
let player = mainScene.physics.add(Player);
mainScene.getViewport().follow(player);
let recordButton = uiLayer.canvasNode.add(Button); let recordButton = uiLayer.canvasNode.add(Button);
recordButton.setSize(100, 50); recordButton.setSize(100, 50);
recordButton.setText("Record"); recordButton.setText("Record");
@ -79,13 +74,8 @@ function main(){
} }
mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Platformer.json"); mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Platformer.json");
let player = mainScene.physics.add(Player, "platformer");
for(let i = 0; i < 30; i++){ mainScene.getViewport().follow(player);
let cc = foregroundLayer.canvasNode.add(ColoredCircle);
cc.setSize(80, 80);
cc.setColor(cc.getColor().lighten().lighten())
cc.getColor().a = 0.5;
}
pauseMenu.disable(); pauseMenu.disable();