📄 cboid.cpp
字号:
/* Copyright (C) Steven Woodcock, 2001.
* All rights reserved worldwide.
*
* This software is provided "as is" without express or implied
* warranties. You may freely copy and compile this source into
* applications you distribute provided that the copyright text
* below is included in the resulting source code, for example:
* "Portions Copyright (C) Steven Woodcock, 2001"
*/
//*********************************************************************
// Name: CBoid.cpp
// Purpose: Class methods for individual boids (flock members).
//*********************************************************************
//
// includes
//
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "CFlock.h"
#include "CBoid.h"
#include "CBox.h"
#include "vector.h"
//
// static variable initialization
//
// visible friends list (work space reused by each flock boid)
CBoid * CBoid::VisibleFriendsList[] = {NULL};
//
// constructor and destructor methods
//
// Constructor #1.
// Creates an individual boid with randomized position,
// velocity, and orientation
CBoid::CBoid (char * id_v)
{
#ifdef BOID_DEBUG
myprintf("\nCBoid constructor #1 called for boid %s.\n",id_v);
#endif
strcpy(m_id, id_v);
m_updates = 0;
m_alive = TRUE;
// generate random position
m_pos.x = RAND() * CBox::WorldPtr->GetBoxWidth()/3;
m_pos.y = RAND() * CBox::WorldPtr->GetBoxHeight()/3;
m_pos.z = RAND() * CBox::WorldPtr->GetBoxLength()/3;
// flip positions for greater randomness
if (RAND() > 0.5f) m_pos.x *= -1;
if (RAND() > 0.5f) m_pos.y *= -1;
if (RAND() > 0.5f) m_pos.z *= -1;
// generate random velocity
m_vel.x = RAND();
m_vel.z = RAND();
// flip velocities for greater randomness
if (RAND() > 0.5f) m_vel.x *= -1;
if (RAND() > 0.5f) m_vel.z *= -1;
// compute speed based on velocity
m_speed = m_vel.length();
// zero out orientation
m_ang.x = m_ang.y = m_ang.z = 0;
// Set the boid individuality parameters. We'll just
// use the base value and add some random amount to that.
// generate this boid's "personal maximum" upper speed
m_max_speed = MaxSpeed + (RAND() * 0.5f);
// feeding distance
m_feeding_distance = MaxFeedingDistance + (RAND() * 0.5f);
// personal range of sight
m_perception_range = DefaultPerceptionRange + (RAND() * 0.5f);
// preferred separation distance
m_preferred_separation = SeparationDist + (RAND() * 0.5f);
// preferred keepaway distance
m_keepaway_distance = KeepAwayDist + (RAND() * 0.5f);
// initial hunger rating
m_hunger = m_starting_hunger = MaxHunger + (SIGN(m_vel.x) * RAND() * 0.5f);
// other values
m_type = None;
m_my_prey = None;
m_my_predator = None;
m_num_flockmates_seen = 0;
m_nearest_flockmate = NULL;
m_dist_to_nearest_flockmate = INFINITY;
m_num_predators_seen = 0;
m_nearest_predator = NULL;
m_dist_to_nearest_predator = INFINITY;
m_num_prey_seen = 0;
m_nearest_prey = NULL;
m_dist_to_nearest_prey = INFINITY;
m_preys_flock = NULL;
m_num_obstacles_seen = 0;
m_nearest_obstacle = NULL;
m_dist_to_nearest_obstacle = INFINITY;
m_reproduction_flag = FALSE;
m_next = m_prev = NULL;
#ifdef BOID_DEBUG
PrintData();
#endif
}
// Constructor #2.
// Creates an individual boid with specific position, velocity,
// orientation, and type
CBoid::CBoid (char * id_v, BoidTypes type_v,
vector * pos_v, vector * vel_v, vector * ang_v)
{
#ifdef BOID_DEBUG
myprintf("\nCBoid constructor #2 called for boid %s.\n",id_v);
#endif
strcpy(m_id, id_v);
m_updates = 0;
m_alive = TRUE;
m_pos = pos_v;
m_vel = vel_v;
m_ang = ang_v;
m_speed = vel_v->length();
// Set the boid individuality parameters. We'll just
// use the base value and add some random amount to that.
// generate this boid's "personal maximum" upper speed
m_max_speed = MaxSpeed + (RAND() * 0.5f);
// feeding distance
m_feeding_distance = MaxFeedingDistance + (RAND() * 0.5f);
// personal range of sight
m_perception_range = DefaultPerceptionRange + (RAND() * 0.5f);
// preferred separation distance
m_preferred_separation = SeparationDist + (RAND() * 0.5f);
// preferred keepaway distance
m_keepaway_distance = KeepAwayDist + (RAND() * 0.5f);
// initial hunger rating
m_hunger = m_starting_hunger = MaxHunger + (SIGN(m_vel.x) * RAND() * 0.5f);
// predator/prey values
m_type = type_v;
m_my_prey = None;
m_my_predator = None;
// other values
m_num_flockmates_seen = 0;
m_nearest_flockmate = NULL;
m_dist_to_nearest_flockmate = INFINITY;
m_num_predators_seen = 0;
m_nearest_predator = NULL;
m_dist_to_nearest_predator = INFINITY;
m_num_prey_seen = 0;
m_nearest_prey = NULL;
m_dist_to_nearest_prey = INFINITY;
m_preys_flock = NULL;
m_num_obstacles_seen = 0;
m_nearest_obstacle = NULL;
m_dist_to_nearest_obstacle = INFINITY;
m_reproduction_flag = FALSE;
m_next = m_prev = NULL;
#ifdef BOID_DEBUG
PrintData();
#endif
}
// Destructor.
// Destroys indicated boid.
CBoid::~CBoid (void)
{
#ifdef BOID_DEBUG
myprintf("\nCBoid destructor called for boid %x\n",this);
#endif
}
//////////////////////////
// public flocking methods
//////////////////////////
// FlockIt.
// Used for frame-by-frame updates; no time deltas on positions.
void CBoid::FlockIt (int flock_id, CBoid *first_boid)
{
vector acc;
#ifdef BOID_DEBUG
myprintf("\n=====\nUpdate %d for %s\n", m_updates, m_id);
myprintf("pos before = %f %f %f\n",m_pos.x,m_pos.y,m_pos.z);
#endif
// test: is this boid alive or dead?
if (!m_alive) return; // he's dead--get outta here
// increment counter
m_updates++;
// Step 1: Update our position.
// Update our position based on the velocity
// vector we computed last time around.
m_oldpos = m_pos; // save off our previous position
m_pos += m_vel; // apply velocities.
// Step 2: Decrement our hunger rating.
// Decrement our hunger rating so that we might become
// hungry--and search for our prey. Don't bother if we
// don't actually *have* any kind of prey to hunt.
if (GetPreyOf()) SetHunger(HungerPerCycle); // nothing magic about the number, it just works well
// Step 3: SeeFriends.
// Determine if we can see any of our flockmates.
SeeFriends (first_boid);
// Step 4: Flocking behavior.
// Do we see any of our flockmates? If yes, it's time to implement
// the first Three Rules (they don't matter if we can't see anybody)
if (m_num_flockmates_seen) {
// Step 5: Implement Rule #1 (Separation).
// Try to maintain our desired separation distance from our nearest flockmate.
AccumulateChanges (acc, KeepDistance());
// Step 6: Implement Rule #2 (Alignment).
// Try to move the same way our nearest flockmate does.
AccumulateChanges (acc, MatchHeading());
// Step 7: Implement Rule #3 (Cohesion).
// Try to move towards the center of the flock.
AccumulateChanges (acc, SteerToCenter());
}
// Step 8a: Implement Rule #4 (Avoidance of obstacles).
// If we see anything blocking or potentially blocking our
// path, try to steer so as to not run into it.
if (SeeObstacles()) AccumulateChanges (acc, AvoidObstacles());
// Step 8b: Implement Rules #4 (Avoidance of enemies)
// We don't like to get eaten, so if we see any predators
// then we want to avoid them if we can.
if (SeePredators (flock_id)) AccumulateChanges (acc, FleePredators());
// Step 9: Hunt prey.
// If we're hungry then we'll want to locate and eat some prey.
if (m_hunger <= HungerTrigger) {
// we're hungry...do we see anything to eat?
if (SeePrey (flock_id)) AccumulateChanges (acc, HuntPrey());
}
// Step 10: Cruising.
// Flockmates or not, enemies or not, figure out
// how fast we'd move if it were all up to us.
AccumulateChanges (acc, Cruising());
// Step 11: Constrain acceleration
// If our acceleration change is more than we allow, constrain it
if (acc.length() > MaxChange) {
#ifdef BOID_DEBUG
myprintf("WARNING: constraining acceleration for boid %x!\n",this);
#endif
// definitely too much...constrain to maximum change
acc.SetMagnitudeOfVector (MaxChange);
}
// Step 12: Implementation.
// Here's where we apply our newly computed acceleration vector
// to create a new velocity vector to use next update cycle.
m_oldvel = m_vel; // save off our previous velocity
// now add in the acceleration
m_vel += acc;
// Step 13: constraint Y velocity changes.
// Attempt to restrict flight straight up/down by damping out Y axis velocity.
// This isn't strictly necessary, but does lead to more realistic looking flight.
m_vel.y *= MaxPitch;
// Step 14: Constrain our speed.
// If we're moving faster than we're allowed to move, constrain our velocity.
m_speed = m_vel.length();
if (m_speed > m_max_speed) {
// definitely too fast...constrain to maximum speed
#ifdef BOID_DEBUG
myprintf("WARNING: constraining speed for boid %s!\n",m_id);
myprintf(" current speed = %f new speed = %f\n",m_speed, m_max_speed);
#endif
m_vel.SetMagnitudeOfVector (m_max_speed);
m_speed = m_max_speed;
}
// Step 15: Compute roll/pitch/yaw.
// Compute our orientation after all this speed adjustment nonsense.
ComputeRPY();
// Step 16: World boundaries.
// If we've wandered outside the world bounds put us back in. You might
// not need to do this for your world, but that depends on your implementation.
WorldBound();
#ifdef BOID_DEBUG
myprintf("final position = %f %f %f\n",m_pos.x, m_pos.y, m_pos.z);
myprintf("final velocity = %f %f %f\n",m_vel.x, m_vel.y, m_vel.z);
myprintf("final acceleration = %f %f %f\n",acc.x, acc.y, acc.z);
#endif
// Step 17: If we're hunting something at present, let's see if we can eat it
if (m_nearest_prey) EatPrey();
// Step 18: Test to see if we've gone without food too long; if so, we die
if (m_hunger < HungerDeath) KillBoid(flock_id);
// Step 19: Can we reproduce?
if (CanReproduce()) MakeBaby(flock_id);
}
///////////////////////////
// private flocking methods
///////////////////////////
// AvoidObstacles.
// Generates a vector to make a flock boid avoid crashing
// into any obstacles that might be nearby.
vector CBoid::AvoidObstacles (void)
{
vector change;
vector obs_pos;
#ifdef BOID_DEBUG
myprintf("\nInside AvoidObstacles\n");
myprintf("Boid id = %s, urgency = %f.\n",m_id,urgency);
#endif
obs_pos = m_nearest_obstacle->GetPos();
// replace obstacle's Y value (at base) with the
// boid's own Y value (since that's where we computed
// the nearest point on its surface)
obs_pos.y = m_pos.y;
// compute vector away from the obstacle
change = m_pos - obs_pos;
// scale the desired change--we really hate running into things
change.SetMagnitudeOfVector(MaxUrgency);
#ifdef BOID_DEBUG
myprintf(" final change vector from AvoidObstacles = %f %f %f\n",
change.x, change.y, change.z);
#endif
// return change vector
return (change);
}
// Cruising.
// Generates a vector indicating how a flock boid would
// like to move, if it were all up to him and he was under
// no other influences of any kind.
vector CBoid::Cruising (void)
{
vector change = m_vel;
float diff = (m_speed - DesiredSpeed)/ m_max_speed;
float urgency = (float) fabs(diff);
#ifdef BOID_DEBUG
myprintf("\nInside Cruising\n");
myprintf(" diff = %f urgency = %f\n",diff, urgency);
myprintf(" m_speed = %f desired speed = %f\n",m_speed, DesiredSpeed);
myprintf(" initial change vector from Cruising = %f %f %f\n",change.x, change.y, change.z);
if (diff > 0) {
myprintf(" slowing down to meet cruising speed...\n");
} else {
myprintf(" speeding up to meet cruising speed...\n");
}
#endif
// constrain the urgency level
if (urgency < MinUrgency) urgency = MinUrgency;
if (urgency > MaxUrgency) urgency = MaxUrgency;
// Now add some "jitter" so that each boid has a
// bit of randomness just to keep things interesting.
// This will also get us moving if we happen to start
// things standing perfectly still (which is sorta boring).
float jitter = RAND();
if (jitter < 0.45f) {
change.x += MinUrgency * SIGN(diff);
} else if (jitter < 0.90f) {
change.z += MinUrgency * SIGN(diff);
} else {
change.y += MinUrgency * SIGN(diff); // we don't like vertical motion all that much
}
#ifdef BOID_DEBUG
myprintf(" intermediate change vector from Cruising = %f %f %f\n",change.x, change.y, change.z);
#endif
// compute velocity change necessary to get to our desired cruising speed
change.SetMagnitudeOfVector ((urgency * (diff > 0 ? -1 : 1) ));
#ifdef BOID_DEBUG
myprintf(" final change vector from Cruising = %f %f %f\n",change.x, change.y, change.z);
#endif
return (change);
}
// FleePredators.
// Generates a vector for a flock boid
// to avoid the nearest predator it sees.
vector CBoid::FleePredators (void)
{
vector change;
#ifdef BOID_DEBUG
myprintf("\nInside FleePredators\n");
#endif
// test: Are we too close to our nearest predator?
if (m_dist_to_nearest_predator < m_keepaway_distance) {
#ifdef BOID_DEBUG
myprintf(" too close to %x\n",m_nearest_predator);
#endif
// yep...compute vector away from predator
change = m_pos - m_nearest_predator->m_pos;
}
#ifdef BOID_DEBUG
myprintf(" final change vector from FleePredators = %f %f %f\n",change.x, change.y, change.z);
#endif
// return change vector
return (change);
}
// HuntPrey.
// Generates a vector to make a flock boid try to
// close position with any prey boid it may be hunting.
vector CBoid::HuntPrey (void)
{
vector change;
#ifdef BOID_DEBUG
myprintf("\nInside HuntPrey\n");
myprintf(" Boid %s is hunting boid %s\n",m_id,m_nearest_prey->GetID());
#endif
// compute a vector towards the prey--we want to get
// as close as we possibly can so we can eat him!
change = m_nearest_prey->m_pos - m_pos;
// store vector for drawing use (if needed)
m_prey_vector = change;
// make it urgent--we're hungry, after all
change.SetMagnitudeOfVector (m_max_speed);
#ifdef BOID_DEBUG
myprintf(" final change vector from HuntPrey = %f %f %f\n",change.x, change.y, change.z);
#endif
// return change vector
return (change);
}
// KeepDistance.
// Generates a vector for a flock boid to maintain his
// desired separation distance from the nearest flockmate he sees.
vector CBoid::KeepDistance (void)
{
float ratio = m_dist_to_nearest_flockmate/m_preferred_separation;
// compute vector towards our nearest buddy
vector change = m_nearest_flockmate->m_pos - m_pos;
#ifdef BOID_DEBUG
myprintf("\nInside KeepDistance\n");
#endif
// constrain computed ratio to our min/max Urgency levels
if (ratio < MinUrgency) ratio = MinUrgency;
if (ratio > MaxUrgency) ratio = MaxUrgency;
// test: are we too close to our nearest flockmate?
#ifdef BOID_DEBUG
myprintf(" dist_to_nearest_flockmate = %f Sep = %f ratio = %f\n",
m_dist_to_nearest_flockmate,m_preferred_separation,ratio);
#endif
if (m_dist_to_nearest_flockmate < m_preferred_separation) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -