let vehicle1; let vehicle2; function setup() { cursor(CROSS); createCanvas(600, 600); vehicle1 = new Vehicle(300, 300, "#CCCCCC"); vehicle2 = new Vehicle(300, 300, "#CC3333"); vehicle2.velocity = randomVector(5); } function draw() { // 追逐鼠标(); // 逃避鼠标(); // 巡游小车(); 小车追逐(); } function 追逐鼠标() { background(64); vehicle1.seek({ position: createVector(mouseX, mouseY) }); vehicle1.move(); vehicle1.turn(); vehicle1.drawPath(); vehicle1.show(); } function 逃避鼠标() { background(64); vehicle1.flee({ position: createVector(mouseX, mouseY) }); vehicle1.move(); vehicle1.turn(); vehicle1.drawPath(); vehicle1.show(); } function 巡游小车() { background(64); vehicle1.wander(); vehicle1.move(); vehicle1.turn(); vehicle1.drawPath(); vehicle1.show(); } function 小车追逐() { background(64); vehicle1.seek(vehicle2, 20); vehicle2.move(); vehicle1.move(); if (vehicle2.turn()) vehicle2.velocity = randomVector(random(2, 5)); vehicle1.turn(); vehicle1.drawPath(); vehicle2.show(); vehicle1.show(); } /** * 返回可指定大小的随机二维向量 * @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.color = color; this.position = createVector(x, y); this.path = []; this.pathes = []; this.velocity = createVector(0, 0); this.heading = this.velocity.heading(); this.acceleration = createVector(0, 0); this.maxVelocity = 5; this.maxAcceleration = 1; } seek(target, slowdownDistance = 100) { const desired = p5.Vector.sub(target.position, 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.position); 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); } 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()); } 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; if (edgeCrossed) { this.pathes.push(this.path); this.path = []; } this.position.x = x; this.position.y = y; 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() { push(); noFill(); stroke(50); strokeWeight(1); // if (this.pathes.length >= 10) this.pathes.shift(); [...this.pathes, this.path].forEach((path) => { beginShape(); path.forEach((v) => vertex(v.x, v.y)); endShape(); }); pop(); } show() { push(); translate(this.position.x, this.position.y); rotate(this.heading); stroke(this.color); fill(this.color); triangle(0, 0, -10, 2.5, -10, -2.5); pop(); } }