@@ -97,7 +97,7 @@ export default class Level { | |||
if (nSteps > 1) { | |||
console.log( | |||
"Too long between frames for physics ("+dt.toFixed(2)+"s). "+ | |||
"Running "+nSteps+" simulations in one frame."); | |||
"Running "+nSteps+" simulations ("+physdt.toFixed(2)+"s) in one frame."); | |||
} | |||
for (let i = 0; i < nSteps; ++i) |
@@ -0,0 +1,26 @@ | |||
import Vec2 from "./Vec2.js"; | |||
export default class Shaker { | |||
constructor(decay = 7) { | |||
this.vec = new Vec2(); | |||
this.decay = decay; | |||
this.shakeX = 0; | |||
this.shakeY = 0; | |||
} | |||
shake(x, y = x) { | |||
this.shakeX = x; | |||
this.shakeY = y; | |||
} | |||
update(dt) { | |||
if (this.shakeX > 0.1) | |||
this.vec.x = (Math.random() - 0.5) * this.shakeX * dt; | |||
if (this.shakeY > 0.1) | |||
this.vec.y = (Math.random() - 0.5) * this.shakeY * dt; | |||
var xRatio = 1 / (1 + (dt * this.decay)); | |||
this.shakeX *= xRatio; | |||
this.shakeY *= xRatio; | |||
} | |||
} |
@@ -15,7 +15,12 @@ export default { | |||
.defineTile("platform", 0, 2) | |||
.defineTile("platform-l", 1, 2) | |||
.defineTile("platform-r", 2, 2) | |||
.defineTile("platform-lr", 3, 2), | |||
.defineTile("platform-lr", 3, 2) | |||
.defineTile("broken-platform", 0, 3) | |||
.defineTile("broken-platform-l", 1, 3) | |||
.defineTile("broken-platform-r", 2, 3) | |||
.defineTile("broken-platform-lr", 3, 3), | |||
entities: new SpriteSheet("assets/entities.png", 19, 32, 2) | |||
.defineTile("player-head", 0, 0), |
@@ -0,0 +1,83 @@ | |||
import Entity from "../Entity.js"; | |||
import Texture from "../Texture.js"; | |||
import Tile from "../Tile.js"; | |||
import Shaker from "../Shaker.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 BrokenPlatform 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, "broken-platform")); | |||
this.shaker = new Shaker(); | |||
this.shake = 0; | |||
this.shakeAccel = 14; | |||
this.shaking = false; | |||
this.floating = true; | |||
this.reliableTime = 1; | |||
this.resetTime = 4; | |||
} | |||
init() { | |||
this.initialPos = this.bounds.pos.clone(); | |||
} | |||
reset() { | |||
this.shake = 0; | |||
this.shaking = false; | |||
this.floating = true; | |||
this.t.physics.velocity.set(0, 0); | |||
this.bounds.pos.set(this.initialPos.x, this.initialPos.y); | |||
} | |||
startShake() { | |||
this.shake = 0; | |||
this.shaking = true | |||
setTimeout(() => { | |||
this.floating = false; | |||
this.shaking = false; | |||
setTimeout(() => { | |||
this.reset(); | |||
}, this.resetTime * 1000); | |||
}, this.reliableTime * 1000); | |||
} | |||
update(dt) { | |||
if (!this.shaking && !this.falling) { | |||
this.t.collider.entities.forEach(e => { | |||
if (e.has("physics")) { | |||
this.startShake(); | |||
} | |||
}); | |||
} | |||
if (this.floating) | |||
this.t.physics.velocity.y -= this.t.physics.gravity * dt; | |||
if (this.shaking) { | |||
this.shaker.shake(this.shake); | |||
this.shake += this.shakeAccel * dt; | |||
} | |||
this.shaker.update(dt); | |||
} | |||
draw(ctx) { | |||
this.texture.draw( | |||
ctx, | |||
this.bounds.pos.pixelX + this.shaker.vec.pixelX, | |||
this.bounds.pos.pixelY + this.shaker.vec.pixelY); | |||
} | |||
} |
@@ -7,7 +7,7 @@ import TPlatform from "../traits/TPlatform.js"; | |||
import TCollider from "../traits/TCollider.js"; | |||
import TPhysics from "../traits/TPhysics.js"; | |||
export default class Platform extends Entity { | |||
export default class FallingPlatform extends Entity { | |||
constructor(level, width = 5) { | |||
super(level); | |||
@@ -7,7 +7,7 @@ import TPlatform from "../traits/TPlatform.js"; | |||
import TCollider from "../traits/TCollider.js"; | |||
import TPhysics from "../traits/TPhysics.js"; | |||
export default class Platform extends Entity { | |||
export default class FloatingPlatform extends Entity { | |||
constructor(level, width = 5) { | |||
super(level); | |||
@@ -16,18 +16,18 @@ export default class Platform extends Entity { | |||
this.addTrait(new TPlatform(this)); | |||
this.addTrait(new TPhysics(this)); | |||
this.texture = new Texture(assets.tiles, | |||
Tile.createLine(this.bounds.size.x, "platform")); | |||
this.targetSpeed = 3; | |||
this.accel = 5; | |||
this.dir = 1; | |||
this.movement = 5; | |||
this.texture = new Texture(assets.tiles, | |||
Tile.createLine(this.bounds.size.x, "platform")); | |||
this.distance = 5; | |||
} | |||
init() { | |||
this.targetTop = this.bounds.pos.y; | |||
this.targetBottom = this.targetTop + this.movement; | |||
this.targetBottom = this.targetTop + this.distance; | |||
this.t.physics.gravity = 0; | |||
this.t.physics.velocity.y = 0; |
@@ -4,6 +4,7 @@ import Vec2 from "./Vec2.js"; | |||
import Player from "./entities/Player.js"; | |||
import FloatingPlatform from "./entities/FloatingPlatform.js"; | |||
import FallingPlatform from "./entities/FallingPlatform.js"; | |||
import BrokenPlatform from "./entities/BrokenPlatform.js"; | |||
import structures from "./structures.js"; | |||
let canvas = document.getElementById("canvas"); | |||
@@ -13,8 +14,13 @@ let level = new Level(canvas); | |||
level.spawnEntity(new Player(level), 10, 1); | |||
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.spawnEntity(new BrokenPlatform(level), 27, 4); | |||
level.start(); | |||
// Pause the game when the tab has been out of focus for more than half a second |
@@ -56,11 +56,16 @@ export default class TPhysics extends Trait { | |||
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"); | |||
(this.entity.bounds.intersectSide(this.groundBounds) === "top") && | |||
!jumping; | |||
if (stillOnGround) { | |||
this.entity.bounds.bottom = this.groundBounds.top; | |||
@@ -76,26 +81,28 @@ export default class TPhysics extends Trait { | |||
} | |||
// Collide with new stuff | |||
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); | |||
}); | |||
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; | |||
} |
@@ -1172,15 +1172,35 @@ | |||
} | |||
}, | |||
"dev-refresh": { | |||
"version": "0.3.0", | |||
"resolved": "https://registry.npmjs.org/dev-refresh/-/dev-refresh-0.3.0.tgz", | |||
"integrity": "sha512-aSbk/oqQ3MokCkNscC0Evu20ht82YgTIPnV2essHFb1bDHzftM88U/ovETFBAzs/9F45uf3t5qNAgiqwMSD6Og==", | |||
"version": "file:../dev-refresh", | |||
"dev": true, | |||
"requires": { | |||
"minimist": "1.2.0", | |||
"node-watch": "0.5.5", | |||
"open": "0.0.5", | |||
"webframe": "0.8.2" | |||
}, | |||
"dependencies": { | |||
"minimist": { | |||
"version": "1.2.0", | |||
"bundled": true, | |||
"dev": true | |||
}, | |||
"node-watch": { | |||
"version": "0.5.5", | |||
"bundled": true, | |||
"dev": true | |||
}, | |||
"open": { | |||
"version": "0.0.5", | |||
"bundled": true, | |||
"dev": true | |||
}, | |||
"webframe": { | |||
"version": "0.8.2", | |||
"bundled": true, | |||
"dev": true | |||
} | |||
} | |||
}, | |||
"diffie-hellman": { | |||
@@ -1636,12 +1656,6 @@ | |||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", | |||
"dev": true | |||
}, | |||
"node-watch": { | |||
"version": "0.5.5", | |||
"resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.5.5.tgz", | |||
"integrity": "sha512-z9xN2ibI6P0UylFadN7oMcIMsoTeCENC0rZyRM5MVK9AqzSPx+uGqKG6KMPeC/laOV4wOGZq/GH0PTstRNSqOA==", | |||
"dev": true | |||
}, | |||
"number-is-nan": { | |||
"version": "1.0.1", | |||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", | |||
@@ -1657,12 +1671,6 @@ | |||
"wrappy": "1.0.2" | |||
} | |||
}, | |||
"open": { | |||
"version": "0.0.5", | |||
"resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", | |||
"integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=", | |||
"dev": true | |||
}, | |||
"os-browserify": { | |||
"version": "0.3.0", | |||
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", | |||
@@ -2184,12 +2192,6 @@ | |||
"indexof": "0.0.1" | |||
} | |||
}, | |||
"webframe": { | |||
"version": "0.8.2", | |||
"resolved": "https://registry.npmjs.org/webframe/-/webframe-0.8.2.tgz", | |||
"integrity": "sha512-ohoXTI8ULn/gGJ6lfhXAYn/2qsfApdd7wTJSts0jYcCJyWOW3+VSKLMEddRn9JLOu88/OeuPIiuHlxZ92IHPBA==", | |||
"dev": true | |||
}, | |||
"wrappy": { | |||
"version": "1.0.2", | |||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", |
@@ -16,6 +16,6 @@ | |||
"babel-preset-env": "^1.6.1", | |||
"babelify": "^8.0.0", | |||
"browserify": "^14.5.0", | |||
"dev-refresh": "^0.3.0" | |||
"dev-refresh": "file:../dev-refresh" | |||
} | |||
} |