| @@ -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" | |||
| } | |||
| } | |||