📄 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 "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 and velocity.
CBoid::CBoid (short id_v)
{
#ifdef BOID_DEBUG
myprintf("\nCBoid constructor #1 called for boid %d.\n",id_v);
#endif
m_id = id_v;
m_perception_range = Default_Perception_Range;
// 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;
// other values
m_num_flockmates_seen = 0;
m_nearest_flockmate = NULL;
m_dist_to_nearest_flockmate = INFINITY;
m_num_enemies_seen = 0;
m_nearest_enemy = NULL;
m_dist_to_nearest_enemy = INFINITY;
m_next = m_prev = NULL;
#ifdef BOID_DEBUG
PrintData();
#endif
}
// Constructor #2.
// Creates an individual boid with specific position and velocity.
CBoid::CBoid (short id_v,
vector * pos_v, vector * vel_v, vector * ang_v)
{
#ifdef BOID_DEBUG
myprintf("\nCBoid constructor #2 called for boid %d.\n",id_v);
#endif
m_id = id_v;
m_perception_range = Default_Perception_Range;
m_pos = pos_v;
m_vel = vel_v;
m_ang = ang_v;
m_speed = vel_v->length();
m_num_flockmates_seen = 0;
m_nearest_flockmate = NULL;
m_dist_to_nearest_flockmate = INFINITY;
m_num_enemies_seen = 0;
m_nearest_enemy = NULL;
m_dist_to_nearest_enemy = INFINITY;
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 for %d\n", m_id);
myprintf("pos before = %f %f %f\n",m_pos.x,m_pos.y,m_pos.z);
#endif
// 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: SeeFriends.
// Determine if we can see any of our flockmates.
SeeFriends (first_boid);
// Step 3: 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 4: Implement Rule #1 (Separation).
// Try to maintain our desired separation distance from our nearest flockmate.
AccumulateChanges (acc, KeepDistance());
// Step 5: Implement Rule #2 (Alignment).
// Try to move the same way our nearest flockmate does.
AccumulateChanges (acc, MatchHeading());
// Step 6: Implement Rule #3 (Cohesion).
// Try to move towards the center of the flock.
AccumulateChanges (acc, SteerToCenter());
}
// Step 6: The Fourth Rule (enemies)
// If we're supposed to react to enemy flocks, determine
// if there are any then avoid them if we can.
if (ReactToEnemies) {
SeeEnemies (flock_id);
AccumulateChanges (acc, FleeEnemies());
}
// Step 7: 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 8: 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 9: 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 10: 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 *= MaxUrgency;
// Step 11: Constrain our speed.
// If we're moving faster than we're allowed to move, constrain our velocity.
if ((m_speed = m_vel.length()) > MaxSpeed) {
// definitely too fast...constrain to maximum speed
#ifdef BOID_DEBUG
myprintf("WARNING: constraining speed for boid %x!\n",this);
myprintf(" current speed = %f new speed = %f\n",m_speed, MaxSpeed);
#endif
m_vel.SetMagnitudeOfVector (MaxSpeed);
m_speed = MaxSpeed;
}
// Step 12: Compute roll/pitch/yaw.
// Compute our orientation after all this speed adjustment nonsense.
ComputeRPY();
// Step 13: 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
}
///////////////////////////
// private flocking methods
///////////////////////////
// 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)/ MaxSpeed;
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);
}
// FleeEnemies.
// Generates a vector for a flock boid to avoid the
// nearest enemy (boid of a different flock) it sees.
vector CBoid::FleeEnemies (void)
{
vector change;
#ifdef BOID_DEBUG
myprintf("\nInside FleeEnemies\n");
#endif
// test: Are we too close to our nearest enemy?
if (m_dist_to_nearest_enemy < KeepAwayDist) {
#ifdef BOID_DEBUG
myprintf(" too close to %x\n",m_nearest_enemy);
#endif
// yep...compute vector away from enemy
change = m_pos - m_nearest_enemy->m_pos;
}
// return change vector
return (change);
#ifdef BOID_DEBUG
myprintf(" final change vector from Cruising = %f %f %f\n",change.x, change.y, change.z);
#endif
}
// 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/SeparationDist;
// 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,SeparationDist,ratio);
#endif
if (m_dist_to_nearest_flockmate < SeparationDist) {
#ifdef BOID_DEBUG
myprintf(" too close!\n");
#endif
// too close...move away from our neighbor
change.SetMagnitudeOfVector (-ratio);
} else if (m_dist_to_nearest_flockmate > SeparationDist) {
#ifdef BOID_DEBUG
myprintf(" too far away!\n");
#endif
// too far away...move towards our neighbor
change.SetMagnitudeOfVector (ratio);
} else {
#ifdef BOID_DEBUG
myprintf(" just right!\n");
#endif
// in the UNLIKELY event we're exactly the right distance away, do nothing
change.SetMagnitudeOfVector (0.0);
}
// return change vector
#ifdef BOID_DEBUG
myprintf(" final change vector from KeepDistance = %f %f %f\n",change.x, change.y, change.z);
#endif
return (change);
}
// MatchHeading.
// Generates a vector for a flock boid to try
// to match the heading of its nearest flockmate.
vector CBoid::MatchHeading (void)
{
// copy the heading of our nearest buddy
vector change = m_nearest_flockmate->m_vel;
#ifdef BOID_DEBUG
myprintf("\nInside MatchHeading\n");
#endif
// normalize and then scale our vector change a bit...after
// all we can't instantly snap to a new heading, can we?
change.SetMagnitudeOfVector (MinUrgency);
// return change vector
#ifdef BOID_DEBUG
myprintf(" final change vector from MatchHeading = %f %f %f\n",change.x, change.y, change.z);
#endif
return (change);
}
// SeeEnemies.
// Determines which enemy flock boids a given flock boid can see.
int CBoid::SeeEnemies (int flock_id)
{
float dist;
CBoid *enemy;
#ifdef BOID_DEBUG
myprintf("\nInside SeeEnemies\n");
#endif
// initialize enemy data
m_num_enemies_seen = 0;
m_nearest_enemy = NULL;
m_dist_to_nearest_enemy = INFINITY;
// loop over each flock and determine the closest one we can see
for (int i = 0; i < CFlock::FlockCount; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -