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; }