mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-24 13:52:45 +00:00
Add distance-based Bezier curve
This commit is contained in:
@@ -382,7 +382,7 @@ export class UnitLayer implements Layer {
|
||||
|
||||
let newTrailSize = 1;
|
||||
const trail = this.unitToTrail.get(unit);
|
||||
// The nuke can move faster than 1 pixel, draw a line for the trail or else it will be dotted
|
||||
// It can move faster than 1 pixel, draw a line for the trail or else it will be dotted
|
||||
if (trail.length >= 1) {
|
||||
const currentX = this.game.x(unit.lastTile());
|
||||
const currentY = this.game.y(unit.lastTile());
|
||||
|
||||
@@ -2,35 +2,37 @@ import { consolex } from "../Consolex";
|
||||
import { Game } from "../game/Game";
|
||||
import { GameMap, TileRef } from "../game/GameMap";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { BezierCurve } from "../utilities/Line";
|
||||
import { DistanceBasedBezierCurve } from "../utilities/Line";
|
||||
import { AStar, PathFindResultType, TileResult } from "./AStar";
|
||||
import { MiniAStar } from "./MiniAStar";
|
||||
|
||||
const parabolaMinHeight = 50;
|
||||
|
||||
export class ParabolaPathFinder {
|
||||
constructor(private mg: GameMap) {}
|
||||
private curve: BezierCurve;
|
||||
private distance: number;
|
||||
private curve: DistanceBasedBezierCurve;
|
||||
|
||||
computeControlPoints(
|
||||
orig: TileRef,
|
||||
dst: TileRef,
|
||||
distanceBasedVertex = true,
|
||||
distanceBasedHeight = true,
|
||||
) {
|
||||
const origX = this.mg.x(orig);
|
||||
const origY = this.mg.y(orig);
|
||||
const dstX = this.mg.x(dst);
|
||||
const dstY = this.mg.y(dst);
|
||||
|
||||
this.curve = new BezierCurve(origX, origY, dstX, dstY);
|
||||
this.curve = new DistanceBasedBezierCurve(origX, origY, dstX, dstY);
|
||||
const dx = dstX - origX;
|
||||
const dy = dstY - origY;
|
||||
this.distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const maxHeight = distanceBasedHeight
|
||||
? Math.max(distance / 3, parabolaMinHeight)
|
||||
: 0;
|
||||
// Use a bezier curve always pointing up
|
||||
const x0 = origX + (dstX - origX) / 4;
|
||||
const maxVertex = distanceBasedVertex ? Math.max(this.distance / 3, 50) : 0;
|
||||
const y0 = Math.max(origY + (dstY - origY) / 4 - maxVertex, 0);
|
||||
const y0 = Math.max(origY + (dstY - origY) / 4 - maxHeight, 0);
|
||||
const x1 = origX + ((dstX - origX) * 3) / 4;
|
||||
const y1 = Math.max(origY + ((dstY - origY) * 3) / 4 - maxVertex, 0);
|
||||
const y1 = Math.max(origY + ((dstY - origY) * 3) / 4 - maxHeight, 0);
|
||||
|
||||
this.curve.setControlPoint0(x0, y0);
|
||||
this.curve.setControlPoint1(x1, y1);
|
||||
@@ -40,8 +42,7 @@ export class ParabolaPathFinder {
|
||||
if (!this.curve) {
|
||||
return;
|
||||
}
|
||||
const incr = speed / (this.distance * 2);
|
||||
const nextPoint = this.curve.increment(incr);
|
||||
const nextPoint = this.curve.increment(speed);
|
||||
if (!nextPoint) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+91
-22
@@ -54,12 +54,8 @@ export class BezierCurve {
|
||||
this.controlPoint0Y = y0;
|
||||
this.controlPoint1X = x1;
|
||||
this.controlPoint1Y = y1;
|
||||
const dx = this.x1 - this.x0;
|
||||
const dy = this.y1 - this.y0;
|
||||
const dist = Math.abs(this.x1 - this.x0);
|
||||
}
|
||||
|
||||
private t: number = 0;
|
||||
private controlPoint0X: number;
|
||||
private controlPoint0Y: number;
|
||||
private controlPoint1X: number;
|
||||
@@ -75,23 +71,96 @@ export class BezierCurve {
|
||||
this.controlPoint1Y = y;
|
||||
}
|
||||
|
||||
increment(incr: number): { x: number; y: number } {
|
||||
// Calculate the next point on the Bézier curve
|
||||
// const incr = speed / (this.distance * 2);
|
||||
this.t = this.t + incr;
|
||||
if (this.t >= 1) {
|
||||
return null; // end reached
|
||||
}
|
||||
const nextX =
|
||||
Math.pow(1 - this.t, 3) * this.x0 +
|
||||
3 * Math.pow(1 - this.t, 2) * this.t * this.controlPoint0X +
|
||||
3 * (1 - this.t) * Math.pow(this.t, 2) * this.controlPoint1X +
|
||||
Math.pow(this.t, 3) * this.x1;
|
||||
const nextY =
|
||||
Math.pow(1 - this.t, 3) * this.y0 +
|
||||
3 * Math.pow(1 - this.t, 2) * this.t * this.controlPoint0Y +
|
||||
3 * (1 - this.t) * Math.pow(this.t, 2) * this.controlPoint1Y +
|
||||
Math.pow(this.t, 3) * this.y1;
|
||||
return { x: nextX, y: nextY };
|
||||
getPointAt(t: number): { x: number; y: number } {
|
||||
const x =
|
||||
Math.pow(1 - t, 3) * this.x0 +
|
||||
3 * Math.pow(1 - t, 2) * t * this.controlPoint0X +
|
||||
3 * (1 - t) * Math.pow(t, 2) * this.controlPoint1X +
|
||||
Math.pow(t, 3) * this.x1;
|
||||
const y =
|
||||
Math.pow(1 - t, 3) * this.y0 +
|
||||
3 * Math.pow(1 - t, 2) * t * this.controlPoint0Y +
|
||||
3 * (1 - t) * Math.pow(t, 2) * this.controlPoint1Y +
|
||||
Math.pow(t, 3) * this.y1;
|
||||
return { x, y };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a cumulative distance LUT to approximate the traveled distance
|
||||
*/
|
||||
export class DistanceBasedBezierCurve extends BezierCurve {
|
||||
private totalDistance: number = 0;
|
||||
private cumulativeDistanceLUT: Array<{ t: number; distance: number }> = [];
|
||||
private lastFoundIndex: number = 0; // To keep track of the last found index
|
||||
|
||||
increment(distance: number): { x: number; y: number } {
|
||||
this.totalDistance += distance;
|
||||
const targetDistance = Math.min(
|
||||
this.totalDistance,
|
||||
this.cumulativeDistanceLUT[this.cumulativeDistanceLUT.length - 1]
|
||||
?.distance || 0,
|
||||
);
|
||||
const t = this.computeTForDistance(targetDistance);
|
||||
if (t >= 1) {
|
||||
return null; // end reached
|
||||
}
|
||||
return this.getPointAt(t);
|
||||
}
|
||||
|
||||
generateCumulativeDistanceLUT(numSteps: number = 500): void {
|
||||
this.cumulativeDistanceLUT = [];
|
||||
let cumulativeDistance = 0;
|
||||
let prevPoint = this.getPointAt(0);
|
||||
|
||||
for (let i = 1; i <= numSteps; i++) {
|
||||
const t = i / numSteps;
|
||||
const currentPoint = this.getPointAt(t);
|
||||
|
||||
const dx = currentPoint.x - prevPoint.x;
|
||||
const dy = currentPoint.y - prevPoint.y;
|
||||
const segmentLength = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
cumulativeDistance += segmentLength;
|
||||
this.cumulativeDistanceLUT.push({ t, distance: cumulativeDistance });
|
||||
prevPoint = currentPoint;
|
||||
}
|
||||
}
|
||||
|
||||
computeTForDistance(distance: number): number {
|
||||
if (this.cumulativeDistanceLUT.length === 0) {
|
||||
this.generateCumulativeDistanceLUT();
|
||||
}
|
||||
if (distance <= 0) return 0;
|
||||
if (
|
||||
distance >=
|
||||
this.cumulativeDistanceLUT[this.cumulativeDistanceLUT.length - 1].distance
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let lowerIndex = this.lastFoundIndex;
|
||||
let upperIndex = this.cumulativeDistanceLUT.length - 1;
|
||||
// Binary search for the closest range
|
||||
while (upperIndex - lowerIndex > 1) {
|
||||
const midIndex = Math.floor((upperIndex + lowerIndex) / 2);
|
||||
if (this.cumulativeDistanceLUT[midIndex].distance < distance) {
|
||||
lowerIndex = midIndex;
|
||||
} else {
|
||||
upperIndex = midIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolate between these two points
|
||||
const lower = this.cumulativeDistanceLUT[lowerIndex];
|
||||
const upper = this.cumulativeDistanceLUT[upperIndex];
|
||||
this.lastFoundIndex = lowerIndex;
|
||||
|
||||
// Linear interpolation of t based on the distance
|
||||
const t =
|
||||
lower.t +
|
||||
((distance - lower.distance) * (upper.t - lower.t)) /
|
||||
(upper.distance - lower.distance);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user