⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cboid.cpp

📁 游戏编程精华02-含有几十个游戏编程例子
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/* 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 + -