Trains on tracks with left and right pathing
This commit is contained in:
parent
68eec35ea2
commit
69475b1bd8
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
bundle.js
|
bundle.js
|
||||||
dist/
|
dist/
|
||||||
|
temp.ts
|
79
bundle.js
79
bundle.js
@ -1476,7 +1476,6 @@
|
|||||||
).filter((s) => s);
|
).filter((s) => s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(track.segments);
|
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
translate(v) {
|
translate(v) {
|
||||||
@ -1495,7 +1494,7 @@
|
|||||||
if (!this.firstSegment) throw new Error("No first segment");
|
if (!this.firstSegment) throw new Error("No first segment");
|
||||||
const rightOnlyPath = [
|
const rightOnlyPath = [
|
||||||
this.firstSegment.copy(),
|
this.firstSegment.copy(),
|
||||||
...this.findRightPath(this.firstSegment)
|
...this.findRightPath(this.firstSegment, /* @__PURE__ */ new Set([this.firstSegment.id]))
|
||||||
];
|
];
|
||||||
rightOnlyPath.forEach((s, i, arr) => {
|
rightOnlyPath.forEach((s, i, arr) => {
|
||||||
if (i === 0) return;
|
if (i === 0) return;
|
||||||
@ -1504,15 +1503,13 @@
|
|||||||
s.prev = prev;
|
s.prev = prev;
|
||||||
prev.next = s;
|
prev.next = s;
|
||||||
});
|
});
|
||||||
console.log(rightOnlyPath);
|
|
||||||
return new Spline(rightOnlyPath);
|
return new Spline(rightOnlyPath);
|
||||||
}
|
}
|
||||||
*findRightPath(start) {
|
*findRightPath(start, seen) {
|
||||||
if (start.frontNeighbours.length === 0) {
|
if (start.frontNeighbours.length === 0) {
|
||||||
yield start;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let rightMost = start.frontNeighbours[0].copy();
|
let rightMost = start.frontNeighbours[0];
|
||||||
for (const segment of start.frontNeighbours) {
|
for (const segment of start.frontNeighbours) {
|
||||||
if (segment.id === rightMost.id) continue;
|
if (segment.id === rightMost.id) continue;
|
||||||
const rotatedSegment = segment.copy();
|
const rotatedSegment = segment.copy();
|
||||||
@ -1529,8 +1526,36 @@
|
|||||||
rightMost = segment;
|
rightMost = segment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (seen.has(rightMost.id)) return;
|
||||||
|
seen.add(rightMost.id);
|
||||||
yield rightMost.copy();
|
yield rightMost.copy();
|
||||||
yield* this.findRightPath(rightMost);
|
yield* this.findRightPath(rightMost, seen);
|
||||||
|
}
|
||||||
|
*findLeftPath(start, seen) {
|
||||||
|
if (start.frontNeighbours.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let leftMost = start.frontNeighbours[0];
|
||||||
|
for (const segment of start.frontNeighbours) {
|
||||||
|
if (segment.id === leftMost.id) continue;
|
||||||
|
const rotatedSegment = segment.copy();
|
||||||
|
rotatedSegment.rotateAboutPoint(
|
||||||
|
rotatedSegment.tangent(0).heading(),
|
||||||
|
rotatedSegment.points[0]
|
||||||
|
);
|
||||||
|
const rotatedLeftMost = leftMost.copy();
|
||||||
|
rotatedLeftMost.rotateAboutPoint(
|
||||||
|
rotatedLeftMost.tangent(0).heading(),
|
||||||
|
rotatedLeftMost.points[0]
|
||||||
|
);
|
||||||
|
if (rotatedSegment.points[3].y < rotatedLeftMost.points[3].y) {
|
||||||
|
leftMost = segment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (seen.has(leftMost.id)) return;
|
||||||
|
seen.add(leftMost.id);
|
||||||
|
yield leftMost.copy();
|
||||||
|
yield* this.findLeftPath(leftMost, seen);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var TrackSegment = class _TrackSegment extends PathSegment {
|
var TrackSegment = class _TrackSegment extends PathSegment {
|
||||||
@ -2083,22 +2108,17 @@
|
|||||||
constructor(track, cars) {
|
constructor(track, cars) {
|
||||||
this.path = track;
|
this.path = track;
|
||||||
this.t = 0;
|
this.t = 0;
|
||||||
this.nodes.push(this.path.followEvenPoints(this.t));
|
|
||||||
this.nodes.push(this.path.followEvenPoints(this.t - this.real2Track(40)));
|
|
||||||
const resources2 = getContextItem("resources");
|
const resources2 = getContextItem("resources");
|
||||||
const engineSprites = resources2.get("engine-sprites");
|
|
||||||
console.log(engineSprites);
|
|
||||||
this.cars = cars;
|
this.cars = cars;
|
||||||
this.cars[0].points = this.nodes.map((n) => n);
|
console.log(track);
|
||||||
this.cars[1].points = this.nodes.map((n) => n);
|
let currentOffset = 0;
|
||||||
let currentOffset = 40;
|
for (const car of this.cars) {
|
||||||
for (const car of cars) {
|
|
||||||
currentOffset += this.spacing;
|
currentOffset += this.spacing;
|
||||||
const a = this.path.followEvenPoints(this.t - currentOffset);
|
const a = this.path.followEvenPoints(this.t - currentOffset);
|
||||||
currentOffset += car.length;
|
currentOffset += car.length;
|
||||||
const b = this.path.followEvenPoints(this.t - currentOffset);
|
const b = this.path.followEvenPoints(this.t - currentOffset);
|
||||||
car.points = [a, b];
|
car.points = [a, b];
|
||||||
this.cars.push(car);
|
this.nodes.push(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
move(dTime) {
|
move(dTime) {
|
||||||
@ -2113,25 +2133,18 @@
|
|||||||
currentOffset += this.spacing;
|
currentOffset += this.spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// draw() {
|
|
||||||
// const doodler = getContextItem<Doodler>("doodler");
|
|
||||||
// this.path.draw();
|
|
||||||
// for (const [i, node] of this.nodes.entries()) {
|
|
||||||
// // doodler.drawCircle(node, 10, { color: "purple", weight: 3 });
|
|
||||||
// doodler.fillCircle(node, 2, { color: "purple" });
|
|
||||||
// // const next = this.nodes[i + 1];
|
|
||||||
// // if (next) {
|
|
||||||
// // const to = Vector.sub(node.point, next.point);
|
|
||||||
// // to.setMag(40);
|
|
||||||
// // doodler.line(next.point, Vector.add(to, next.point))
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
draw() {
|
draw() {
|
||||||
for (const car of this.cars) {
|
const doodler2 = getContextItem("doodler");
|
||||||
car.draw();
|
this.path.draw();
|
||||||
|
for (const [i, node] of this.nodes.entries()) {
|
||||||
|
doodler2.fillCircle(node, 2, { color: "purple" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// draw() {
|
||||||
|
// for (const car of this.cars) {
|
||||||
|
// car.draw();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
real2Track(length) {
|
real2Track(length) {
|
||||||
return length / this.path.pointSpacing;
|
return length / this.path.pointSpacing;
|
||||||
}
|
}
|
||||||
@ -2207,6 +2220,7 @@
|
|||||||
]);
|
]);
|
||||||
update(dt) {
|
update(dt) {
|
||||||
const ctx2 = getContext();
|
const ctx2 = getContext();
|
||||||
|
const input = getContextItem("inputManager");
|
||||||
ctx2.track.draw();
|
ctx2.track.draw();
|
||||||
for (const train of ctx2.trains) {
|
for (const train of ctx2.trains) {
|
||||||
train.move(dt);
|
train.move(dt);
|
||||||
@ -2292,7 +2306,6 @@
|
|||||||
fillScreen: true,
|
fillScreen: true,
|
||||||
bg: "#302040"
|
bg: "#302040"
|
||||||
});
|
});
|
||||||
doodler.scale = doodler.maxScale;
|
|
||||||
var colors = [
|
var colors = [
|
||||||
"red",
|
"red",
|
||||||
"orange",
|
"orange",
|
||||||
|
2
main.ts
2
main.ts
@ -21,7 +21,7 @@ const doodler = new ZoomableDoodler({
|
|||||||
bg: "#302040",
|
bg: "#302040",
|
||||||
});
|
});
|
||||||
// doodler.minScale = 0.1;
|
// doodler.minScale = 0.1;
|
||||||
(doodler as any).scale = doodler.maxScale;
|
// (doodler as any).scale = doodler.maxScale;
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
"red",
|
"red",
|
||||||
|
@ -3,6 +3,7 @@ import { InputManager } from "../../lib/input.ts";
|
|||||||
import { TrackSystem } from "../../track/system.ts";
|
import { TrackSystem } from "../../track/system.ts";
|
||||||
import { Tender } from "../../train/cars.ts";
|
import { Tender } from "../../train/cars.ts";
|
||||||
import { RedEngine } from "../../train/engines.ts";
|
import { RedEngine } from "../../train/engines.ts";
|
||||||
|
import { DotFollower } from "../../train/newTrain.ts";
|
||||||
import { Train } from "../../train/train.ts";
|
import { Train } from "../../train/train.ts";
|
||||||
import { State } from "../machine.ts";
|
import { State } from "../machine.ts";
|
||||||
import { States } from "./index.ts";
|
import { States } from "./index.ts";
|
||||||
@ -15,6 +16,8 @@ export class RunningState extends State<States> {
|
|||||||
]);
|
]);
|
||||||
override update(dt: number): void {
|
override update(dt: number): void {
|
||||||
const ctx = getContext() as { trains: Train[]; track: TrackSystem };
|
const ctx = getContext() as { trains: Train[]; track: TrackSystem };
|
||||||
|
// const ctx = getContext() as { trains: DotFollower[]; track: TrackSystem };
|
||||||
|
const input = getContextItem<InputManager>("inputManager");
|
||||||
// TODO
|
// TODO
|
||||||
// Update trains
|
// Update trains
|
||||||
// Update world
|
// Update world
|
||||||
@ -22,6 +25,9 @@ export class RunningState extends State<States> {
|
|||||||
// Draw (maybe via a layer system that syncs with doodler)
|
// Draw (maybe via a layer system that syncs with doodler)
|
||||||
ctx.track.draw();
|
ctx.track.draw();
|
||||||
for (const train of ctx.trains) {
|
for (const train of ctx.trains) {
|
||||||
|
// if (input.getKeyState("ArrowUp")) {
|
||||||
|
// train.acceleration.x += 10;
|
||||||
|
// }
|
||||||
train.move(dt);
|
train.move(dt);
|
||||||
train.draw();
|
train.draw();
|
||||||
}
|
}
|
||||||
@ -32,7 +38,11 @@ export class RunningState extends State<States> {
|
|||||||
const inputManager = getContextItem<InputManager>("inputManager");
|
const inputManager = getContextItem<InputManager>("inputManager");
|
||||||
const track = getContextItem<TrackSystem>("track");
|
const track = getContextItem<TrackSystem>("track");
|
||||||
const ctx = getContext() as { trains: Train[] };
|
const ctx = getContext() as { trains: Train[] };
|
||||||
|
// const ctx = getContext() as { trains: DotFollower[] };
|
||||||
inputManager.onKey(" ", () => {
|
inputManager.onKey(" ", () => {
|
||||||
|
// const path = track.path;
|
||||||
|
// const follower = new DotFollower(path, path.points[0].copy());
|
||||||
|
// ctx.trains.push(follower);
|
||||||
const train = new Train(track.path, [new RedEngine(), new Tender()]);
|
const train = new Train(track.path, [new RedEngine(), new Tender()]);
|
||||||
ctx.trains.push(train);
|
ctx.trains.push(train);
|
||||||
});
|
});
|
||||||
|
@ -107,10 +107,12 @@ export class TrackSystem {
|
|||||||
if (data.length === 0) return undefined;
|
if (data.length === 0) return undefined;
|
||||||
const track = new TrackSystem([]);
|
const track = new TrackSystem([]);
|
||||||
const neighborMap = new Map<string, [string[], string[]]>();
|
const neighborMap = new Map<string, [string[], string[]]>();
|
||||||
|
|
||||||
for (const segment of data) {
|
for (const segment of data) {
|
||||||
track.segments.set(segment.id, TrackSegment.deserialize(segment));
|
track.segments.set(segment.id, TrackSegment.deserialize(segment));
|
||||||
neighborMap.set(segment.id, [segment.fNeighbors, segment.bNeighbors]);
|
neighborMap.set(segment.id, [segment.fNeighbors, segment.bNeighbors]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const segment of track.segments.values()) {
|
for (const segment of track.segments.values()) {
|
||||||
segment.setTrack(track);
|
segment.setTrack(track);
|
||||||
const neighbors = neighborMap.get(segment.id);
|
const neighbors = neighborMap.get(segment.id);
|
||||||
@ -123,7 +125,7 @@ export class TrackSystem {
|
|||||||
).filter((s) => s) as TrackSegment[];
|
).filter((s) => s) as TrackSegment[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(track.segments);
|
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +148,7 @@ export class TrackSystem {
|
|||||||
if (!this.firstSegment) throw new Error("No first segment");
|
if (!this.firstSegment) throw new Error("No first segment");
|
||||||
const rightOnlyPath = [
|
const rightOnlyPath = [
|
||||||
this.firstSegment.copy(),
|
this.firstSegment.copy(),
|
||||||
...this.findRightPath(this.firstSegment),
|
...this.findRightPath(this.firstSegment, new Set([this.firstSegment.id])),
|
||||||
];
|
];
|
||||||
|
|
||||||
rightOnlyPath.forEach((s, i, arr) => {
|
rightOnlyPath.forEach((s, i, arr) => {
|
||||||
@ -157,17 +159,17 @@ export class TrackSystem {
|
|||||||
prev.next = s;
|
prev.next = s;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(rightOnlyPath);
|
|
||||||
|
|
||||||
return new Spline<TrackSegment>(rightOnlyPath);
|
return new Spline<TrackSegment>(rightOnlyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
*findRightPath(start: TrackSegment): Generator<TrackSegment> {
|
*findRightPath(
|
||||||
|
start: TrackSegment,
|
||||||
|
seen: Set<string>,
|
||||||
|
): Generator<TrackSegment> {
|
||||||
if (start.frontNeighbours.length === 0) {
|
if (start.frontNeighbours.length === 0) {
|
||||||
yield start;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let rightMost = start.frontNeighbours[0].copy();
|
let rightMost = start.frontNeighbours[0];
|
||||||
for (const segment of start.frontNeighbours) {
|
for (const segment of start.frontNeighbours) {
|
||||||
if (segment.id === rightMost.id) continue;
|
if (segment.id === rightMost.id) continue;
|
||||||
const rotatedSegment = segment.copy();
|
const rotatedSegment = segment.copy();
|
||||||
@ -184,8 +186,39 @@ export class TrackSystem {
|
|||||||
rightMost = segment;
|
rightMost = segment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (seen.has(rightMost.id)) return;
|
||||||
|
seen.add(rightMost.id);
|
||||||
yield rightMost.copy();
|
yield rightMost.copy();
|
||||||
yield* this.findRightPath(rightMost);
|
yield* this.findRightPath(rightMost, seen);
|
||||||
|
}
|
||||||
|
*findLeftPath(
|
||||||
|
start: TrackSegment,
|
||||||
|
seen: Set<string>,
|
||||||
|
): Generator<TrackSegment> {
|
||||||
|
if (start.frontNeighbours.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let leftMost = start.frontNeighbours[0];
|
||||||
|
for (const segment of start.frontNeighbours) {
|
||||||
|
if (segment.id === leftMost.id) continue;
|
||||||
|
const rotatedSegment = segment.copy();
|
||||||
|
rotatedSegment.rotateAboutPoint(
|
||||||
|
rotatedSegment.tangent(0).heading(),
|
||||||
|
rotatedSegment.points[0],
|
||||||
|
);
|
||||||
|
const rotatedLeftMost = leftMost.copy();
|
||||||
|
rotatedLeftMost.rotateAboutPoint(
|
||||||
|
rotatedLeftMost.tangent(0).heading(),
|
||||||
|
rotatedLeftMost.points[0],
|
||||||
|
);
|
||||||
|
if (rotatedSegment.points[3].y < rotatedLeftMost.points[3].y) {
|
||||||
|
leftMost = segment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (seen.has(leftMost.id)) return;
|
||||||
|
seen.add(leftMost.id);
|
||||||
|
yield leftMost.copy();
|
||||||
|
yield* this.findLeftPath(leftMost, seen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,7 +456,6 @@ export class Spline<T extends PathSegment = PathSegment> {
|
|||||||
const i = Math.floor(t) % this.evenPoints.length;
|
const i = Math.floor(t) % this.evenPoints.length;
|
||||||
const a = this.evenPoints[i];
|
const a = this.evenPoints[i];
|
||||||
const b = this.evenPoints[(i + 1) % this.evenPoints.length];
|
const b = this.evenPoints[(i + 1) % this.evenPoints.length];
|
||||||
|
|
||||||
return Vector.lerp(a, b, t % 1);
|
return Vector.lerp(a, b, t % 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
147
train/newTrain.ts
Normal file
147
train/newTrain.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { Doodler, Vector } from "@bearmetal/doodler";
|
||||||
|
import { getContextItem } from "../lib/context.ts";
|
||||||
|
import { Spline, TrackSegment } from "../track/system.ts";
|
||||||
|
|
||||||
|
export class DotFollower {
|
||||||
|
position: Vector;
|
||||||
|
velocity: Vector;
|
||||||
|
acceleration: Vector;
|
||||||
|
maxSpeed: number;
|
||||||
|
maxForce: number;
|
||||||
|
_trailingPoint: number;
|
||||||
|
protected _leadingPoint: number;
|
||||||
|
|
||||||
|
path: Spline<TrackSegment>;
|
||||||
|
|
||||||
|
get trailingPoint() {
|
||||||
|
const desired = this.velocity.copy();
|
||||||
|
desired.normalize();
|
||||||
|
desired.mult(-this._trailingPoint);
|
||||||
|
|
||||||
|
return Vector.add(this.position, desired);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(path: Spline<TrackSegment>, pos: Vector) {
|
||||||
|
this.path = path;
|
||||||
|
this.position = pos;
|
||||||
|
this.velocity = new Vector();
|
||||||
|
this.acceleration = new Vector();
|
||||||
|
this.maxSpeed = 3;
|
||||||
|
this.maxForce = 0.3;
|
||||||
|
|
||||||
|
this._trailingPoint = 0;
|
||||||
|
this._leadingPoint = 0;
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
move(dt: number) {
|
||||||
|
dt *= 10;
|
||||||
|
const force = calculatePathForce(this, this.path.points);
|
||||||
|
this.applyForce(force.mult(dt));
|
||||||
|
this.velocity.limit(this.maxSpeed);
|
||||||
|
this.acceleration.limit(this.maxForce);
|
||||||
|
this.velocity.add(this.acceleration.copy().mult(dt));
|
||||||
|
this.position.add(this.velocity.copy().mult(dt));
|
||||||
|
this.edges();
|
||||||
|
}
|
||||||
|
|
||||||
|
edges() {
|
||||||
|
const doodler = getContextItem<Doodler>("doodler");
|
||||||
|
|
||||||
|
if (this.position.x > doodler.width) this.position.x = 0;
|
||||||
|
if (this.position.y > doodler.height) this.position.y = 0;
|
||||||
|
if (this.position.x < 0) this.position.x = doodler.width;
|
||||||
|
if (this.position.y < 0) this.position.y = doodler.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
const doodler = getContextItem<Doodler>("doodler");
|
||||||
|
doodler.drawRotated(this.position, this.velocity.heading() || 0, () => {
|
||||||
|
doodler.fillCenteredRect(this.position, 20, 20, { fillColor: "white" });
|
||||||
|
});
|
||||||
|
for (const point of this.path.points) {
|
||||||
|
doodler.drawCircle(point, 4, { color: "red", weight: 3 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyForce(force: Vector) {
|
||||||
|
this.velocity.add(force);
|
||||||
|
}
|
||||||
|
|
||||||
|
static edges(point: Vector, width: number, height: number) {
|
||||||
|
if (point.x > width) point.x = 0;
|
||||||
|
if (point.y > height) point.y = 0;
|
||||||
|
if (point.x < 0) point.x = width;
|
||||||
|
if (point.y < 0) point.y = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closestPointOnLineSegment(p: Vector, a: Vector, b: Vector): Vector {
|
||||||
|
// Vector AB
|
||||||
|
// const AB = { x: b.x - a.x, y: b.y - a.y };
|
||||||
|
const AB = Vector.sub(b, a);
|
||||||
|
// Vector AP
|
||||||
|
// const AP = { x: p.x - a.x, y: p.y - a.y };
|
||||||
|
const AP = Vector.sub(p, a);
|
||||||
|
// Dot product of AP and AB
|
||||||
|
// const AB_AB = AB.x * AB.x + AB.y * AB.y;
|
||||||
|
const AB_AB = Vector.dot(AB, AB);
|
||||||
|
// const AP_AB = AP.x * AB.x + AP.y * AB.y;
|
||||||
|
const AP_AB = Vector.dot(AP, AB);
|
||||||
|
// Project AP onto AB
|
||||||
|
const t = AP_AB / AB_AB;
|
||||||
|
|
||||||
|
// Clamp t to the range [0, 1] to restrict to the segment
|
||||||
|
const tClamped = Math.max(0, Math.min(1, t));
|
||||||
|
|
||||||
|
// Closest point on the segment
|
||||||
|
return new Vector({ x: a.x + AB.x * tClamped, y: a.y + AB.y * tClamped });
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculatePathForce(f: DotFollower, path: Vector[]) {
|
||||||
|
let closestPoint: Vector = path[0];
|
||||||
|
let minDistance = Infinity;
|
||||||
|
|
||||||
|
// Loop through each segment to find the closest point on the path
|
||||||
|
for (let i = 0; i < path.length - 1; i++) {
|
||||||
|
const segmentStart = path[i];
|
||||||
|
const segmentEnd = path[i + 1];
|
||||||
|
|
||||||
|
// Find the closest point on the segment
|
||||||
|
const closest = closestPointOnLineSegment(
|
||||||
|
f.position,
|
||||||
|
segmentStart,
|
||||||
|
segmentEnd,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate the distance from the follower to the closest point
|
||||||
|
// const distance = Math.sqrt(
|
||||||
|
// Math.pow(follower.position.x - closest.x, 2) +
|
||||||
|
// Math.pow(follower.position.y - closest.y, 2),
|
||||||
|
// );
|
||||||
|
|
||||||
|
const distance = Vector.dist(f.position, closest);
|
||||||
|
|
||||||
|
// Track the closest point
|
||||||
|
if (distance < minDistance) {
|
||||||
|
minDistance = distance;
|
||||||
|
closestPoint = closest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the force to apply toward the closest point
|
||||||
|
// const force = {
|
||||||
|
// x: closestPoint.x - f.position.x,
|
||||||
|
// y: closestPoint.y - f.position.y,
|
||||||
|
// };
|
||||||
|
const force = Vector.sub(closestPoint, f.position);
|
||||||
|
|
||||||
|
// Normalize the force and apply a magnitude (this will depend on your desired strength)
|
||||||
|
const magnitude = 100; // Adjust this based on your needs
|
||||||
|
force.setMag(magnitude);
|
||||||
|
return force;
|
||||||
|
}
|
@ -22,12 +22,9 @@ export class Train {
|
|||||||
constructor(track: Spline<TrackSegment>, cars: TrainCar[]) {
|
constructor(track: Spline<TrackSegment>, cars: TrainCar[]) {
|
||||||
this.path = track;
|
this.path = track;
|
||||||
this.t = 0;
|
this.t = 0;
|
||||||
this.nodes.push(this.path.followEvenPoints(this.t));
|
|
||||||
this.nodes.push(this.path.followEvenPoints(this.t - this.real2Track(40)));
|
|
||||||
const resources = getContextItem<ResourceManager>("resources");
|
const resources = getContextItem<ResourceManager>("resources");
|
||||||
const engineSprites = resources.get<HTMLImageElement>("engine-sprites")!;
|
|
||||||
console.log(engineSprites);
|
|
||||||
this.cars = cars;
|
this.cars = cars;
|
||||||
|
console.log(track);
|
||||||
// this.cars.push(
|
// this.cars.push(
|
||||||
// new TrainCar(
|
// new TrainCar(
|
||||||
// 55,
|
// 55,
|
||||||
@ -44,16 +41,15 @@ export class Train {
|
|||||||
// { at: new Vector(80, 0), width: 40, height: 20 },
|
// { at: new Vector(80, 0), width: 40, height: 20 },
|
||||||
// ),
|
// ),
|
||||||
// );
|
// );
|
||||||
this.cars[0].points = this.nodes.map((n) => n) as [Vector, Vector];
|
let currentOffset = 0;
|
||||||
this.cars[1].points = this.nodes.map((n) => n) as [Vector, Vector];
|
for (const car of this.cars) {
|
||||||
let currentOffset = 40;
|
|
||||||
for (const car of cars) {
|
|
||||||
currentOffset += this.spacing;
|
currentOffset += this.spacing;
|
||||||
const a = this.path.followEvenPoints(this.t - currentOffset);
|
const a = this.path.followEvenPoints(this.t - currentOffset);
|
||||||
currentOffset += car.length;
|
currentOffset += car.length;
|
||||||
const b = this.path.followEvenPoints(this.t - currentOffset);
|
const b = this.path.followEvenPoints(this.t - currentOffset);
|
||||||
car.points = [a, b];
|
car.points = [a, b];
|
||||||
this.cars.push(car);
|
this.nodes.push(a, b);
|
||||||
|
// this.cars.push(car);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,27 +69,27 @@ export class Train {
|
|||||||
// this.draw();
|
// this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw() {
|
|
||||||
// const doodler = getContextItem<Doodler>("doodler");
|
|
||||||
// this.path.draw();
|
|
||||||
// for (const [i, node] of this.nodes.entries()) {
|
|
||||||
// // doodler.drawCircle(node, 10, { color: "purple", weight: 3 });
|
|
||||||
// doodler.fillCircle(node, 2, { color: "purple" });
|
|
||||||
// // const next = this.nodes[i + 1];
|
|
||||||
// // if (next) {
|
|
||||||
// // const to = Vector.sub(node.point, next.point);
|
|
||||||
// // to.setMag(40);
|
|
||||||
// // doodler.line(next.point, Vector.add(to, next.point))
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
for (const car of this.cars) {
|
const doodler = getContextItem<Doodler>("doodler");
|
||||||
car.draw();
|
this.path.draw();
|
||||||
|
for (const [i, node] of this.nodes.entries()) {
|
||||||
|
// doodler.drawCircle(node, 10, { color: "purple", weight: 3 });
|
||||||
|
doodler.fillCircle(node, 2, { color: "purple" });
|
||||||
|
// const next = this.nodes[i + 1];
|
||||||
|
// if (next) {
|
||||||
|
// const to = Vector.sub(node.point, next.point);
|
||||||
|
// to.setMag(40);
|
||||||
|
// doodler.line(next.point, Vector.add(to, next.point))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw() {
|
||||||
|
// for (const car of this.cars) {
|
||||||
|
// car.draw();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
real2Track(length: number) {
|
real2Track(length: number) {
|
||||||
return length / this.path.pointSpacing;
|
return length / this.path.pointSpacing;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user