| if (nSteps > 1) { | if (nSteps > 1) { | ||||
| console.log( | console.log( | ||||
| "Too long between frames for physics ("+dt.toFixed(2)+"s). "+ | "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) | for (let i = 0; i < nSteps; ++i) |
| 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; | |||||
| } | |||||
| } |
| .defineTile("platform", 0, 2) | .defineTile("platform", 0, 2) | ||||
| .defineTile("platform-l", 1, 2) | .defineTile("platform-l", 1, 2) | ||||
| .defineTile("platform-r", 2, 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) | entities: new SpriteSheet("assets/entities.png", 19, 32, 2) | ||||
| .defineTile("player-head", 0, 0), | .defineTile("player-head", 0, 0), |
| 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); | |||||
| } | |||||
| } |
| import TCollider from "../traits/TCollider.js"; | import TCollider from "../traits/TCollider.js"; | ||||
| import TPhysics from "../traits/TPhysics.js"; | import TPhysics from "../traits/TPhysics.js"; | ||||
| export default class Platform extends Entity { | |||||
| export default class FallingPlatform extends Entity { | |||||
| constructor(level, width = 5) { | constructor(level, width = 5) { | ||||
| super(level); | super(level); | ||||
| import TCollider from "../traits/TCollider.js"; | import TCollider from "../traits/TCollider.js"; | ||||
| import TPhysics from "../traits/TPhysics.js"; | import TPhysics from "../traits/TPhysics.js"; | ||||
| export default class Platform extends Entity { | |||||
| export default class FloatingPlatform extends Entity { | |||||
| constructor(level, width = 5) { | constructor(level, width = 5) { | ||||
| super(level); | super(level); | ||||
| this.addTrait(new TPlatform(this)); | this.addTrait(new TPlatform(this)); | ||||
| this.addTrait(new TPhysics(this)); | this.addTrait(new TPhysics(this)); | ||||
| this.texture = new Texture(assets.tiles, | |||||
| Tile.createLine(this.bounds.size.x, "platform")); | |||||
| this.targetSpeed = 3; | this.targetSpeed = 3; | ||||
| this.accel = 5; | this.accel = 5; | ||||
| this.dir = 1; | this.dir = 1; | ||||
| this.movement = 5; | |||||
| this.texture = new Texture(assets.tiles, | |||||
| Tile.createLine(this.bounds.size.x, "platform")); | |||||
| this.distance = 5; | |||||
| } | } | ||||
| init() { | init() { | ||||
| this.targetTop = this.bounds.pos.y; | 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.gravity = 0; | ||||
| this.t.physics.velocity.y = 0; | this.t.physics.velocity.y = 0; |
| 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 FallingPlatform from "./entities/FallingPlatform.js"; | ||||
| import BrokenPlatform from "./entities/BrokenPlatform.js"; | |||||
| import structures from "./structures.js"; | import structures from "./structures.js"; | ||||
| let canvas = document.getElementById("canvas"); | let canvas = document.getElementById("canvas"); | ||||
| 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, 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.start(); | level.start(); | ||||
| // Pause the game when the tab has been out of focus for more than half a second | // Pause the game when the tab has been out of focus for more than half a second |
| 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 | // Check if we're still on the same ground | ||||
| if (this.onGround) { | if (this.onGround) { | ||||
| let stillOnGround = | let stillOnGround = | ||||
| (this.entity.bounds.intersects(this.groundBounds)) && | (this.entity.bounds.intersects(this.groundBounds)) && | ||||
| (this.entity.bounds.intersectSide(this.groundBounds) === "top"); | |||||
| (this.entity.bounds.intersectSide(this.groundBounds) === "top") && | |||||
| !jumping; | |||||
| if (stillOnGround) { | if (stillOnGround) { | ||||
| this.entity.bounds.bottom = this.groundBounds.top; | this.entity.bounds.bottom = this.groundBounds.top; | ||||
| } | } | ||||
| // Collide with new stuff | // 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; | this.oldGroundBounds = null; | ||||
| } | } |
| } | } | ||||
| }, | }, | ||||
| "dev-refresh": { | "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, | "dev": true, | ||||
| "requires": { | "requires": { | ||||
| "minimist": "1.2.0", | "minimist": "1.2.0", | ||||
| "node-watch": "0.5.5", | "node-watch": "0.5.5", | ||||
| "open": "0.0.5", | "open": "0.0.5", | ||||
| "webframe": "0.8.2" | "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": { | "diffie-hellman": { | ||||
| "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", | ||||
| "dev": true | "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": { | "number-is-nan": { | ||||
| "version": "1.0.1", | "version": "1.0.1", | ||||
| "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", | ||||
| "wrappy": "1.0.2" | "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": { | "os-browserify": { | ||||
| "version": "0.3.0", | "version": "0.3.0", | ||||
| "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", | "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", | ||||
| "indexof": "0.0.1" | "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": { | "wrappy": { | ||||
| "version": "1.0.2", | "version": "1.0.2", | ||||
| "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", |
| "babel-preset-env": "^1.6.1", | "babel-preset-env": "^1.6.1", | ||||
| "babelify": "^8.0.0", | "babelify": "^8.0.0", | ||||
| "browserify": "^14.5.0", | "browserify": "^14.5.0", | ||||
| "dev-refresh": "^0.3.0" | |||||
| "dev-refresh": "file:../dev-refresh" | |||||
| } | } | ||||
| } | } |