📄 cboid.cpp
字号:
/* Copyright (C) Steven Woodcock, 2000. * 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, 2000" *///*********************************************************************// 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 + -