fixed issues with tilemaps and physics
This commit is contained in:
parent
033680cf52
commit
bd961d04d3
|
@ -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[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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"){
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
14
src/main.ts
14
src/main.ts
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user