216 lines
7.1 KiB
JavaScript
216 lines
7.1 KiB
JavaScript
/**
|
|
* 返回可指定大小的随机二维向量
|
|
* @param {number} magnitude 向量大小
|
|
* @return {p5.Vector} vector 随机二维向量
|
|
*/
|
|
function randomVector(magnitude) {
|
|
let vector = p5.Vector.random2D();
|
|
return magnitude ? vector.mult(magnitude) : vector;
|
|
}
|
|
|
|
class Vehicle {
|
|
constructor(x, y, color) {
|
|
this.path = [];
|
|
this.color = color;
|
|
this.position = createVector(x, y);
|
|
this.velocity = createVector(0, 0);
|
|
this.heading = this.velocity.heading();
|
|
this.acceleration = createVector(0, 0);
|
|
this.maxVelocity = 6;
|
|
this.maxAcceleration = 0.25;
|
|
}
|
|
|
|
seek(target, slowdownDistance = 100) {
|
|
const desired = p5.Vector.sub(target, this.position);
|
|
const distance = desired.mag();
|
|
const magnitude =
|
|
slowdownDistance > 0 && distance <= slowdownDistance
|
|
? map(distance, 0, slowdownDistance, 0, this.maxVelocity)
|
|
: this.maxVelocity;
|
|
desired.setMag(magnitude);
|
|
const steering = p5.Vector.sub(desired, this.velocity);
|
|
this.acceleration.add(steering);
|
|
}
|
|
|
|
flee(target, fleeDistance = 100) {
|
|
const desired = p5.Vector.sub(this.position, target);
|
|
const distance = desired.mag();
|
|
const magnitude =
|
|
!fleeDistance || fleeDistance <= 0
|
|
? this.maxVelocity
|
|
: distance <= fleeDistance
|
|
? map(distance, 0, fleeDistance, this.maxVelocity, 0)
|
|
: 0;
|
|
desired.setMag(magnitude);
|
|
const steering = p5.Vector.sub(desired, this.velocity);
|
|
this.acceleration.add(steering);
|
|
}
|
|
|
|
wander() {
|
|
const target = this.velocity.copy();
|
|
target.setMag(50).add(this.position);
|
|
|
|
// stroke(128);
|
|
// const x1 = target.x + -25 * cos(PI / 2 + this.heading);
|
|
// const y1 = target.y + -25 * sin(PI / 2 + this.heading);
|
|
// const x2 = target.x + 25 * cos(PI / 2 + this.heading);
|
|
// const y2 = target.y + 25 * sin(PI / 2 + this.heading);
|
|
// line(x1, y1, x2, y2);
|
|
|
|
const offset = map(noise(Date.now() / 1000), 0, 1, -50, 50);
|
|
const x = offset * cos(PI / 2 + this.heading);
|
|
const y = offset * sin(PI / 2 + this.heading);
|
|
target.add(x, y);
|
|
|
|
noStroke();
|
|
fill("#F063A4");
|
|
circle(target.x, target.y, 5);
|
|
|
|
const steering = target.sub(this.position);
|
|
this.acceleration.add(steering);
|
|
}
|
|
|
|
follow(path, ahead = 50) {
|
|
// find future point of vehicle
|
|
const future = p5.Vector.add(
|
|
this.position,
|
|
this.velocity.copy().mult(ahead)
|
|
);
|
|
|
|
// noStroke();
|
|
// fill("#FF0000");
|
|
// circle(future.x, future.y, 5);
|
|
|
|
// find project point on the path
|
|
const v1 = p5.Vector.sub(future, path.start);
|
|
const v2 = p5.Vector.sub(path.end, path.start);
|
|
const project = path.start.copy().add(vectorProjection(v1, v2));
|
|
|
|
noStroke();
|
|
fill("#00FF00");
|
|
circle(project.x, project.y, 5);
|
|
|
|
// find the distance between project point and future point
|
|
const distance = future.dist(project);
|
|
// if the distance larger than path width then seek the project point
|
|
if (distance > path.width) this.seek(project, -1);
|
|
}
|
|
|
|
move() {
|
|
this.acceleration.limit(this.maxAcceleration);
|
|
this.velocity.add(this.acceleration);
|
|
if (this.velocity.mag() > 0) this.heading = this.velocity.heading();
|
|
this.velocity.limit(this.maxVelocity);
|
|
this.position.add(this.velocity);
|
|
this.acceleration.set(0, 0);
|
|
this.path.push(this.position.copy());
|
|
|
|
// this.acceleration.limit(this.maxAcceleration);
|
|
// // 计算时间差
|
|
// const duration = deltaTime / 1000;
|
|
// // 计算速度及位移
|
|
// const deltaVelocity = this.acceleration.mult(duration);
|
|
// const averageVelocity = deltaVelocity.copy().div(2).add(this.velocity);
|
|
// const movement = averageVelocity.mult(duration);
|
|
// // 更新速度及位置
|
|
// this.velocity.add(deltaVelocity);
|
|
// this.velocity.limit(this.maxVelocity);
|
|
// if (this.velocity.mag() > 0) this.heading = this.velocity.heading();
|
|
// this.position.add(movement);
|
|
// this.acceleration.set(0, 0);
|
|
// this.path.push(this.position.copy());
|
|
}
|
|
|
|
turn() {
|
|
let x = this.position.x;
|
|
let y = this.position.y;
|
|
while (x < 0 || x >= width) x = (x + width) % width;
|
|
while (y < 0 || y >= height) y = (y + height) % height;
|
|
const edgeCrossed = x != this.position.x || y != this.position.y;
|
|
this.position.x = x;
|
|
this.position.y = y;
|
|
if (edgeCrossed) {
|
|
this.path.pop();
|
|
this.path.push(null);
|
|
}
|
|
return edgeCrossed;
|
|
}
|
|
|
|
// bounce() {
|
|
// if (this.position.x <= this.size / 2) {
|
|
// this.position.x = this.size / 2;
|
|
// this.velocity.x *= -1;
|
|
// return true;
|
|
// } else if (this.position.x > canvasWidth - this.size / 2) {
|
|
// this.position.x = canvasWidth - this.size / 2;
|
|
// this.velocity.x *= -1;
|
|
// return true;
|
|
// }
|
|
// if (this.position.y <= this.size / 2) {
|
|
// this.position.y = this.size / 2;
|
|
// this.velocity.y *= -1;
|
|
// return true;
|
|
// } else if (this.position.y >= canvasHeight - this.size / 2) {
|
|
// this.position.y = canvasHeight - this.size / 2;
|
|
// this.velocity.y *= -1;
|
|
// return true;
|
|
// }
|
|
// return false;
|
|
// }
|
|
|
|
drawPath(length = 1024) {
|
|
push();
|
|
noFill();
|
|
stroke(255, 64);
|
|
strokeWeight(1);
|
|
if (this.path.length >= length) this.path.shift();
|
|
beginShape();
|
|
this.path.forEach((v) => {
|
|
if (!v) {
|
|
endShape();
|
|
beginShape();
|
|
} else {
|
|
vertex(v.x, v.y);
|
|
}
|
|
});
|
|
endShape();
|
|
pop();
|
|
}
|
|
|
|
show(mode = "triangle") {
|
|
push();
|
|
translate(this.position.x, this.position.y);
|
|
rotate(this.heading);
|
|
stroke(this.color);
|
|
fill(this.color);
|
|
if (mode == "point") circle(0, 0, 5);
|
|
else triangle(0, 0, -10, 2.5, -10, -2.5);
|
|
pop();
|
|
}
|
|
}
|
|
|
|
class Path {
|
|
constructor(x1, y1, x2, y2, width = 20) {
|
|
this.start = createVector(x1, y1);
|
|
this.end = createVector(x2, y2);
|
|
this.width = width;
|
|
}
|
|
|
|
show() {
|
|
push();
|
|
stroke(255, 64);
|
|
strokeWeight(this.width * 2);
|
|
line(this.start.x, this.start.y, this.end.x, this.end.y);
|
|
stroke(255);
|
|
strokeWeight(1);
|
|
line(this.start.x, this.start.y, this.end.x, this.end.y);
|
|
pop();
|
|
}
|
|
}
|
|
|
|
function vectorProjection(v1, v2) {
|
|
const v2n = v2.copy().normalize();
|
|
const sp = v1.dot(v2n);
|
|
return v2n.setMag(sp);
|
|
}
|