Provided framework, no changes
This commit is contained in:
parent
8b1db96d91
commit
580b49ae55
|
@ -0,0 +1,97 @@
|
||||||
|
/// In this file, you will have to implement seek and waypoint-following
|
||||||
|
/// The relevant locations are marked with "TODO"
|
||||||
|
|
||||||
|
class Crumb
|
||||||
|
{
|
||||||
|
PVector position;
|
||||||
|
Crumb(PVector position)
|
||||||
|
{
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
fill(255);
|
||||||
|
noStroke();
|
||||||
|
circle(this.position.x, this.position.y, CRUMB_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Boid
|
||||||
|
{
|
||||||
|
Crumb[] crumbs = {};
|
||||||
|
int last_crumb;
|
||||||
|
float acceleration;
|
||||||
|
float rotational_acceleration;
|
||||||
|
KinematicMovement kinematic;
|
||||||
|
PVector target;
|
||||||
|
|
||||||
|
Boid(PVector position, float heading, float max_speed, float max_rotational_speed, float acceleration, float rotational_acceleration)
|
||||||
|
{
|
||||||
|
this.kinematic = new KinematicMovement(position, heading, max_speed, max_rotational_speed);
|
||||||
|
this.last_crumb = millis();
|
||||||
|
this.acceleration = acceleration;
|
||||||
|
this.rotational_acceleration = rotational_acceleration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(float dt)
|
||||||
|
{
|
||||||
|
if (target != null)
|
||||||
|
{
|
||||||
|
// TODO: Implement seek here
|
||||||
|
}
|
||||||
|
|
||||||
|
// place crumbs, do not change
|
||||||
|
if (LEAVE_CRUMBS && (millis() - this.last_crumb > CRUMB_INTERVAL))
|
||||||
|
{
|
||||||
|
this.last_crumb = millis();
|
||||||
|
this.crumbs = (Crumb[])append(this.crumbs, new Crumb(this.kinematic.position));
|
||||||
|
if (this.crumbs.length > MAX_CRUMBS)
|
||||||
|
this.crumbs = (Crumb[])subset(this.crumbs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not change
|
||||||
|
this.kinematic.update(dt);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
for (Crumb c : this.crumbs)
|
||||||
|
{
|
||||||
|
c.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fill(255);
|
||||||
|
noStroke();
|
||||||
|
float x = kinematic.position.x;
|
||||||
|
float y = kinematic.position.y;
|
||||||
|
float r = kinematic.heading;
|
||||||
|
circle(x, y, BOID_SIZE);
|
||||||
|
// front
|
||||||
|
float xp = x + BOID_SIZE*cos(r);
|
||||||
|
float yp = y + BOID_SIZE*sin(r);
|
||||||
|
|
||||||
|
// left
|
||||||
|
float x1p = x - (BOID_SIZE/2)*sin(r);
|
||||||
|
float y1p = y + (BOID_SIZE/2)*cos(r);
|
||||||
|
|
||||||
|
// right
|
||||||
|
float x2p = x + (BOID_SIZE/2)*sin(r);
|
||||||
|
float y2p = y - (BOID_SIZE/2)*cos(r);
|
||||||
|
triangle(xp, yp, x1p, y1p, x2p, y2p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void seek(PVector target)
|
||||||
|
{
|
||||||
|
this.target = target;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void follow(ArrayList<PVector> waypoints)
|
||||||
|
{
|
||||||
|
// TODO: change to follow *all* waypoints
|
||||||
|
this.target = waypoints.get(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
// These are custom maps (for part b), defined as lists of coordinates for the outline
|
||||||
|
// You can add additional maps as you see fit
|
||||||
|
PVector[] customMap(int nr)
|
||||||
|
{
|
||||||
|
if (nr == 1)
|
||||||
|
{
|
||||||
|
return new PVector[] {new PVector(0, 1), new PVector(0.8, 1), new PVector(0.55, 0.7), new PVector(0.75, 0.1),
|
||||||
|
new PVector(1, 0.9), new PVector(1, 0), new PVector(0.25, 0), new PVector(0.25, 0.15), new PVector(0, 0.15)};
|
||||||
|
}
|
||||||
|
else if (nr == 2)
|
||||||
|
{
|
||||||
|
return new PVector[] {new PVector(0, 1), new PVector(0.25, 1), new PVector(0.4, 0.75), new PVector(0.45, 0.5),
|
||||||
|
new PVector(0.1, 0.5), new PVector(0.12, 0.35), new PVector(0.25, 0.35), new PVector(0.5, 0.4),
|
||||||
|
new PVector(0.5, 0.65), new PVector(0.6, 0.65), new PVector(0.6, 0.8), new PVector(0.5, 0.82),
|
||||||
|
new PVector(0.3, 1), new PVector(1,1), new PVector(1,0), new PVector(0.57, 0), new PVector(0.6, 0.35),
|
||||||
|
new PVector(0.7, 0.35), new PVector(0.67, 0.1), new PVector(0.85, 0.1), new PVector(0.82,0.15),
|
||||||
|
new PVector(0.75, 0.2), new PVector(0.75, 0.75), new PVector(0.85, 0.92), new PVector(0.62, 0.9),
|
||||||
|
new PVector(0.7, 0.8), new PVector(0.7, 0.5), new PVector(0.58, 0.45), new PVector(0.45,0),
|
||||||
|
new PVector(0.25, 0), new PVector(0.25, 0.15), new PVector(0, 0.15)};
|
||||||
|
}
|
||||||
|
else if (nr == 3)
|
||||||
|
{
|
||||||
|
return new PVector[] {new PVector(0, 1), new PVector(0.45, 1), new PVector(0.45, 0.6), new PVector(0.1, 0.6),
|
||||||
|
new PVector(0.2, 0.23), new PVector(0.35, 0.25), new PVector(0.35, 0.35), new PVector(0.25, 0.35),
|
||||||
|
new PVector(0.25, 0.3), new PVector(0.2, 0.3), new PVector(0.2, 0.55), new PVector(0.3, 0.55),
|
||||||
|
new PVector(0.3, 0.46), new PVector(0.4, 0.45), new PVector(0.4, 0.55), new PVector(0.6, 0.62),
|
||||||
|
new PVector(0.6, 0.85), new PVector(0.5, 0.85), new PVector(0.5, 1), new PVector(1,1),
|
||||||
|
new PVector(1,0.9), new PVector(0.7, 0.9), new PVector(0.7, 0.62),
|
||||||
|
new PVector(0.8, 0.75), new PVector(0.8, 0.85), new PVector(1, 0.85),
|
||||||
|
new PVector(1,0), new PVector(0.9, 0), new PVector(0.93, 0.13), new PVector(0.87, 0.1),
|
||||||
|
new PVector(0.85, 0.05), new PVector(0.75, 0.05), new PVector(0.75, 0.15), new PVector(0.8, 0.15),
|
||||||
|
new PVector(0.8, 0.25), new PVector(0.95,0.25), new PVector(0.92, 0.55), new PVector(0.75, 0.55),
|
||||||
|
new PVector(0.75, 0.45), new PVector(0.85, 0.40), new PVector(0.8, 0.5), new PVector(0.9, 0.5),
|
||||||
|
new PVector(0.9, 0.35), new PVector(0.67, 0.35), new PVector(0.67, 0.2), new PVector(0.5, 0.2),
|
||||||
|
new PVector(0.5, 0.35), new PVector(0.65, 0.45), new PVector(0.45, 0.47),
|
||||||
|
new PVector(0.45, 0.1), new PVector(0.7, 0.15), new PVector(0.7, 0), new PVector(0.25, 0),
|
||||||
|
new PVector(0.25, 0.15), new PVector(0, 0.15)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (nr == 4)
|
||||||
|
{
|
||||||
|
return new PVector[] {new PVector(0, 1), new PVector(0.85, 1), new PVector(0.87, 0.15), new PVector(0.45, 0.15), new PVector(0.35, 0.3),
|
||||||
|
new PVector(0.15, 0.3), new PVector(0.15, 0.55), new PVector(0.5, 0.55), new PVector(0.57, 0.42), new PVector(0.45, 0.75), new PVector(0.6, 0.8),
|
||||||
|
new PVector(0.6, 0.6), new PVector(0.65, 0.35), new PVector(0.55, 0.35), new PVector(0.47, 0.45), new PVector(0.3, 0.45),
|
||||||
|
new PVector(0.32, 0.37), new PVector(0.45, 0.4), new PVector(0.5, 0.27), new PVector(0.7, 0.3), new PVector(0.7, 0.85),
|
||||||
|
new PVector(0.4, 0.8), new PVector(0.4, 0.65), new PVector(0.13, 0.65), new PVector(0.1, 0.27), new PVector(0.35, 0.2),
|
||||||
|
new PVector(0.45, 0.12), new PVector(0.9, 0.1), new PVector(0.9, 1), new PVector(1,1), new PVector(1,0), new PVector(0.25, 0),
|
||||||
|
new PVector(0.25, 0.15), new PVector(0, 0.15)
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
else /// you can use nr==5, nr==6, ... nr==9 to add your own custom maps
|
||||||
|
{
|
||||||
|
return new PVector[] {new PVector(0, 1), new PVector(0.8, 1), new PVector(0.55, 0.7), new PVector(0.75, 0.1),
|
||||||
|
new PVector(1, 0.9), new PVector(1, 0), new PVector(0.25, 0), new PVector(0.25, 0.15), new PVector(0, 0.15)};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
/// called when "f" is pressed; should instantiate additional boids and start flocking
|
||||||
|
void flock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// called when "f" is pressed again; should remove the flock
|
||||||
|
void unflock()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/// Do not change this file!
|
||||||
|
/// The only methods you can call are increaseSpeed, getPosition, getHeading, getSpeed and getRotationalVelocity
|
||||||
|
|
||||||
|
class KinematicMovement
|
||||||
|
{
|
||||||
|
// position
|
||||||
|
private PVector position;
|
||||||
|
private float heading;
|
||||||
|
|
||||||
|
private float speed;
|
||||||
|
private float rotational_velocity;
|
||||||
|
|
||||||
|
float max_speed;
|
||||||
|
float max_rotational_speed;
|
||||||
|
KinematicMovement(PVector position, float heading, float max_speed, float max_rotational_speed)
|
||||||
|
{
|
||||||
|
this.position = position;
|
||||||
|
this.heading = heading;
|
||||||
|
this.speed = 0;
|
||||||
|
this.rotational_velocity = 0;
|
||||||
|
this.max_speed = max_speed;
|
||||||
|
this.max_rotational_speed = max_rotational_speed;
|
||||||
|
}
|
||||||
|
void update(float dt)
|
||||||
|
{
|
||||||
|
PVector velocity = new PVector(cos(this.heading), sin(this.heading)).mult(speed);
|
||||||
|
|
||||||
|
PVector destination = PVector.add(this.position, PVector.mult(velocity, dt));
|
||||||
|
// check for map collisions; only move if no collisions
|
||||||
|
if (!map.collides(this.position, destination))
|
||||||
|
this.position = destination;
|
||||||
|
this.heading += this.rotational_velocity*dt;
|
||||||
|
this.heading = normalize_angle(this.heading);
|
||||||
|
}
|
||||||
|
private void setSpeed(float s, float rs)
|
||||||
|
{
|
||||||
|
this.speed = constrain(s, -max_speed, max_speed);
|
||||||
|
this.rotational_velocity = constrain(rs, -this.max_rotational_speed, this.max_rotational_speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the public methods
|
||||||
|
void increaseSpeed(float ds, float drs)
|
||||||
|
{
|
||||||
|
setSpeed(this.speed + ds, this.rotational_velocity + drs);
|
||||||
|
}
|
||||||
|
|
||||||
|
PVector getPosition()
|
||||||
|
{
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getHeading()
|
||||||
|
{
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getSpeed()
|
||||||
|
{
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRotationalVelocity()
|
||||||
|
{
|
||||||
|
return rotational_velocity;
|
||||||
|
}
|
||||||
|
// End public methods
|
||||||
|
}
|
|
@ -0,0 +1,286 @@
|
||||||
|
/// You do not have to change this file, but you can, if you want to add a more sophisticated generator.
|
||||||
|
class Wall
|
||||||
|
{
|
||||||
|
PVector start;
|
||||||
|
PVector end;
|
||||||
|
PVector normal;
|
||||||
|
PVector direction;
|
||||||
|
float len;
|
||||||
|
|
||||||
|
Wall(PVector start, PVector end)
|
||||||
|
{
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
direction = PVector.sub(this.end, this.start);
|
||||||
|
len = direction.mag();
|
||||||
|
direction.normalize();
|
||||||
|
normal = new PVector(-direction.y, direction.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean crosses(PVector from, PVector to)
|
||||||
|
{
|
||||||
|
// Vector pointing from `this.start` to `from`
|
||||||
|
PVector d1 = PVector.sub(from, this.start);
|
||||||
|
// Vector pointing from `this.start` to `to`
|
||||||
|
PVector d2 = PVector.sub(to, this.start);
|
||||||
|
// If both vectors are on the same side of the wall
|
||||||
|
// their dot products with the normal will have the same sign
|
||||||
|
// If they are both positive, or both negative, their product will
|
||||||
|
// be positive.
|
||||||
|
float dist1 = normal.dot(d1);
|
||||||
|
float dist2 = normal.dot(d2);
|
||||||
|
if (dist1 * dist2 > 0) return false;
|
||||||
|
|
||||||
|
// if the start and end are on different sides, we need to determine
|
||||||
|
// how far the intersection point is along the wall
|
||||||
|
// first we determine how far the projections of from and to are
|
||||||
|
// along the wall
|
||||||
|
float ldist1 = direction.dot(d1);
|
||||||
|
float ldist2 = direction.dot(d2);
|
||||||
|
|
||||||
|
// the distance of the intersection point from the start
|
||||||
|
// is proportional to the normal distance of `from` in
|
||||||
|
// along the total movement
|
||||||
|
float t = dist1/(dist1 - dist2);
|
||||||
|
|
||||||
|
// calculate the intersection as this proportion
|
||||||
|
float intersection = ldist1 + t*(ldist2 - ldist1);
|
||||||
|
if (intersection < 0 || intersection > len) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the mid-point of this wall
|
||||||
|
PVector center()
|
||||||
|
{
|
||||||
|
return PVector.mult(PVector.add(start, end), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
strokeWeight(3);
|
||||||
|
line(start.x, start.y, end.x, end.y);
|
||||||
|
if (SHOW_WALL_DIRECTION)
|
||||||
|
{
|
||||||
|
PVector marker = PVector.add(PVector.mult(start, 0.2), PVector.mult(end, 0.8));
|
||||||
|
circle(marker.x, marker.y, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPolygon(ArrayList<Wall> walls, PVector[] nodes)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < nodes.length; ++ i)
|
||||||
|
{
|
||||||
|
int next = (i+1)%nodes.length;
|
||||||
|
walls.add(new Wall(nodes[i], nodes[next]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPolygonScaled(ArrayList<Wall> walls, PVector[] nodes)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < nodes.length; ++ i)
|
||||||
|
{
|
||||||
|
int next = (i+1)%nodes.length;
|
||||||
|
walls.add(new Wall(new PVector(nodes[i].x*width, nodes[i].y*height), new PVector(nodes[next].x*width, nodes[next].y*height)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Obstacle
|
||||||
|
{
|
||||||
|
ArrayList<Wall> walls;
|
||||||
|
Obstacle()
|
||||||
|
{
|
||||||
|
walls = new ArrayList<Wall>();
|
||||||
|
PVector origin = new PVector(width*0.1 + random(width*0.65), height*0.12 + random(height*0.65));
|
||||||
|
if (origin.x < 100 && origin.y > 500) origin.add(new PVector(150,0));
|
||||||
|
PVector[] nodes = new PVector[] {};
|
||||||
|
float angle = random(100)/100.0;
|
||||||
|
for (int i = 0; i < 3 + random(2) && angle < TAU; ++i)
|
||||||
|
{
|
||||||
|
float distance = height*0.05 + random(height*0.15);
|
||||||
|
nodes = (PVector[])append(nodes, PVector.add(origin, new PVector(cos(-angle)*distance, sin(-angle)*distance)));
|
||||||
|
angle += 1 + random(25)/50;
|
||||||
|
}
|
||||||
|
AddPolygon(walls, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a (closed!) polygon surrounded by walls, tests if the
|
||||||
|
// given point is inside that polygon.
|
||||||
|
// Note that this only works for polygons that are inside the
|
||||||
|
// visible screen (or not too far outside)
|
||||||
|
boolean isPointInPolygon(PVector point, ArrayList<Wall> walls)
|
||||||
|
{
|
||||||
|
// we create a test point "far away" horizontally
|
||||||
|
PVector testpoint = PVector.add(point, new PVector(width*2, 0));
|
||||||
|
|
||||||
|
// Then we count how often the line from the given point
|
||||||
|
// to our test point intersects the polygon outline
|
||||||
|
int count = 0;
|
||||||
|
for (Wall w: walls)
|
||||||
|
{
|
||||||
|
if (w.crosses(point, testpoint))
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we cross an odd number of times, we started inside
|
||||||
|
// otherwise we started outside the polygon
|
||||||
|
// Intersections alternate between enter and exit,
|
||||||
|
// so if we "know" that the testpoint is outside
|
||||||
|
// and odd number means we exited one more time
|
||||||
|
// than we entered.
|
||||||
|
return (count%2) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Map
|
||||||
|
{
|
||||||
|
ArrayList<Wall> walls;
|
||||||
|
ArrayList<Obstacle> obstacles;
|
||||||
|
ArrayList<Wall> outline;
|
||||||
|
|
||||||
|
ArrayList<PVector> pts;
|
||||||
|
|
||||||
|
Map()
|
||||||
|
{
|
||||||
|
walls = new ArrayList<Wall>();
|
||||||
|
outline = new ArrayList<Wall>();
|
||||||
|
obstacles = new ArrayList<Obstacle>();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean collides(PVector from, PVector to)
|
||||||
|
{
|
||||||
|
for (Wall w : walls)
|
||||||
|
{
|
||||||
|
if (w.crosses(from, to)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doSplit(boolean xdir, float from, float to, float other, float otherend, ArrayList<PVector> points, int level)
|
||||||
|
{
|
||||||
|
float range = abs(to-from);
|
||||||
|
float sign = -1;
|
||||||
|
if (to > from) sign = 1;
|
||||||
|
if (range < 70) return;
|
||||||
|
if (level > 1 && random(0,1) < 0.05*level) return;
|
||||||
|
float split = from + sign*random(range*0.35, range*0.45);
|
||||||
|
float splitend = split + sign*random(20, range*0.35-10);
|
||||||
|
if (xdir)
|
||||||
|
points.add(new PVector(split, other));
|
||||||
|
else
|
||||||
|
points.add(new PVector(other, split));
|
||||||
|
float othersign = 1;
|
||||||
|
if (otherend < other) othersign = -1;
|
||||||
|
float otherrange = abs(other-otherend);
|
||||||
|
float spikeend = other + othersign*random(otherrange*0.4, otherrange*(0.9 - 0.1*level));
|
||||||
|
doSplit(!xdir, other, spikeend, split, from, points, level+1);
|
||||||
|
if (xdir)
|
||||||
|
{
|
||||||
|
points.add(new PVector(split, spikeend));
|
||||||
|
points.add(new PVector(splitend, spikeend));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
points.add(new PVector(spikeend, split));
|
||||||
|
points.add(new PVector(spikeend, splitend));
|
||||||
|
}
|
||||||
|
doSplit(!xdir, spikeend, other, splitend, to, points, level + 1);
|
||||||
|
|
||||||
|
if (xdir)
|
||||||
|
points.add(new PVector(splitend, other));
|
||||||
|
else
|
||||||
|
points.add(new PVector(other, splitend));
|
||||||
|
}
|
||||||
|
|
||||||
|
void randomMap()
|
||||||
|
{
|
||||||
|
ArrayList<PVector> points = new ArrayList<PVector>();
|
||||||
|
|
||||||
|
points.add(new PVector(0, height));
|
||||||
|
doSplit(true, 50, width, height, 0, points, 0);
|
||||||
|
points.add(new PVector(width, height));
|
||||||
|
points.add(new PVector(width, 0));
|
||||||
|
points.add(new PVector(200, 0));
|
||||||
|
points.add(new PVector(200, 80));
|
||||||
|
points.add(new PVector(0, 80));
|
||||||
|
|
||||||
|
//pts = points;
|
||||||
|
AddPolygon(outline, points.toArray(new PVector[]{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void generate(int which)
|
||||||
|
{
|
||||||
|
outline.clear();
|
||||||
|
obstacles.clear();
|
||||||
|
walls.clear();
|
||||||
|
if (which < 0)
|
||||||
|
{
|
||||||
|
randomMap();
|
||||||
|
}
|
||||||
|
else if (which == 0)
|
||||||
|
{
|
||||||
|
AddPolygon(outline, new PVector[] {new PVector(-100, height+100), new PVector(width+100, height+100), new PVector(width+100, -100), new PVector(-100,-100)});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddPolygonScaled(outline, customMap(which));
|
||||||
|
}
|
||||||
|
walls.addAll(outline);
|
||||||
|
for (int i = 0; i < random(MAX_OBSTACLES); ++i)
|
||||||
|
{
|
||||||
|
Obstacle obst = new Obstacle();
|
||||||
|
boolean ok = true;
|
||||||
|
// only obstacle if it doesn't intersect with any existing one (or the exterior)
|
||||||
|
for (Wall w : obst.walls)
|
||||||
|
{
|
||||||
|
if (collides(w.start, w.end)) ok = false;
|
||||||
|
if (!isReachable(w.start)) ok = false;
|
||||||
|
}
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
obstacles.add(obst);
|
||||||
|
walls.addAll(obst.walls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(float dt)
|
||||||
|
{
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
stroke(255);
|
||||||
|
strokeWeight(3);
|
||||||
|
for (Wall w : walls)
|
||||||
|
{
|
||||||
|
w.draw();
|
||||||
|
}
|
||||||
|
if (pts != null)
|
||||||
|
{
|
||||||
|
PVector current = new PVector(width/2, height/2);
|
||||||
|
for (PVector p : pts)
|
||||||
|
{
|
||||||
|
fill(255,0,0);
|
||||||
|
circle(p.x, p.y, 4);
|
||||||
|
line(current.x, current.y, p.x, p.y);
|
||||||
|
current = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isReachable(PVector point)
|
||||||
|
{
|
||||||
|
if (!isPointInPolygon(point, outline)) return false;
|
||||||
|
for (Obstacle o: obstacles)
|
||||||
|
{
|
||||||
|
if (isPointInPolygon(point, o.walls)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Useful to sort lists by a custom key
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
|
||||||
|
/// In this file you will implement your navmesh and pathfinding.
|
||||||
|
|
||||||
|
/// This node representation is just a suggestion
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
ArrayList<Wall> polygon;
|
||||||
|
PVector center;
|
||||||
|
ArrayList<Node> neighbors;
|
||||||
|
ArrayList<Wall> connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NavMesh
|
||||||
|
{
|
||||||
|
void bake(Map map)
|
||||||
|
{
|
||||||
|
/// generate the graph you need for pathfinding
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<PVector> findPath(PVector start, PVector destination)
|
||||||
|
{
|
||||||
|
/// implement A* to find a path
|
||||||
|
ArrayList<PVector> result = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void update(float dt)
|
||||||
|
{
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
/// use this to draw the nav mesh graph
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/// You do not need to change anything in this file, but you can
|
||||||
|
/// For example, if you want to add additional options controllable by keys
|
||||||
|
/// keyPressed would be the place for that.
|
||||||
|
|
||||||
|
ArrayList<PVector> waypoints = new ArrayList<PVector>();
|
||||||
|
Boid billy;
|
||||||
|
int lastt;
|
||||||
|
|
||||||
|
int mapnr = 0;
|
||||||
|
|
||||||
|
Map map = new Map();
|
||||||
|
NavMesh nm = new NavMesh();
|
||||||
|
|
||||||
|
boolean entering_path = false;
|
||||||
|
|
||||||
|
boolean show_nav_mesh = false;
|
||||||
|
|
||||||
|
boolean show_waypoints = false;
|
||||||
|
|
||||||
|
boolean show_help = false;
|
||||||
|
|
||||||
|
boolean flocking_enabled = false;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
size(800, 600);
|
||||||
|
|
||||||
|
billy = new Boid(BILLY_START, BILLY_START_HEADING, BILLY_MAX_SPEED, BILLY_MAX_ROTATIONAL_SPEED, BILLY_MAX_ACCELERATION, BILLY_MAX_ROTATIONAL_ACCELERATION);
|
||||||
|
randomSeed(0);
|
||||||
|
map.generate(mapnr);
|
||||||
|
nm.bake(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mousePressed() {
|
||||||
|
if (show_help) return;
|
||||||
|
PVector target = new PVector(mouseX, mouseY);
|
||||||
|
if (!map.isReachable(target)) return;
|
||||||
|
if (mouseButton == LEFT)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (waypoints.size() == 0)
|
||||||
|
{
|
||||||
|
billy.seek(target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
waypoints.add(target);
|
||||||
|
entering_path = false;
|
||||||
|
billy.follow(waypoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mouseButton == RIGHT)
|
||||||
|
{
|
||||||
|
if (!entering_path)
|
||||||
|
waypoints = new ArrayList<PVector>();
|
||||||
|
waypoints.add(target);
|
||||||
|
entering_path = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyPressed()
|
||||||
|
{
|
||||||
|
if (show_help)
|
||||||
|
{
|
||||||
|
show_help = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key == 'h')
|
||||||
|
{
|
||||||
|
show_help = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_help) return;
|
||||||
|
if (key == 'g')
|
||||||
|
{
|
||||||
|
map.generate(-1);
|
||||||
|
mapnr = -1;
|
||||||
|
nm.bake(map);
|
||||||
|
}
|
||||||
|
else if (key == 'n')
|
||||||
|
{
|
||||||
|
show_nav_mesh = !show_nav_mesh;
|
||||||
|
}
|
||||||
|
else if (key == 'w')
|
||||||
|
{
|
||||||
|
show_waypoints = !show_waypoints;
|
||||||
|
}
|
||||||
|
else if ((key >= '1' && key <= '9'))
|
||||||
|
{
|
||||||
|
mapnr = key-'1' + 1;
|
||||||
|
map.generate(mapnr);
|
||||||
|
|
||||||
|
nm.bake(map);
|
||||||
|
}
|
||||||
|
else if (key == '0')
|
||||||
|
{
|
||||||
|
mapnr = 0;
|
||||||
|
map.generate(0);
|
||||||
|
nm.bake(map);
|
||||||
|
}
|
||||||
|
else if (key == 'f')
|
||||||
|
{
|
||||||
|
flocking_enabled = !flocking_enabled;
|
||||||
|
if (flocking_enabled)
|
||||||
|
{
|
||||||
|
flock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unflock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_status(boolean active, String show, int x)
|
||||||
|
{
|
||||||
|
fill(255,255,255);
|
||||||
|
if (active)
|
||||||
|
fill(255,0,0);
|
||||||
|
text(show, x, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() {
|
||||||
|
background(0);
|
||||||
|
|
||||||
|
if (entering_path || show_waypoints)
|
||||||
|
{
|
||||||
|
stroke(255,0,0);
|
||||||
|
strokeWeight(1);
|
||||||
|
PVector current = billy.kinematic.position;
|
||||||
|
if (show_waypoints && billy.target != null)
|
||||||
|
{
|
||||||
|
line(current.x, current.y, billy.target.x, billy.target.y);
|
||||||
|
current = billy.target;
|
||||||
|
}
|
||||||
|
for (PVector wp : waypoints)
|
||||||
|
{
|
||||||
|
line(current.x, current.y, wp.x, wp.y);
|
||||||
|
current = wp;
|
||||||
|
}
|
||||||
|
if (entering_path)
|
||||||
|
line(current.x, current.y, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float dt = (millis() - lastt)/1000.0;
|
||||||
|
lastt = millis();
|
||||||
|
billy.update(dt);
|
||||||
|
map.update(dt);
|
||||||
|
if (show_nav_mesh)
|
||||||
|
nm.update(dt);
|
||||||
|
textSize(12);
|
||||||
|
show_status(show_nav_mesh, "N", 30);
|
||||||
|
show_status(show_waypoints, "W", 50);
|
||||||
|
show_status(show_help, "H", 70);
|
||||||
|
show_status(flocking_enabled, "F", 90);
|
||||||
|
if (mapnr < 0)
|
||||||
|
show_status(false, "R", 110);
|
||||||
|
else
|
||||||
|
show_status(false, String.format("%d", mapnr), 110);
|
||||||
|
|
||||||
|
if (show_help)
|
||||||
|
{
|
||||||
|
fill(255);
|
||||||
|
stroke(0,0,255);
|
||||||
|
rect(width*0.25, height*0.25, width*0.5, height*0.5);
|
||||||
|
fill(0);
|
||||||
|
textSize(32);
|
||||||
|
text("HELP", width*0.5-30, height*0.25 + 40);
|
||||||
|
textSize(18);
|
||||||
|
text("0,1,2,3,4 - Show custom map 0,1,2,3,4", width*0.25+40, height*0.25 + 70);
|
||||||
|
text("G - Generate random map", width*0.25+40, height*0.25 + 90);
|
||||||
|
text("N - Show NavMesh", width*0.25+40, height*0.25 + 110);
|
||||||
|
text("W - Show waypoints while moving", width*0.25+40, height*0.25 + 130);
|
||||||
|
text("F - Enable/disable flocking", width*0.25+40, height*0.25 + 150);
|
||||||
|
text("H - This screen", width*0.25+40, height*0.25 + 170);
|
||||||
|
|
||||||
|
text("Press any key to close", width*0.5 - 80, height*0.75 - 80);
|
||||||
|
textSize(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Currently not used, but you may want to use it to enable/disable
|
||||||
|
// draw-calls or debug output as needed
|
||||||
|
boolean DEBUG = false;
|
||||||
|
|
||||||
|
// The radius of the circle representing the boid body
|
||||||
|
int BOID_SIZE = 20;
|
||||||
|
|
||||||
|
// Where does billy start?
|
||||||
|
PVector BILLY_START = new PVector(50,500);
|
||||||
|
float BILLY_START_HEADING = 0;
|
||||||
|
|
||||||
|
// How fast can billy go and turn?
|
||||||
|
float BILLY_MAX_SPEED = 80;
|
||||||
|
float BILLY_MAX_ROTATIONAL_SPEED = 3;
|
||||||
|
|
||||||
|
float BILLY_MAX_ACCELERATION = 1;
|
||||||
|
float BILLY_MAX_ROTATIONAL_ACCELERATION = 1;
|
||||||
|
|
||||||
|
// Should boids leave breadcrumbs behind?
|
||||||
|
boolean LEAVE_CRUMBS = true;
|
||||||
|
|
||||||
|
// How many crumbs?
|
||||||
|
int MAX_CRUMBS = 1000;
|
||||||
|
|
||||||
|
// Time between crumbs
|
||||||
|
int CRUMB_INTERVAL = 200;
|
||||||
|
|
||||||
|
// How big are the crumbs?
|
||||||
|
int CRUMB_SIZE = 2;
|
||||||
|
|
||||||
|
// use for debugging, if you want to see where walls start/end (a circle is drawn closer to the end)
|
||||||
|
boolean SHOW_WALL_DIRECTION = false;
|
||||||
|
|
||||||
|
// How many obstacles should be generated *at most*
|
||||||
|
// Note that maps 2-4 are pretty dense and obstacles
|
||||||
|
// are only placed if they won't intersect with the map
|
||||||
|
int MAX_OBSTACLES = 0;
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Normalize an angle to be between 0 and TAU (= 2 PI)
|
||||||
|
float normalize_angle(float angle)
|
||||||
|
{
|
||||||
|
while (angle < 0) angle += TAU;
|
||||||
|
while (angle > TAU) angle -= TAU;
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize an angle to be between -PI and PI
|
||||||
|
float normalize_angle_left_right(float angle)
|
||||||
|
{
|
||||||
|
while (angle < -PI) angle += TAU;
|
||||||
|
while (angle > PI) angle -= TAU;
|
||||||
|
return angle;
|
||||||
|
}
|
Loading…
Reference in New Issue