Add ship movement

This commit is contained in:
asraelite 2018-03-03 15:58:51 +00:00
parent 09b3df649c
commit 4959519f39
15 changed files with 278 additions and 77 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before After
Before After

View file

@ -25,11 +25,11 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0.0" inkscape:pageopacity="0.0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="2.8" inkscape:zoom="7.9195959"
inkscape:cx="54.726513" inkscape:cx="59.772356"
inkscape:cy="10.343454" inkscape:cy="7.3948782"
inkscape:document-units="mm" inkscape:document-units="mm"
inkscape:current-layer="layer1" inkscape:current-layer="g828"
showgrid="true" showgrid="true"
inkscape:window-width="1366" inkscape:window-width="1366"
inkscape:window-height="714" inkscape:window-height="714"
@ -60,41 +60,44 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(-85.489576,-133.78125)"> transform="translate(-85.489576,-133.78125)">
<path <g
style="opacity:1;fill:#a0948f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" id="g828">
d="m 85.989583,134.28125 h 10.583332 10.583335 v 1.32292 l -2.64583,3.96875 -3.96875,2.64583 h -7.937502 l -3.968751,-2.64583 -2.645834,-3.96875 z" <path
id="rect821" sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" /> id="rect821"
<path d="m 85.989583,134.28125 h 10.583332 10.583335 v 1.32292 l -2.64583,3.96875 -3.96875,2.64583 h -7.937502 l -3.968751,-2.64583 -2.645834,-3.96875 z"
style="opacity:1;fill:#6b3c25;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" style="opacity:1;fill:#a0948f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
d="m 89.958334,135.60417 h 2.645834 v 6.61458 l -1.322917,-1.32292 z" <path
id="rect908" sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> id="rect908"
<path d="m 89.958334,135.60417 h 2.645834 v 6.61458 l -1.322917,-1.32292 z"
style="opacity:1;fill:#6b3c25;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" style="opacity:1;fill:#6b3c25;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
d="m 103.1875,135.60417 h -2.64582 v 6.61458 l 1.32291,-1.32292 z" <path
id="rect908-3" sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> id="rect908-3"
<path d="m 103.1875,135.60417 h -2.64582 v 6.61458 l 1.32291,-1.32292 z"
style="opacity:1;fill:#272727;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill" style="opacity:1;fill:#6b3c25;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
d="m 85.989578,134.28125 h 21.166672 v 1.32292 H 85.989578 Z" <path
id="rect908-5-6" sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> id="rect908-5-6"
<path d="m 85.989578,134.28125 h 21.166672 v 1.32292 H 85.989578 Z"
style="opacity:1;fill:#191919;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" style="opacity:1;fill:#272727;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill" />
d="m 91.281251,154.125 h 10.583329 l 1.32292,1.32292 H 89.958334 Z" <path
id="rect908-5-6-2" sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> id="thrust"
<path d="m 91.281251,154.125 h 10.583329 l 1.32292,1.32292 H 89.958334 Z"
style="opacity:1;fill:#5b4f4f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" style="opacity:1;fill:#191919;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
d="m 94.456251,142.21875 h 4.233333 l -0.79375,2.64583 5.291666,10.58334 -2.64583,-1.32292 h -7.937502 l -2.645834,1.32292 5.29167,-10.58334 z" <path
id="rect908-7" sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" /> id="rect908-7"
d="m 94.456251,142.21875 h 4.233333 l -0.79375,2.64583 5.291666,10.58334 -2.64583,-1.32292 h -7.937502 l -2.645834,1.32292 5.29167,-10.58334 z"
style="opacity:1;fill:#5b4f4f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before After
Before After

103
dist/img/modules/light_thruster_on.svg vendored Normal file
View file

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22.166674mm"
height="22.16667mm"
viewBox="0 0 22.166674 22.16667"
version="1.1"
id="svg8"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="light_thruster_on.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="27.702102"
inkscape:cy="21.479202"
inkscape:document-units="mm"
inkscape:current-layer="g828"
showgrid="true"
inkscape:window-width="1366"
inkscape:window-height="714"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="false">
<inkscape:grid
type="xygrid"
id="grid815"
originx="-85.489575"
originy="-141.05208" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-85.489576,-133.78125)">
<g
id="g828">
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
id="rect821"
d="m 85.989583,134.28125 h 10.583332 10.583335 v 1.32292 l -2.64583,3.96875 -3.96875,2.64583 h -7.937502 l -3.968751,-2.64583 -2.645834,-3.96875 z"
style="opacity:1;fill:#a0948f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect908"
d="m 89.958334,135.60417 h 2.645834 v 6.61458 l -1.322917,-1.32292 z"
style="opacity:1;fill:#6b3c25;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect908-3"
d="m 103.1875,135.60417 h -2.64582 v 6.61458 l 1.32291,-1.32292 z"
style="opacity:1;fill:#6b3c25;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect908-5-6"
d="m 85.989578,134.28125 h 21.166672 v 1.32292 H 85.989578 Z"
style="opacity:1;fill:#272727;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="thrust"
d="m 91.281251,154.125 h 10.583329 l 1.32292,1.32292 H 89.958334 Z"
style="opacity:1;fill:#e49514;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="rect908-7"
d="m 94.456251,142.21875 h 4.233333 l -0.79375,2.64583 5.291666,10.58334 -2.64583,-1.32292 h -7.937502 l -2.645834,1.32292 5.29167,-10.58334 z"
style="opacity:1;fill:#5b4f4f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -11,7 +11,10 @@ export const images = {
small: 'modules/small_fuel_tank.svg' small: 'modules/small_fuel_tank.svg'
}, },
thruster: { thruster: {
light: 'modules/light_thruster.svg' light: {
off: 'modules/light_thruster.svg',
on: 'modules/light_thruster_on.svg',
}
} }
}, },
celestials: { celestials: {

View file

@ -13,6 +13,6 @@ export const STAR_DENSITY = (SECTOR_SIZE ** 2) / 10000;
export const GRAVITATIONAL_CONSTANT = 0.01; export const GRAVITATIONAL_CONSTANT = 0.01;
// Perspective constraints. Higher zoom value = closer. // Perspective constraints. Higher zoom value = closer.
export const MIN_ZOOM = 1; export const MIN_ZOOM = 1;
export const MAX_ZOOM = 30; export const MAX_ZOOM = 100;
export const DEFAULT_ZOOM = 10; export const DEFAULT_ZOOM = 10;
export const ZOOM_SPEED = 0.01; export const ZOOM_SPEED = 0.01;

View file

@ -5,6 +5,7 @@ export const modules = {
tooltip: 'A small, simple capsule. Provides just enough ' + tooltip: 'A small, simple capsule. Provides just enough ' +
'rotational power for a small rocket.', 'rotational power for a small rocket.',
type: 'capsule', type: 'capsule',
id: 'small',
mass: 2, mass: 2,
rotation: 0.1 rotation: 0.1
} }
@ -14,6 +15,7 @@ export const modules = {
name: 'Small Fuel Tank', name: 'Small Fuel Tank',
tooltip: 'A small flimsy tank with enough fuel for a short trip.', tooltip: 'A small flimsy tank with enough fuel for a short trip.',
type: 'fuel', type: 'fuel',
id: 'small',
mass: 1, mass: 1,
capacity: 3 capacity: 3
} }
@ -24,6 +26,7 @@ export const modules = {
tooltip: 'Powerful enough to lift a small ship, but not much ' + tooltip: 'Powerful enough to lift a small ship, but not much ' +
'more. Not very efficient.', 'more. Not very efficient.',
type: 'thruster', type: 'thruster',
id: 'light',
mass: 2, mass: 2,
thrust: 10, thrust: 10,
isp: 200 isp: 200

25
js/game/control.mjs Normal file
View file

@ -0,0 +1,25 @@
import * as input from '../input.mjs';
import * as player from './player.mjs';
export const mapping = {
thrust: 'KeyW',
left: 'KeyA',
right: 'KeyD'
};
export function tick() {
let held = input.keyCode.held;
let pressed = input.keyCode.pressed;
if (held[mapping.thrust]) {
player.ship.applyThrust({ forward: 1 });
}
if (held[mapping.left]) {
player.ship.applyThrust({ turnLeft: 1 });
}
if (held[mapping.right]) {
player.ship.applyThrust({ turnRight: 1 });
}
}

View file

@ -1,6 +1,7 @@
import * as game from './index.mjs'; import * as game from './index.mjs';
import * as graphics from '../graphics/index.mjs'; import * as graphics from '../graphics/index.mjs';
import * as world from '../world/index.mjs'; import * as world from '../world/index.mjs';
import * as player from './player.mjs';
export function startGame() { export function startGame() {
game.changeView('game'); game.changeView('game');

View file

@ -4,6 +4,8 @@ import * as assets from '../assets.mjs';
import * as input from '../input.mjs'; import * as input from '../input.mjs';
import * as world from '../world/index.mjs'; import * as world from '../world/index.mjs';
import * as events from './events.mjs'; import * as events from './events.mjs';
import * as control from './control.mjs';
import * as player from './player.mjs';
export let state; export let state;
@ -35,11 +37,16 @@ export function changeView(view) {
if (view == 'game') { if (view == 'game') {
world.init(); world.init();
player.init();
} }
} }
async function tick() { async function tick() {
if (state.view == 'game') world.tick(); if (state.view == 'game') {
world.tick();
control.tick();
}
gui.tick(); gui.tick();
graphics.render(); graphics.render();
input.tick(); input.tick();

7
js/game/player.mjs Normal file
View file

@ -0,0 +1,7 @@
import * as world from '../world/index.mjs';
export let ship;
export function init() {
ship = world.playerShip;
}

View file

@ -8,15 +8,14 @@ export function render() {
} }
function renderShip(ship) { function renderShip(ship) {
context.fillStyle = 'red'; context.save();
//context.fillRect(ship.x, ship.y, 10, 10); context.translate(ship.x, ship.y);
let size = 1; context.rotate(ship.r);
context.drawImage(assets.modules.capsule.small, ship.x, ship.y, let [cx, cy] = ship.com;
size, size); ship.modules.forEach(m => {
context.drawImage(assets.modules.fuel.small, ship.x, ship.y + size, context.drawImage(m.currentImage, m.x - cx, m.y - cy, 1, 1);
size, size); });
context.drawImage(assets.modules.thruster.light, ship.x, context.restore();
ship.y + size * 2, size, size);
} }
const celestialImages = { const celestialImages = {

View file

@ -8,12 +8,15 @@ export default class Body {
this.xvel = 0; this.xvel = 0;
this.yvel = 0; this.yvel = 0;
this.rvel = 0; this.rvel = 0;
this.rfriction = 0.9;
this.mass = mass; this.mass = mass;
} }
tickMotion() { tickMotion() {
this.x += this.xvel; this.x += this.xvel;
this.y += this.yvel; this.y += this.yvel;
this.r += this.rvel;
this.rvel *= this.rfriction;
} }
tickGravity(bodies) { tickGravity(bodies) {
@ -40,4 +43,10 @@ export default class Body {
this.xvel = 0; this.xvel = 0;
this.yvel = 0; this.yvel = 0;
} }
applyDirectionalForce(x, y, r) {
this.xvel += (x * Math.cos(this.r) - y * Math.sin(this.r)) / this.mass;
this.yvel += (y * Math.cos(this.r) - x * Math.sin(this.r)) / this.mass;
this.rvel += r / this.mass;
}
} }

View file

@ -1,7 +1,10 @@
import {images as assets} from '../assets.mjs';
export default class Module { export default class Module {
constructor(x, y, { constructor(x, y, {
name = 'Unnamed Module', name = 'Unnamed Module',
type = 'block', type = 'block',
id = 'unknown',
mass = 1, mass = 1,
// Fuel // Fuel
filled = false, filled = false,
@ -13,7 +16,31 @@ export default class Module {
this.name = name; this.name = name;
this.type = type; this.type = type;
this.mass = mass; this.mass = mass;
this.id = id;
this.images = assets.modules[this.type][this.id];
// Fuel // Fuel
this.fuel = filled ? fuelCapacity : 0; if (this.type == 'fuel') {
this.fuel = filled ? fuelCapacity : 0;
} else if (this.type == 'thruster') {
this.power = 0;
}
}
reset() {
if (this.type == 'thruster') {
this.power = 0;
}
}
get currentImage() {
if (this.type == 'thruster') {
return this.power > 0 ? this.images.on : this.images.off;
} else {
return this.images;
}
}
get com() {
return [this.x + 0.5, this.y + 0.5];
} }
} }

View file

@ -14,6 +14,7 @@ export default class Ship extends Body {
this.tickMotion(); this.tickMotion();
this.tickGravity(world.celestials); this.tickGravity(world.celestials);
this.resolveCollisions(); this.resolveCollisions();
this.modules.forEach(m => m.reset());
} }
addModule(x, y, properties, options) { addModule(x, y, properties, options) {
@ -29,11 +30,12 @@ export default class Ship extends Body {
refreshShape() { refreshShape() {
let points = []; let points = [];
this.modules.forEach(m => points.push([m.x, m.y, m.mass])); this.modules.forEach(m => points.push([...m.com, m.mass]));
this.mass = points.reduce((a, [,,b]) => a + b, 0); this.mass = points.reduce((a, [,,b]) => a + b, 0);
this.com = points.reduce(([ax, ay], b) => this.com = points.reduce(([ax, ay], [bx, by, bm]) =>
[ax + b.x * b.mass, ay + b.y * b.mass], [0, 0]) [ax + bx * bm, ay + by * bm], [0, 0])
.map(x => x / this.mass); .map(x => x / this.mass);
window.q = points;
} }
resolveCollisions() { resolveCollisions() {
@ -45,4 +47,16 @@ export default class Ship extends Body {
} }
}) })
} }
applyThrust({ forward = 0, left = 0, right = 0, back = 0,
turnLeft = 0, turnRight = 0}) {
let turnForce = (turnRight - turnLeft) / 20;
this.applyDirectionalForce(0, -forward / 30, turnForce);
this.modules.forEach(m => {
if (m.type !== 'thruster') return;
m.power = forward;
});
}
} }

View file

@ -5,7 +5,7 @@ import {modules} from '../data.mjs';
import * as world from './index.mjs'; import * as world from './index.mjs';
export function player() { export function player() {
let ship = new Ship(0, 0); let ship = new Ship(0, -45);
ship.addModule(0, 0, modules.capsule.small); ship.addModule(0, 0, modules.capsule.small);
ship.addModule(0, 1, modules.fuel.small, { filled: true }); ship.addModule(0, 1, modules.fuel.small, { filled: true });
ship.addModule(0, 2, modules.thruster.light); ship.addModule(0, 2, modules.thruster.light);
@ -15,7 +15,7 @@ export function player() {
} }
export function startPlanet() { export function startPlanet() {
return celestial(-40, 10, 40, { return celestial(0, 0, 40, {
density: 1, density: 1,
type: 'green' type: 'green'
}); });