257 lines
7.0 KiB
JavaScript
257 lines
7.0 KiB
JavaScript
const canvasWidth = 800;
|
|
const canvasHeight = 800;
|
|
const backgroundColor = "#333333";
|
|
let vehicle;
|
|
let target;
|
|
let demo = "wander";
|
|
|
|
function setup() {
|
|
createCanvas(canvasWidth, canvasHeight);
|
|
cursor(CROSS);
|
|
vehicle = new Vehicle(0, 0);
|
|
target = new Target(canvasWidth / 2, canvasHeight / 2);
|
|
if (demo == "wander") {
|
|
vehicle.maxVelocity = 5;
|
|
vehicle.maxSteering = 3;
|
|
vehicle.velocity = createVector(1, 1);
|
|
}
|
|
}
|
|
|
|
function draw() {
|
|
background(backgroundColor);
|
|
switch (demo) {
|
|
case "seek2":
|
|
demoSeek2();
|
|
break;
|
|
case "arrive":
|
|
demoSeek(true);
|
|
break;
|
|
case "wander":
|
|
demoWander();
|
|
break;
|
|
default:
|
|
demoSeek();
|
|
}
|
|
}
|
|
|
|
class Vehicle {
|
|
constructor(x, y) {
|
|
this.position = createVector(x, y);
|
|
this.velocity = createVector(0, 0);
|
|
this.acceleration = createVector(0, 0);
|
|
this.color = "#FFFFFF";
|
|
this.size = 32;
|
|
this.maxVelocity = 6;
|
|
this.maxSteering = 0.25;
|
|
this.path = [];
|
|
this.drawPath = true;
|
|
this.pathes = [];
|
|
}
|
|
|
|
seek(target, arrive = false) {
|
|
let steering = p5.Vector.sub(target.position, this.position);
|
|
|
|
let desiredVelocity = this.maxVelocity;
|
|
if (arrive) {
|
|
let slowdownDistance = this.size * 3;
|
|
let distance = steering.mag();
|
|
if (distance < slowdownDistance) {
|
|
desiredVelocity = map(distance, 0, 100, 0, this.maxVelocity);
|
|
}
|
|
}
|
|
|
|
steering.setMag(desiredVelocity);
|
|
steering.sub(this.velocity);
|
|
return steering;
|
|
}
|
|
|
|
flee(target) {
|
|
let steering = this.seek(target).mult(-1);
|
|
return steering;
|
|
}
|
|
|
|
wander() {
|
|
let wanderPoint = this.velocity.copy();
|
|
wanderPoint.setMag(100);
|
|
wanderPoint.add(this.position);
|
|
|
|
// stroke(255);
|
|
// let x1 = wanderPoint.x + -25 * cos(PI / 2 + this.velocity.heading());
|
|
// let y1 = wanderPoint.y + -25 * sin(PI / 2 + this.velocity.heading());
|
|
// let x2 = wanderPoint.x + 25 * cos(PI / 2 + this.velocity.heading());
|
|
// let y2 = wanderPoint.y + 25 * sin(PI / 2 + this.velocity.heading());
|
|
// line(x1, y1, x2, y2);
|
|
|
|
noStroke();
|
|
fill("#F063A4");
|
|
let offset = map(noise(Date.now() / 1000), 0, 1, -50, 50);
|
|
let x = offset * cos(PI / 2 + this.velocity.heading());
|
|
let y = offset * sin(PI / 2 + this.velocity.heading());
|
|
wanderPoint.add(x, y);
|
|
circle(wanderPoint.x, wanderPoint.y, 8);
|
|
let steering = wanderPoint.sub(this.position);
|
|
return steering;
|
|
}
|
|
|
|
update(steering) {
|
|
if (steering) {
|
|
steering.limit(this.maxSteering);
|
|
this.acceleration.add(steering);
|
|
}
|
|
this.velocity.add(this.acceleration);
|
|
this.velocity.limit(this.maxVelocity);
|
|
this.position.add(this.velocity);
|
|
this.acceleration.set(0);
|
|
}
|
|
|
|
show() {
|
|
noFill();
|
|
if (this.pathes.length >= 10) this.pathes.shift();
|
|
stroke(255);
|
|
strokeWeight(2);
|
|
[...this.pathes, this.path].forEach((path, i) => {
|
|
let c = map(i, 0, this.pathes.length, 127, 255);
|
|
let w = map(i, 0, this.pathes.length, 1, 5);
|
|
stroke(c);
|
|
strokeWeight(w);
|
|
beginShape();
|
|
path.forEach((v) => vertex(v.x, v.y));
|
|
endShape();
|
|
});
|
|
|
|
// stroke(255);
|
|
// strokeWeight(4);
|
|
// beginShape();
|
|
// this.path.forEach((v) => {
|
|
// vertex(v.x, v.y);
|
|
// });
|
|
// endShape();
|
|
|
|
push();
|
|
noStroke();
|
|
fill(this.color);
|
|
translate(this.position.x, this.position.y);
|
|
rotate(this.velocity.heading());
|
|
triangle(
|
|
this.size / 2,
|
|
0,
|
|
-this.size / 2,
|
|
-this.size / 4,
|
|
-this.size / 2,
|
|
this.size / 4
|
|
);
|
|
pop();
|
|
}
|
|
|
|
edges() {
|
|
if (this.position.x < 0) {
|
|
this.position.x = canvasWidth;
|
|
return true;
|
|
} else if (this.position.x >= canvasWidth) {
|
|
this.position.x = 0;
|
|
return true;
|
|
}
|
|
if (this.position.y < 0) {
|
|
this.position.y = canvasHeight;
|
|
return true;
|
|
} else if (this.position.y >= canvasHeight) {
|
|
this.position.y = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
class Target extends Vehicle {
|
|
constructor(x, y) {
|
|
super(x, y);
|
|
this.position = createVector(random(canvasWidth), random(canvasHeight));
|
|
this.color = "#F063A4";
|
|
this.size = 32;
|
|
this.maxVelocity = 6;
|
|
this.maxSteering = 1;
|
|
}
|
|
|
|
show() {
|
|
push();
|
|
stroke(255);
|
|
strokeWeight(1);
|
|
fill(this.color);
|
|
circle(this.position.x, this.position.y, this.size);
|
|
pop();
|
|
}
|
|
}
|
|
|
|
function demoSeek(arrive) {
|
|
target.position.x = mouseX;
|
|
target.position.y = mouseY;
|
|
let steering = vehicle.seek(target, arrive);
|
|
vehicle.update(steering);
|
|
target.show();
|
|
vehicle.show();
|
|
}
|
|
|
|
function demoWander() {
|
|
let steering = vehicle.wander();
|
|
vehicle.update(steering);
|
|
if (vehicle.edges()) {
|
|
vehicle.pathes.push(vehicle.path);
|
|
vehicle.path = [];
|
|
}
|
|
if (frameCount % 1 == 0) vehicle.path.push(vehicle.position.copy());
|
|
vehicle.show();
|
|
}
|
|
|
|
function demoSeek2() {
|
|
let distance = p5.Vector.dist(vehicle.position, target.position);
|
|
if (distance < target.size / 2) {
|
|
target.position = createVector(
|
|
random(canvasWidth),
|
|
random(canvasHeight)
|
|
);
|
|
target.acceleration = createVector(0, 0);
|
|
target.velocity = createVector(0, 0);
|
|
}
|
|
|
|
let seekSteering = vehicle.seek(target);
|
|
let fleeSteering = target.flee(vehicle);
|
|
vehicle.update(seekSteering);
|
|
target.update(fleeSteering);
|
|
target.bounce();
|
|
target.show();
|
|
vehicle.show();
|
|
}
|
|
|
|
/**
|
|
* 获取随机向量,可指定大小
|
|
* @param {number} magnitude 向量大小
|
|
* @return {p5.Vector} vector 随机向量
|
|
*/
|
|
function randomVector(magnitude) {
|
|
let vector = p5.Vector.random2D();
|
|
if (magnitude) vector.mult(magnitude);
|
|
return vector;
|
|
}
|