class Tracker { List<Vec3D> objectTrails; Vec3D speed; Vec3D acc = new Vec3D(0, 0, 0); Vec3D loc; String type, babyType; float maxforce = 0.01; float max = 4.0f; float vel = 1.0; float amp,r; float maxDist = 100; boolean sepActive = false; boolean wonderTrigger = false; boolean instanceable; int maxChildren,instanceTriggerCount; float wandertheta; float wanderTVal; //value to swap rotation after x position changes float wanderD; // Distance for our "wander circle" float wanderR; //Radius for our "wander circle" float wanderT; // Randomly change wander theta //------------------------------------------------------------------------------------- Tracker(Vec3D location, Vec3D Speed, boolean instance, String Type, String BabyType) { this.loc = location; this.objectTrails = new ArrayList<Vec3D>(); this.r = 12; this.instanceTriggerCount = 0; this.instanceable = instance; this.type = Type; this.wandertheta = 0; this.babyType = BabyType; this.speed = Speed; } //---------------------------------------------------------------------- //Run------------------------------------------------------------------- //Run all the other methods for this dude------------------------------- //---------------------------------------------------------------------- void run() { pathFollow(); if (this.sepActive && this.instanceable) { separate(); } if(this.wonderTrigger){ wander(); } /* if (this.wonderTrigger) { wander(); } */ if (simulate) { move(); } viz(); trail(); } //-------------------------------------------------------------------------- //Wanderer Behavior--------------------------------------------------------- //“Wandering is a type of random steering which has some long term order: - //the steering direction on one frame is related to the steering direction //on the next frame. This produces more interesting motion than, for example //,simply generating a random steering direction each frame.” Reynolds----- //-------------------------------------------------------------------------- void wander() { if (stepCount < this.wanderTVal) { if (type == "parent") { this.wandertheta = this.wanderT; } else { if (babyType == "w_a") { this.wandertheta = this.wanderT; } else { this.wandertheta = -this.wanderT; } } } else if (stepCount >= this.wanderTVal && stepCount <this.wanderTVal*2) { if (type == "parent") { this.wandertheta = -this.wanderT; } else { if (babyType == "w_a") { this.wandertheta = -this.wanderT; } else { this.wandertheta = this.wanderT; } } } else { stepCount = 0; } // Now we have to calculate the new location to steer towards on the wander circle Vec3D circleloc = speed.copy(); // Start with velocity circleloc.normalize(); // Normalize to get heading circleloc.scaleSelf(this.wanderD); // Multiply by distance circleloc.addSelf(this.loc); // Make it relative to boid's location float h = speed.headingXY(); // We need to know the heading to offset wandertheta Vec3D circleOffSet = new Vec3D(this.wanderR*cos(this.wandertheta+h), this.wanderR*sin(this.wandertheta+h), 0); Vec3D target = circleloc.add(circleOffSet); seek(target); if (diagram) drawWanderStuff(loc, circleloc, target, wanderR); // Render wandering circle, etc. } //------------------------------------------------------------------------------------------- //Diagram------------------------------------------------------------------------------------ //A method just to draw the circle associated with wandering--------------------------------- //------------------------------------------------------------------------------------------- void drawWanderStuff(Vec3D location, Vec3D circle, Vec3D target, float rad) { stroke(255); noFill(); ellipseMode(CENTER); ellipse(circle.x, circle.y, rad*2, rad*2); ellipse(target.x, target.y, 4, 4); line(location.x, location.y, circle.x, circle.y); line(circle.x, circle.y, target.x, target.y); } //------------------------------------------------------------------------------------------- //Path Follow Method------------------------------------------------------------------------- //Follows the path if inside the radius------------------------------------------------------ //------------------------------------------------------------------------------------------- void pathFollow() { Vec3D predict = this.speed.copy(); predict.normalize(); predict.scaleSelf(this.amp); Vec3D nextPosPrev = loc.add(predict); Vec3D target = null; Vec3D normal = null; float worldRecord = 1000000; for (int z = 0; z < pathList.size(); z++) { for (int i = 0; i < pathList.get(z).points.size()-1; i++) { Vec3D a = pathList.get(z).points.get(i); Vec3D b = pathList.get(z).points.get(i+1); Vec3D normalPoint = getNormalPoint(nextPosPrev, a, b); //Finding the normals for each line segment if (normalPoint.x < min(a.x, b.x) || normalPoint.x > max(a.x, b.x)) { normalPoint = b.copy(); } float distance = nextPosPrev.distanceTo(normalPoint); if (distance < worldRecord) { worldRecord = distance; normal = normalPoint; Vec3D dir = b.sub(a); //Look at the direction of the line segment so we can seek a little bit ahead of the normal dir.normalize(); dir.scaleSelf(10); target = normalPoint.copy(); target.add(dir); } } } //remove if you want to just go to the line //maxDist = 10000000000000f; if (worldRecord < this.maxDist) { this.sepActive = true; this.wonderTrigger = true; if (worldRecord > tempPath.radius) { seek(target); } else { //IF TYPE IS PARENT AND HAS CREATED LESS THAN THE ALLOWED CHILDREN THEN CREATE MORE if (this.instanceable && instanceTriggerCount < maxChildren) { addNew = true; triggerLoc = nextPosPrev.copy(); childSpawners.add(new Vec3D(this.loc)); childSpawnType.add(instanceTriggerCount); triggerCount++; instanceTriggerCount ++; } else { addNew = false; } Vec3D zero = new Vec3D(0, 0, 0); zero.scaleSelf(3); acc.addSelf(zero); } } else { wonderTrigger = false; } // Draw the debugging stuff if (drawFutureLoc) { // Draw predicted future location stroke(255); fill(0); line(this.loc.x, this.loc.y, this.loc.x, this.loc.y); ellipse(nextPosPrev.x, nextPosPrev.y, 4, 4); // Draw normal location stroke(255); fill(0); ellipse(normal.x, normal.y, 4, 4); // Draw actual target (red if steering towards it) line(nextPosPrev.x, nextPosPrev.y, normal.x, normal.y); if (worldRecord > tempPath.radius) fill(255, 0, 0); noStroke(); ellipse(target.x, target.y, 8, 8); } } //------------------------------------------------------------------------------------------- //-------------------Get the Normal Point on the Path Method--------------------------------- //------------------------------------------------------------------------------------------- Vec3D getNormalPoint(Vec3D p, Vec3D a, Vec3D b) { Vec3D ap = p.sub(a); Vec3D ab = b.sub(a); ab.normalize(); ab.scaleSelf(ap.dot(ab)); //Using the dot product for scalar projection Vec3D normalPoint = a.add(ab); // Finding the normal point along the line segment return normalPoint; } //------------------------------------------------------------------------------------------- //---------------------------------------Seek Target Method---------------------------------- //------------------------------------------------------------------------------------------- void seek(Vec3D target) { Vec3D desired = target.sub(this.loc); desired.normalize(); desired.scaleSelf(this.max); Vec3D steer = desired.sub(this.speed); steer.limit(this.maxforce); // Limit the magnitude of the steering force. steer.scaleSelf(3); applyForce(steer); } //------------------------------------------------------------------------------------------- //Apply Force Method------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- void applyForce(Vec3D force) { this.acc.addSelf(force); } //---------------------------------------------------------------------- //Move------------------------------------------------------------------ //Move and update the position------------------------------------------ //---------------------------------------------------------------------- void move() { this.speed.addSelf(this.acc); this.speed.limit(this.max); this.loc.addSelf(this.speed); this.objectTrails.add(new Vec3D(this.loc)); this.acc.clear(); } //------------------------------------------------------------------------------------------- //Viz Method--------------------------------------------------------------------------------- //Draw heads--------------------------------------------------------------------------------- void viz() { if (type == "parent") { stroke(255, 0, 0); strokeWeight(6); point(this.loc.x, this.loc.y); } else { if (babyType == "w_a") { stroke(0, 0, 255); strokeWeight(6); point(this.loc.x, this.loc.y); } else { stroke(0, 255, 255); strokeWeight(6); point(this.loc.x, this.loc.y); } } } //------------------------------------------------------------------------------------------- //Separation Method-------------------------------------------------------------------------- //Method checks for nearby boids and steers away--------------------------------------------- //------------------------------------------------------------------------------------------- void separate () { float desiredseparation = this.r*2; Vec3D steer = new Vec3D(0, 0, 0); int count = 0; // For every boid in the system, check if it's too close for (int i = 0; i < agentList.size(); i++) { Tracker other = (Tracker) agentList.get(i); float d = this.loc.distanceTo(other.loc); // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) if ((d > 0) && (d < desiredseparation)) { // Calculate vector pointing away from neighbor Vec3D diff = loc.sub(other.loc); diff.normalize(); diff.normalizeTo(1/d); // Weight by distance steer.addSelf(diff); count++; // Keep track of how many } } // Average -- divide by how many if (count > 0) { steer.scaleSelf(1.0/(float)count); } // As long as the vector is greater than 0 if (steer.magnitude() > 0) { // Implement Reynolds: Steering = Desired - Velocity steer.normalize(); steer.scaleSelf(this.max); steer.subSelf(this.speed); steer.limit(this.maxforce); } steer.scaleSelf(3); applyForce(steer); } //------------------------------------------------------------------------------------------- //Trail Method------------------------------------------------------------------------------- //The trail is drawn from previous loc to new location--------------------------------------- //------------------------------------------------------------------------------------------- void trail() { if (this.objectTrails.size() > 0) { for (int j = 0; j < this.objectTrails.size(); j++) { if (j != 0) { Vec3D pos = this.objectTrails.get(j); Vec3D prevpos = this.objectTrails.get(j - 1); if(this.type == "parent"){ stroke(255, 0, 0, map(j, 0, this.objectTrails.size(), 0, 200)); strokeWeight(map(j, 0, this.objectTrails.size(), 0.45, 1.5)); }else if(this.babyType == "w_a"){ stroke(0, 0, 255, map(j, 0, this.objectTrails.size(), 0, 200)); strokeWeight(map(j, 0, this.objectTrails.size(), 0.45, 1.0)); }else{ stroke(0,255, 255, map(j, 0, this.objectTrails.size(), 0, 200)); strokeWeight(map(j, 0, this.objectTrails.size(), 0.45, 1.0)); } line(pos.x, pos.y, prevpos.x, prevpos.y); } } } } } Update