Browse Source

physics stuff

master
mortie 6 years ago
parent
commit
8bd00dac3f

+ 23
- 9
js/Entity.js View File

} }


export default class Entity { export default class Entity {
constructor(level) {
constructor(level, layerName) {
this.layerName = layerName;
this.level = level; this.level = level;
this.bounds = new Rect(); this.bounds = new Rect();
this.time = 0; this.time = 0;


this.traitOrder = [
"collider",
"platform",
"wall",
"keyboardController",
"physics",
];
this.t = { this.t = {
physics: null,
keyboardController: null,
collider: null, collider: null,
platform: null, platform: null,
wall: null, wall: null,
keyboardController: null,
physics: null,
}; };


this.traits = []; this.traits = [];
} }


_init() { _init() {
for (let traitName of this.traitOrder) {
let t = this.t[traitName];
if (t == null)
continue;

this.traits.push(t);
if (t.update !== Trait.prototype.update)
this.updateTraits.push(t);
if (t.postUpdate !== Trait.prototype.postUpdate)
this.postUpdateTraits.push(t);
}

for (let trait of this.traits) { for (let trait of this.traits) {
trait._init(); trait._init();
} }
throw new Error("Invalid trait:", t); throw new Error("Invalid trait:", t);


this.t[t.name] = t; this.t[t.name] = t;
this.traits.push(t);

if (t.update !== Trait.prototype.update)
this.updateTraits.push(t);
if (t.postUpdate !== Trait.prototype.postUpdate)
this.postUpdateTraits.push(t);
} }
} }

+ 22
- 4
js/Level.js View File

this.minFPS = 5; this.minFPS = 5;
this.backgroundColor = "#87CEFA"; this.backgroundColor = "#87CEFA";


this.layerOrder = {
platform: 0,
player: 1,
};
this.layers = [];
this.entities = []; this.entities = [];
this.colliders = []; this.colliders = [];


spawnEntity(ent, x, y) { spawnEntity(ent, x, y) {
ent.bounds.pos.set(x, y); ent.bounds.pos.set(x, y);


let layerIdx = this.layerOrder[ent.layerName];
console.log(ent.layerName, this.layerOrder, layerIdx);
if (layerIdx == null)
throw new Error("Unknown layer name: "+ent.layerName);

if (this.layers[layerIdx] == null)
this.layers[layerIdx] = [];

this.entities.push(ent); this.entities.push(ent);
this.layers[layerIdx].push(ent);
if (ent.has("collider")) if (ent.has("collider"))
this.colliders.push(ent); this.colliders.push(ent);


structure.init(); structure.init();
} }


physics(dt) {
this.entities.forEach(ent =>
physicsLayer(dt, layer) {
layer.forEach(ent =>
ent._update(dt)); ent._update(dt));
this.entities.forEach(ent =>
layer.forEach(ent =>
ent._postUpdate(dt)); ent._postUpdate(dt));
}


physics(dt) {
// Collide with structures // Collide with structures
this.structures.forEach(s => { this.structures.forEach(s => {
if (!s.attrs.wall && !s.attrs.platform) if (!s.attrs.wall && !s.attrs.platform)
ent.t.collider.collideEntity(ent2); ent.t.collider.collideEntity(ent2);
}); });
}); });

this.layers.forEach(l => this.physicsLayer(dt, l));
} }


draw() { draw() {
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);


this.entities.forEach(ent => ent.draw(this.ctx));
this.layers.forEach(l => l.forEach(ent => ent.draw(this.ctx)));
this.structures.forEach(struct => struct.draw(this.ctx)); this.structures.forEach(struct => struct.draw(this.ctx));
} }



+ 6
- 2
js/entities/BrokenPlatform.js View File



export default class BrokenPlatform extends Entity { export default class BrokenPlatform extends Entity {
constructor(level, width = 5) { constructor(level, width = 5) {
super(level);
super(level, "platform");


this.bounds.size.set(width, 0.5); this.bounds.size.set(width, 0.5);
this.addTrait(new TCollider(this)); this.addTrait(new TCollider(this));
update(dt) { update(dt) {
if (!this.shaking && !this.falling) { if (!this.shaking && !this.falling) {
this.t.collider.entities.forEach(e => { this.t.collider.entities.forEach(e => {
if (e.has("physics")) {
let fall =
e.has("physics") &&
e.bounds.intersectSide(this.bounds) === "top";

if (fall) {
this.startShake(); this.startShake();
} }
}); });

+ 0
- 26
js/entities/FallingPlatform.js View File

import Entity from "../Entity.js";
import Texture from "../Texture.js";
import Tile from "../Tile.js";
import assets from "../assets.js";

import TPlatform from "../traits/TPlatform.js";
import TCollider from "../traits/TCollider.js";
import TPhysics from "../traits/TPhysics.js";

export default class FallingPlatform extends Entity {
constructor(level, width = 5) {
super(level);

this.bounds.size.set(width, 0.5);
this.addTrait(new TCollider(this));
this.addTrait(new TPlatform(this));
this.addTrait(new TPhysics(this));

this.texture = new Texture(assets.tiles,
Tile.createLine(this.bounds.size.x, "platform"));
}

draw(ctx) {
this.texture.drawAt(ctx, this.bounds.pos);
}
}

+ 1
- 1
js/entities/FloatingPlatform.js View File



export default class FloatingPlatform extends Entity { export default class FloatingPlatform extends Entity {
constructor(level, width = 5) { constructor(level, width = 5) {
super(level);
super(level, "platform");


this.bounds.size.set(width, 0.5); this.bounds.size.set(width, 0.5);
this.addTrait(new TCollider(this)); this.addTrait(new TCollider(this));

+ 1
- 1
js/entities/Player.js View File



export default class Player extends Entity { export default class Player extends Entity {
constructor(level) { constructor(level) {
super(level);
super(level, "player");


this.bounds.size.set(1, 2); this.bounds.size.set(1, 2);
this.addTrait(new TCollider(this)); this.addTrait(new TCollider(this));

+ 0
- 5
js/main.js View File



import Player from "./entities/Player.js"; import Player from "./entities/Player.js";
import FloatingPlatform from "./entities/FloatingPlatform.js"; import FloatingPlatform from "./entities/FloatingPlatform.js";
import FallingPlatform from "./entities/FallingPlatform.js";
import BrokenPlatform from "./entities/BrokenPlatform.js"; import BrokenPlatform from "./entities/BrokenPlatform.js";
import structures from "./structures.js"; import structures from "./structures.js";




level.spawnEntity(new Player(level), 10, 1); level.spawnEntity(new Player(level), 10, 1);
level.spawnEntity(new FloatingPlatform(level), 16, 4); level.spawnEntity(new FloatingPlatform(level), 16, 4);
level.spawnEntity(new FallingPlatform(level), 20, 1);
level.spawnEntity(new FallingPlatform(level), 20, 0);
level.spawnEntity(new FallingPlatform(level), 20, -1);
level.spawnEntity(new FallingPlatform(level), 20, -2);
level.spawnStructure(structures.floor(8, 6), 4, 4); level.spawnStructure(structures.floor(8, 6), 4, 4);


level.spawnEntity(new BrokenPlatform(level), 27, 4); level.spawnEntity(new BrokenPlatform(level), 27, 4);

+ 1
- 1
js/traits/TKeyboardController.js View File

this.entity.time -phys.timeLastOnGround < this.jumpLeeway; this.entity.time -phys.timeLastOnGround < this.jumpLeeway;


if (this.pressed.jump && canJump) { if (this.pressed.jump && canJump) {
phys.velocity.y = -this.jump;
phys.velocity.y -= this.jump;
this.entity.bounds.pos.y += phys.velocity.y * dt; this.entity.bounds.pos.y += phys.velocity.y * dt;
this.jumpTime = this.jumpTimeMax; this.jumpTime = this.jumpTimeMax;
this.jumping = true; this.jumping = true;

+ 47
- 88
js/traits/TPhysics.js View File

constructor(entity) { constructor(entity) {
super(entity, "physics"); super(entity, "physics");


this.velocity = new Vec2();
this.gravity = 40; this.gravity = 40;
this.groundFriction = 6; this.groundFriction = 6;
this.airFriction = 2; this.airFriction = 2;


this.absVelocity = new Vec2();
this.velocity = new Vec2();

this.timeLastOnGround = 0;
this.onGround = false; this.onGround = false;
this.groundBounds = null; this.groundBounds = null;
this.groundVelocity = null; this.groundVelocity = null;
this.oldGroundBounds = null;
this.timeLastOnGround = 0;
} }


collideTop(bounds, absVelocity) {
if (this.onGround || bounds === this.oldGroundBounds)
return;

this.onGround = true;
this.groundBounds = bounds;
this.groundVelocity = absVelocity;

this.velocity.x -= absVelocity.x;
this.velocity.y = 0;
this.entity.bounds.bottom = bounds.top;
collideTop(bounds, velocity) {
if (!this.groundBounds || bounds.top < this.groundBounds.top) {
this.groundBounds = bounds;
this.groundVelocity = velocity;
}
} }


collideWall(bounds, absVelocity) {
collideWall(bounds, velocity) {
let side = this.entity.bounds.intersectSide(bounds); let side = this.entity.bounds.intersectSide(bounds);


if (side === "top") if (side === "top")
return this.collideTop(bounds, absVelocity);
return this.collideTop(bounds, velocity);
} }


collidePlatform(bounds, absVelocity) {
collidePlatform(bounds, velocity) {
let side = this.entity.bounds.intersectSide(bounds); let side = this.entity.bounds.intersectSide(bounds);


if (side === "top") if (side === "top")
return this.collideTop(bounds, absVelocity);
return this.collideTop(bounds, velocity);
} }


update(dt) { update(dt) {


// Gravity
if (!this.onGround)
this.velocity.y += this.gravity * dt;

// Collide
if (this.entity.has("collider")) { if (this.entity.has("collider")) {

let jumping =
this.groundVelocity &&
this.velocity.y < this.groundVelocity.y;

// Check if we're still on the same ground
if (this.onGround) {
let stillOnGround =
(this.entity.bounds.intersects(this.groundBounds)) &&
(this.entity.bounds.intersectSide(this.groundBounds) === "top") &&
!jumping;

if (stillOnGround) {
this.entity.bounds.bottom = this.groundBounds.top;
} else {
this.velocity.x += this.groundVelocity.x;
this.velocity.y += this.groundVelocity.y;

this.onGround = false;
this.oldGroundBounds = this.groundBounds;
this.groundBounds = null;
this.groundVelocity = null;
}
}

// Collide with new stuff
if (!jumping) {
let collider = this.entity.t.collider;
collider.entities.forEach(e => {
let vel;
if (e.has("physics"))
vel = e.t.physics.absVelocity;
else
vel = zeroVector;

if (e.has("wall"))
this.collideWall(e.bounds, vel);
else if (e.has("platform"))
this.collidePlatform(e.bounds, vel);
});
collider.structures.forEach((s, i) => {
let bounds = collider.structureBounds[i];
if (s.attrs.wall)
this.collideWall(bounds, zeroVector);
else if (s.attrs.platform)
this.collidePlatform(bounds, zeroVector);
});
}

this.oldGroundBounds = null;
let collider = this.entity.t.collider;
this.groundBounds = null;
this.groundVelocity = null;

collider.entities.forEach(e => {
let vel = zeroVector;
if (e.has("physics"))
vel = e.t.physics.velocity;

if (e.has("wall"))
this.collideWall(e.bounds, vel);
else if (e.has("platform"))
this.collidePlatform(e.bounds, vel);
});
collider.structures.forEach((s, i) => {
let bounds = collider.structureBounds[i];
if (s.attrs.wall)
this.collideWall(bounds, zeroVector);
else if (s.attrs.platform)
this.collidePlatform(bounds, zeroVector);
});
} }


// Track the last time we were on the ground
if (this.onGround)
this.onGround =
(this.groundVelocity && this.velocity.y >= this.groundVelocity.y);

if (this.onGround) {
this.timeLastOnGround = this.entity.time; this.timeLastOnGround = this.entity.time;
this.velocity.y = this.groundVelocity.y;
}

if (!this.onGround)
this.velocity.y += this.gravity * dt;


// Apply friction // Apply friction
var fric = this.onGround ? this.groundFriction : this.airFriction; var fric = this.onGround ? this.groundFriction : this.airFriction;
} }


postUpdate(dt) { postUpdate(dt) {
this.jumped = false;

// Update absVelocity
if (this.onGround) {
this.absVelocity.x = this.velocity.x + this.groundVelocity.x;
this.absVelocity.y = this.velocity.y + this.groundVelocity.y;
} else {
this.absVelocity.x = this.velocity.x;
this.absVelocity.y = this.velocity.y;
}


// Move // Move
this.entity.bounds.pos.x += this.absVelocity.x * dt;
this.entity.bounds.pos.y += this.absVelocity.y * dt;
this.entity.bounds.pos.x += this.velocity.x * dt;
this.entity.bounds.pos.y += this.velocity.y * dt;

if (this.onGround)
this.entity.bounds.bottom = this.groundBounds.top;
} }
} }

Loading…
Cancel
Save