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