Improve planet collision
This commit is contained in:
parent
4959519f39
commit
ba803b7ca9
7 changed files with 86 additions and 28 deletions
|
@ -58,8 +58,7 @@ class Perspective {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.focus !== null) {
|
if (this.focus !== null) {
|
||||||
this.x = this.focus.x;
|
[this.x, this.y] = this.focus.com;
|
||||||
this.y = this.focus.y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.rotationFocus !== null) {
|
if (this.rotationFocus !== null) {
|
||||||
|
|
|
@ -5,15 +5,19 @@ import * as world from '../world/index.mjs';
|
||||||
export function render() {
|
export function render() {
|
||||||
world.celestials.forEach(renderCelestial);
|
world.celestials.forEach(renderCelestial);
|
||||||
world.ships.forEach(renderShip);
|
world.ships.forEach(renderShip);
|
||||||
|
if (typeof window.q !== 'undefined') {
|
||||||
|
context.fillStyle = 'red';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderShip(ship) {
|
function renderShip(ship) {
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(ship.x, ship.y);
|
context.translate(...ship.com);
|
||||||
context.rotate(ship.r);
|
context.rotate(ship.r);
|
||||||
let [cx, cy] = ship.com;
|
let [cx, cy] = ship.localCom;
|
||||||
|
context.translate(-cx, -cy);
|
||||||
ship.modules.forEach(m => {
|
ship.modules.forEach(m => {
|
||||||
context.drawImage(m.currentImage, m.x - cx, m.y - cy, 1, 1);
|
context.drawImage(m.currentImage, m.x, m.y, 1, 1);
|
||||||
});
|
});
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
|
@ -23,6 +27,6 @@ const celestialImages = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCelestial(cel) {
|
function renderCelestial(cel) {
|
||||||
context.drawImage(cel.image, cel.x - cel.radius, cel.y - cel.radius,
|
context.drawImage(cel.image, cel.x, cel.y,
|
||||||
cel.diameter, cel.diameter);
|
cel.diameter, cel.diameter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,32 @@ export default class Body {
|
||||||
this.mass = mass;
|
this.mass = mass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get com() {
|
||||||
|
return [this.x, this.y];
|
||||||
|
}
|
||||||
|
|
||||||
|
get pos() {
|
||||||
|
return [this.x, this.y];
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorldPoint(lx, ly) {
|
||||||
|
let [cx, cy] = this.localCom;
|
||||||
|
let [nx, ny] = this.rotateVector(lx - cx, ly - cy, this.r);
|
||||||
|
return [nx + this.x + cx, ny + this.y + cy];
|
||||||
|
}
|
||||||
|
|
||||||
|
getLocalPoint(wx, wy) {
|
||||||
|
let [lx, ly] = [wx - this.x, wy - this.y];
|
||||||
|
let [cx, cy] = this.localCom;
|
||||||
|
let [nx, ny] = this.rotateVector(lx, ly, -this.r);
|
||||||
|
return [nx - cx, ny - cy];
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateVector(x, y, r) {
|
||||||
|
return [(x * Math.cos(this.r) - y * Math.sin(this.r)),
|
||||||
|
(y * Math.cos(this.r) - x * Math.sin(this.r))];
|
||||||
|
}
|
||||||
|
|
||||||
tickMotion() {
|
tickMotion() {
|
||||||
this.x += this.xvel;
|
this.x += this.xvel;
|
||||||
this.y += this.yvel;
|
this.y += this.yvel;
|
||||||
|
@ -22,19 +48,23 @@ export default class Body {
|
||||||
tickGravity(bodies) {
|
tickGravity(bodies) {
|
||||||
bodies.forEach(b => {
|
bodies.forEach(b => {
|
||||||
let force = b.mass / this.mass / (this.distanceTo(b) ** 2) * G;
|
let force = b.mass / this.mass / (this.distanceTo(b) ** 2) * G;
|
||||||
let angle = Math.atan2(b.y - this.y, b.x - this.x);
|
let [[ax, ay], [bx, by]] = [this.com, b.com];
|
||||||
|
let angle = Math.atan2(by - ay, bx - ax);
|
||||||
this.xvel += Math.cos(angle) * force;
|
this.xvel += Math.cos(angle) * force;
|
||||||
this.yvel += Math.sin(angle) * force;
|
this.yvel += Math.sin(angle) * force;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
distanceTo(body) {
|
distanceTo(body) {
|
||||||
return Math.max(Math.sqrt(((body.x - this.x) ** 2) +
|
let [[ax, ay], [bx, by]] = [this.com, body.com];
|
||||||
((body.y - this.y) ** 2)), 1);
|
let result = Math.max(Math.sqrt(((bx - ax) ** 2) +
|
||||||
|
((by - ay) ** 2)), 1);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
approach(body, distance) {
|
approach(body, distance) {
|
||||||
let angle = Math.atan2(body.y - this.y, body.x - this.x);
|
let [[ax, ay], [bx, by]] = [this.com, body.com];
|
||||||
|
let angle = Math.atan2(by - ay, bx - ax);
|
||||||
this.x += Math.cos(angle) * distance;
|
this.x += Math.cos(angle) * distance;
|
||||||
this.y += Math.sin(angle) * distance;
|
this.y += Math.sin(angle) * distance;
|
||||||
}
|
}
|
||||||
|
@ -45,8 +75,9 @@ export default class Body {
|
||||||
}
|
}
|
||||||
|
|
||||||
applyDirectionalForce(x, y, r) {
|
applyDirectionalForce(x, y, r) {
|
||||||
this.xvel += (x * Math.cos(this.r) - y * Math.sin(this.r)) / this.mass;
|
let [vx, vy] = this.rotateVector(x, y, r);
|
||||||
this.yvel += (y * Math.cos(this.r) - x * Math.sin(this.r)) / this.mass;
|
this.xvel += vx;
|
||||||
|
this.yvel += vy;
|
||||||
this.rvel += r / this.mass;
|
this.rvel += r / this.mass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,12 @@ export default class Celestial extends Body {
|
||||||
this.image = imageArr[Math.random() * imageArr.length | 0];
|
this.image = imageArr[Math.random() * imageArr.length | 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
tick() {
|
get com() {
|
||||||
|
return [this.x + this.radius, this.y + this.radius];
|
||||||
}
|
}
|
||||||
|
|
||||||
get center() {
|
tick() {
|
||||||
return [this.x + this.radius / 2, this.y + this.radius / 2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get diameter() {
|
get diameter() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {images as assets} from '../assets.mjs';
|
import {images as assets} from '../assets.mjs';
|
||||||
|
|
||||||
export default class Module {
|
export default class Module {
|
||||||
constructor(x, y, {
|
constructor(x, y, ship, {
|
||||||
name = 'Unnamed Module',
|
name = 'Unnamed Module',
|
||||||
type = 'block',
|
type = 'block',
|
||||||
id = 'unknown',
|
id = 'unknown',
|
||||||
|
@ -16,6 +16,7 @@ export default class Module {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.mass = mass;
|
this.mass = mass;
|
||||||
|
this.ship = ship;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.images = assets.modules[this.type][this.id];
|
this.images = assets.modules[this.type][this.id];
|
||||||
// Fuel
|
// Fuel
|
||||||
|
@ -32,6 +33,10 @@ export default class Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get com() {
|
||||||
|
return this.ship.getWorldPoint(...this.localCom);
|
||||||
|
}
|
||||||
|
|
||||||
get currentImage() {
|
get currentImage() {
|
||||||
if (this.type == 'thruster') {
|
if (this.type == 'thruster') {
|
||||||
return this.power > 0 ? this.images.on : this.images.off;
|
return this.power > 0 ? this.images.on : this.images.off;
|
||||||
|
@ -40,7 +45,7 @@ export default class Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get com() {
|
get localCom() {
|
||||||
return [this.x + 0.5, this.y + 0.5];
|
return [this.x + 0.5, this.y + 0.5];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,14 @@ export default class Ship extends Body {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
super(x, y, 0);
|
super(x, y, 0);
|
||||||
|
|
||||||
this.com = [0, 0];
|
this.localCom = [0, 0];
|
||||||
this.modules = new Set();
|
this.modules = new Set();
|
||||||
|
this.maxRadius = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get com() {
|
||||||
|
let [lx, ly] = this.localCom;
|
||||||
|
return [this.x + lx, this.y + ly];
|
||||||
}
|
}
|
||||||
|
|
||||||
tick() {
|
tick() {
|
||||||
|
@ -18,7 +24,7 @@ export default class Ship extends Body {
|
||||||
}
|
}
|
||||||
|
|
||||||
addModule(x, y, properties, options) {
|
addModule(x, y, properties, options) {
|
||||||
let module = new Module(x, y, {...properties, ...options});
|
let module = new Module(x, y, this, {...properties, ...options});
|
||||||
this.modules.add(module);
|
this.modules.add(module);
|
||||||
this.refreshShape();
|
this.refreshShape();
|
||||||
}
|
}
|
||||||
|
@ -30,24 +36,37 @@ export default class Ship extends Body {
|
||||||
|
|
||||||
refreshShape() {
|
refreshShape() {
|
||||||
let points = [];
|
let points = [];
|
||||||
this.modules.forEach(m => points.push([...m.com, m.mass]));
|
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.com = points.reduce(([ax, ay], [bx, by, bm]) =>
|
this.localCom = points.reduce(([ax, ay], [bx, by, bm]) =>
|
||||||
[ax + bx * bm, ay + by * bm], [0, 0])
|
[ax + bx * bm, ay + by * bm], [0, 0])
|
||||||
.map(x => x / this.mass);
|
.map(x => x / this.mass);
|
||||||
window.q = points;
|
let [lx, ly] = this.localCom;
|
||||||
|
this.maxRadius = points.reduce((a, [bx, by]) =>
|
||||||
|
Math.max(Math.sqrt((bx - lx) ** 2 + (by - ly) ** 2), a), 0) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveCollisions() {
|
resolveCollisions() {
|
||||||
world.celestials.forEach(c => {
|
world.celestials.forEach(c => {
|
||||||
let dis = this.distanceTo(c);
|
let dis = this.distanceTo(c);
|
||||||
if (dis < c.radius) {
|
|
||||||
this.approach(c, dis - c.radius);
|
if (dis < c.radius + this.maxRadius) {
|
||||||
this.halt();
|
this.modules.forEach(m => {
|
||||||
|
this.checkModuleCollision(m, c);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkModuleCollision(module, body) {
|
||||||
|
let p = this.getWorldPoint(...module.localCom)
|
||||||
|
let dis = body.distanceTo({ com: p });
|
||||||
|
if (dis < body.radius + 0.5) {
|
||||||
|
this.approach(body, dis - (body.radius + 0.5));
|
||||||
|
this.halt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
applyThrust({ forward = 0, left = 0, right = 0, back = 0,
|
applyThrust({ forward = 0, left = 0, right = 0, back = 0,
|
||||||
turnLeft = 0, turnRight = 0}) {
|
turnLeft = 0, turnRight = 0}) {
|
||||||
let turnForce = (turnRight - turnLeft) / 20;
|
let turnForce = (turnRight - turnLeft) / 20;
|
||||||
|
|
|
@ -16,13 +16,13 @@ export function player() {
|
||||||
|
|
||||||
export function startPlanet() {
|
export function startPlanet() {
|
||||||
return celestial(0, 0, 40, {
|
return celestial(0, 0, 40, {
|
||||||
density: 1,
|
density: 10,
|
||||||
type: 'green'
|
type: 'green'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function celestial(x, y, radius, params) {
|
export function celestial(x, y, radius, params) {
|
||||||
let celestial = new Celestial(x, y, radius, params);
|
let celestial = new Celestial(x - radius, y - radius, radius, params);
|
||||||
world.celestials.add(celestial);
|
world.celestials.add(celestial);
|
||||||
return celestial;
|
return celestial;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue