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