Use variable physics tick rate
This commit is contained in:
parent
bf71f55130
commit
b159b58973
12 changed files with 86 additions and 67 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
*.map
|
||||
dist/improcket.min.js
|
||||
node_modules
|
||||
.DS_Store
|
||||
|
|
|
@ -27,29 +27,29 @@ export const mapping = {
|
|||
|
||||
let held, pressed;
|
||||
|
||||
export function tick() {
|
||||
export function tick(delta: number) {
|
||||
held = input.keyCode.held;
|
||||
pressed = input.keyCode.pressed;
|
||||
|
||||
if (state.editing) {
|
||||
tickEditing();
|
||||
} else if (state.playing && !state.gameOver && !state.paused) {
|
||||
tickPlaying();
|
||||
tickPlaying(delta);
|
||||
}
|
||||
|
||||
if (!state.editing) {
|
||||
if (input.mouse.scroll !== 0) {
|
||||
// Fix for Firefox.
|
||||
let delta = input.mouse.scroll > 0 ? -50 : 50;
|
||||
graphics.changeZoom(delta);
|
||||
let scrollDelta = input.mouse.scroll > 0 ? -50 : 50;
|
||||
graphics.changeZoom(scrollDelta);
|
||||
}
|
||||
|
||||
if (held[mapping.zoomIn]) {
|
||||
graphics.changeZoom(-10);
|
||||
graphics.changeZoom(-10 * delta);
|
||||
}
|
||||
|
||||
if (held[mapping.zoomOut]) {
|
||||
graphics.changeZoom(10);
|
||||
graphics.changeZoom(10 * delta);
|
||||
}
|
||||
|
||||
if (pressed[mapping.togglePause] && !state.gameOver) {
|
||||
|
@ -74,8 +74,8 @@ export function tick() {
|
|||
}
|
||||
}
|
||||
|
||||
function tickPlaying() {
|
||||
let power = held[mapping.reduce] ? 0.3 : 1;
|
||||
function tickPlaying(delta: number) {
|
||||
let power = (held[mapping.reduce] ? 0.3 : 1) * delta;
|
||||
|
||||
if (held[mapping.thrust] && playerShip.fuel !== 0) {
|
||||
playerShip.applyThrust({ forward: power });
|
||||
|
|
|
@ -64,7 +64,6 @@ export function toMenu() {
|
|||
}
|
||||
|
||||
export function togglePause() {
|
||||
console.log(game.state.paused);
|
||||
game.state.paused = !game.state.paused;
|
||||
audio.play('pause');
|
||||
if (game.state.paused) {
|
||||
|
|
|
@ -25,7 +25,7 @@ export async function init() {
|
|||
gui.init();
|
||||
input.init();
|
||||
|
||||
events.playMusic();
|
||||
// events.playMusic();
|
||||
//events.startGame();
|
||||
|
||||
loop(tick);
|
||||
|
@ -52,27 +52,36 @@ export function changeView(view) {
|
|||
}
|
||||
}
|
||||
|
||||
function loop(fn, fps = 60) {
|
||||
let then = Date.now();
|
||||
let interval = 1000 / fps;
|
||||
async function loop(fn) {
|
||||
let then = Date.now();
|
||||
|
||||
(function loop(time) {
|
||||
fn();
|
||||
while (true) {
|
||||
const delta = (Date.now() - then) * 60;
|
||||
fn(delta / 1000);
|
||||
then = Date.now();
|
||||
|
||||
requestAnimationFrame(loop);
|
||||
})(0);
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
}
|
||||
};
|
||||
|
||||
function tick() {
|
||||
function tick(delta: number) {
|
||||
events.tick();
|
||||
|
||||
if (state.view == 'game' && !state.paused) {
|
||||
world.tick();
|
||||
world.tick(delta);
|
||||
}
|
||||
|
||||
control.tick();
|
||||
control.tick(delta);
|
||||
|
||||
gui.tick();
|
||||
graphics.render();
|
||||
graphics.render(delta);
|
||||
input.tick();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as consts from '../consts';
|
|||
const TAU = consts.TAU;
|
||||
|
||||
export let canvas, context, tempCanvas, tempContext;
|
||||
export let perspective;
|
||||
export let perspective: Perspective;
|
||||
export let trace = true;
|
||||
export let markers = true;
|
||||
|
||||
|
@ -36,7 +36,7 @@ export function init() {
|
|||
context.fillText('Loading...', canvas.width / 2, canvas.height / 2);
|
||||
}
|
||||
|
||||
export function render() {
|
||||
export function render(delta: number) {
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.fillStyle = '#000';
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
@ -46,7 +46,7 @@ export function render() {
|
|||
context.clip();
|
||||
|
||||
context.save();
|
||||
perspective.tick();
|
||||
perspective.tick(delta);
|
||||
perspective.transformRotate();
|
||||
renderBackground(perspective.rotation);
|
||||
perspective.transformCanvas();
|
||||
|
@ -171,7 +171,7 @@ class Perspective {
|
|||
return Math.atan2(Math.sin(b - a), Math.cos(b - a));
|
||||
}
|
||||
|
||||
tick() {
|
||||
tick(delta: number) {
|
||||
if (this.focus !== null)
|
||||
[this.x, this.y] = this.focus.com;
|
||||
|
||||
|
@ -192,12 +192,11 @@ class Perspective {
|
|||
this.normalize();
|
||||
|
||||
let dif = Math.abs(this.targetRotation - this.rotation);
|
||||
this.rotationMet = dif < (this.rotationMet ? 0.3 : 0.05);
|
||||
|
||||
this.rotation = this.currentRotation;
|
||||
this.zoom = this.currentZoom;
|
||||
|
||||
this.transition *= 0.9;
|
||||
this.transition *= 0.9 ** delta;
|
||||
this.zoomTransition *= this.zoomTransitionSpeed;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,22 +64,22 @@ export default class Body {
|
|||
return this.rotateVector(x, y, this.r);
|
||||
}
|
||||
|
||||
tickMotion(speed = 1) {
|
||||
this.x += this.xvel * speed;
|
||||
this.y += this.yvel * speed;
|
||||
this.r += this.rvel * speed;
|
||||
this.rvel *= this.rfriction * speed;
|
||||
tickMotion(delta: number) {
|
||||
this.x += this.xvel * delta;
|
||||
this.y += this.yvel * delta;
|
||||
this.r += this.rvel * delta;
|
||||
this.rvel *= this.rfriction ** delta;
|
||||
}
|
||||
|
||||
tickGravity(bodies, speed = 1) {
|
||||
tickGravity(delta: number, bodies) {
|
||||
for (let body of bodies) {
|
||||
const distanceSquared = this.distanceToSquared(body);
|
||||
if (distanceSquared > (1000 ** 2)) continue;
|
||||
let force = body.mass / distanceSquared * G;
|
||||
let [[ax, ay], [bx, by]] = [this.com, body.com];
|
||||
let angle = Math.atan2(by - ay, bx - ax);
|
||||
this.xvel += Math.cos(angle) * force * speed;
|
||||
this.yvel += Math.sin(angle) * force * speed;
|
||||
this.xvel += Math.cos(angle) * force * delta;
|
||||
this.yvel += Math.sin(angle) * force * delta;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,11 +111,11 @@ export default class Body {
|
|||
this.yvel = 0;
|
||||
}
|
||||
|
||||
applyDirectionalForce(x, y, r) {
|
||||
applyDirectionalForce(x, y, rotation: number) {
|
||||
let [vx, vy] = this.rotateVector(x, y);
|
||||
this.xvel += vx / this.mass;
|
||||
this.yvel += vy / this.mass;
|
||||
this.rvel += r / this.mass;
|
||||
this.rvel += rotation / this.mass;
|
||||
}
|
||||
|
||||
orbit(cel, altitude, angle = 0) {
|
||||
|
|
|
@ -32,7 +32,7 @@ export default class Celestial extends Body {
|
|||
|
||||
}
|
||||
|
||||
tick() {
|
||||
tick(delta: number) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -46,12 +46,12 @@ export default class Entity extends Body {
|
|||
entities.delete(this);
|
||||
}
|
||||
|
||||
tick() {
|
||||
tick(delta: number) {
|
||||
if (Math.abs(playerShip.x - this.x) > 500 ||
|
||||
Math.abs(playerShip.y - this.y) > 500) return;
|
||||
this.r += consts.ENTITY_ROTATION_RATE;
|
||||
this.tickMotion();
|
||||
if (this.gravity) this.tickGravity(celestials);
|
||||
this.tickMotion(delta);
|
||||
if (this.gravity) this.tickGravity(delta, celestials);
|
||||
let col = this.getCelestialCollision(celestials);
|
||||
|
||||
if (col !== false) {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import * as spawn from './spawn';
|
||||
import * as graphics from '../graphics/index';
|
||||
import { Particle } from './particle';
|
||||
import Tracer from './tracer';
|
||||
import Ship from './ship';
|
||||
import Celestial from './celestial';
|
||||
import Entity from './entity';
|
||||
|
||||
export const entities = new Set();
|
||||
export const celestials = new Set();
|
||||
export const ships = new Set();
|
||||
export const particles = new Set();
|
||||
export const tracers = new Set();
|
||||
export const entities: Set<Entity> = new Set();
|
||||
export const celestials: Set<Celestial> = new Set();
|
||||
export const ships: Set<Ship> = new Set();
|
||||
export const particles: Set<Particle> = new Set();
|
||||
export const tracers: Set<Tracer> = new Set();
|
||||
|
||||
export let playerShip = null;
|
||||
export let playerShip: Ship = null;
|
||||
|
||||
export let speed = 1;
|
||||
|
||||
|
@ -43,14 +48,14 @@ export function decreaseSpeed() {
|
|||
if (speed > 1) speed -= 1;
|
||||
}
|
||||
|
||||
export function tick() {
|
||||
export function tick(delta: number) {
|
||||
for (let i = 0; i < speed; i++) {
|
||||
particles.forEach(p => p.tick());
|
||||
celestials.forEach(c => c.tick());
|
||||
entities.forEach(e => e.tick());
|
||||
ships.forEach(s => s.tick());
|
||||
particles.forEach(p => p.tick(delta));
|
||||
celestials.forEach(c => c.tick(delta));
|
||||
entities.forEach(e => e.tick(delta));
|
||||
ships.forEach(s => s.tick(delta));
|
||||
}
|
||||
|
||||
spawn.tick();
|
||||
if (graphics.trace) tracers.forEach(t => t.tick());
|
||||
if (graphics.trace) tracers.forEach(t => t.tick(delta));
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export function createItemToss(ship) {
|
|||
}));
|
||||
}
|
||||
|
||||
class Particle extends Body {
|
||||
export class Particle extends Body {
|
||||
constructor(x, y, {
|
||||
xvel = 0,
|
||||
yvel = 0,
|
||||
|
@ -113,14 +113,14 @@ class Particle extends Body {
|
|||
return [this.x - this.size / 2, this.y - this.size / 2];
|
||||
}
|
||||
|
||||
tick() {
|
||||
tick(delta: number) {
|
||||
if (this.life-- <= 0) {
|
||||
particles.delete(this);
|
||||
return;
|
||||
}
|
||||
|
||||
this.tickMotion();
|
||||
if (this.gravity) this.tickGravity(celestials);
|
||||
this.tickMotion(delta);
|
||||
if (this.gravity) this.tickGravity(delta, celestials);
|
||||
|
||||
this.xvel *= this.friction;
|
||||
this.yvel *= this.friction;
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as consts from '../consts';
|
|||
import * as particle from './particle';
|
||||
import * as events from '../game/events';
|
||||
import Tracer from './tracer';
|
||||
import {state} from '../game/index';
|
||||
import { state } from '../game/index';
|
||||
|
||||
export default class Ship extends Body {
|
||||
constructor(x, y) {
|
||||
|
@ -50,15 +50,18 @@ export default class Ship extends Body {
|
|||
return closest;
|
||||
}
|
||||
|
||||
tick() {
|
||||
tick(delta: number) {
|
||||
if (this.crashed) return;
|
||||
if (!state.editing) this.tickMotion();
|
||||
if (!this.landed) this.tickGravity(world.celestials);
|
||||
if (!state.editing) this.tickMotion(delta);
|
||||
if (!this.landed) this.tickGravity(delta, world.celestials);
|
||||
if (!state.editing) this.resolveCollisions();
|
||||
|
||||
this.modules.forEach(m => {
|
||||
if (m.type == 'thruster' && m.power !== 0) {
|
||||
for (let i = 0; i < 2; i++) particle.createThrustExhaust(m);
|
||||
for (let i = 0; i < 20; i++) {
|
||||
if (Math.random() > (delta / 10)) continue;
|
||||
particle.createThrustExhaust(m);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -89,7 +92,7 @@ export default class Ship extends Body {
|
|||
}
|
||||
|
||||
addModule(x, y, properties, options) {
|
||||
let module = new Module(x, y, this, {...properties, ...options});
|
||||
let module = new Module(x, y, this, { ...properties, ...options });
|
||||
this.modules.add(module);
|
||||
this.refreshShape();
|
||||
}
|
||||
|
@ -102,7 +105,7 @@ export default class Ship extends Body {
|
|||
refreshShape() {
|
||||
let points = [];
|
||||
this.modules.forEach(m => points.push([...m.localCom, m.mass]));
|
||||
this.mass = points.reduce((a, [,,b]) => a + b, 0);
|
||||
this.mass = points.reduce((a, [, , b]) => a + b, 0);
|
||||
this.localCom = points.reduce(([ax, ay], [bx, by, bm]) =>
|
||||
[ax + bx * bm, ay + by * bm], [0, 0])
|
||||
.map(x => x / this.mass);
|
||||
|
@ -214,7 +217,7 @@ export default class Ship extends Body {
|
|||
}
|
||||
|
||||
applyThrust({ forward = 0, left = 0, right = 0, back = 0,
|
||||
turnLeft = 0, turnRight = 0}) {
|
||||
turnLeft = 0, turnRight = 0 }) {
|
||||
|
||||
let thrustForce = -forward * consts.THRUST_POWER * this.thrust;
|
||||
let turnForce = (turnRight - turnLeft) * consts.TURN_POWER;
|
||||
|
|
|
@ -6,8 +6,11 @@ import {celestials, particles, entities} from './index';
|
|||
import * as particle from './particle';
|
||||
import * as consts from '../consts';
|
||||
import * as events from '../game/events';
|
||||
import Ship from './ship';
|
||||
|
||||
export default class Tracer extends Body {
|
||||
ship: Ship;
|
||||
|
||||
constructor(ship) {
|
||||
super(...ship.pos, 0.1);
|
||||
|
||||
|
@ -39,13 +42,13 @@ export default class Tracer extends Body {
|
|||
[this.x, this.y] = this.ship.com;
|
||||
}
|
||||
|
||||
tick() {
|
||||
tick(delta: number) {
|
||||
this.run(this.ship.computation);
|
||||
}
|
||||
|
||||
tickPath(speed) {
|
||||
this.tickMotion(speed);
|
||||
this.tickGravity(celestials, speed);
|
||||
this.tickGravity(speed, celestials);
|
||||
return !!this.getCelestialCollision(celestials);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue