# 鱼群算法实现-The Three Simple Rules of Flocking Behaviors: Alignment, Cohesion, and Separation

In the natural world, organisms exhibit certain behaviors when traveling in groups. This phenomenon, also known as flocking, occurs at both microscopic scales (bacteria) and macroscopic scales (fish). Using computers, these patterns can be simulated by creating simple rules and combining them. This is known as emergent behavior, and can be used in games to simulate chaotic or life-like group movement.

Note: Although this tutorial is written using Flash and AS3, you should be able to use the same techniques and concepts in almost any game development environment.

In this tutorial, I will cover the three main rules used to simulate flocking and explain how to implement each one.  Before we begin, here's some terminology I'll be using:

• Agent: A single entity or character.
• Velocity vector: An agent's current velocity.
• Neighborhood: A certain area around the agent, used to look for other agents.
• Resultant: The vector obtained from the calculations of the rule.

This demo shows the effects of the three flocking rules which I'll explain in this tutorial: alignmentcohesion, and separation.

src="https://source.tutsplus.com/gamedev/authors/VijayPemmaraju/flocking/demo/Flocking.swf" width="600" height="450" frameborder="0" scrolling="no" style="box-sizing: border-box; max-width: 100%;">

Image adapted from  Craig Reynolds' article

Alignment is a behavior that causes a particular agent to line up with agents close by.

First, we'll make a function that takes an agent and returns a velocity vector.

 1 2 3 public function computeAlignment(myAgent:Agent):Point { }

We'll need two variables: one for storing the vector we'll compute, and another for keeping track of the number of neighbors of the agent.

 1 2 var v:Point =  new Point(); var neighborCount =  0 ;

With our variables initialized, we now iterate through all of the agents and find the ones within the neighbor radius - that is, those close enough to be considered neighbors of the specified agent. If an agent is found within the radius, its velocity is added to the computation vector, and the neighbor count is incremented.

 01 02 03 04 05 06 07 08 09 10 11 12 13 14 for each ( var agent:Agent  in agentArray) {      if (agent != myAgent)      {          if (myAgent.distanceFrom(agent) <  300 )          {              v.x += agent.velocity.x;              v.y += agent.velocity.y;              neighborCount++;          }        }   }

If no neighbors were found, we simply return the zero vector (the default value of the computation vector).

 1 2 if (neighborCount ==  0 )      return v;

Finally, we divide the computation vector by the neighbor count and normalize it (divide it by its length to get a vector of length 1), obtaining the final resultant vector.

 1 2 3 4 v.x /= neighborCount; v.y /= neighborCount; v.normalize( 1 ); return v;

Image adapted from  Craig Reynolds' article

Cohesion is a behavior that causes agents to steer towards the "center of mass" - that is, the average position of the agents within a certain radius.

The implementation is almost identical to that of the alignment behavior, but there are some key differences. First, instead of adding the velocity to the computation vector, the position is added instead.

 1 2 v.x += agent.x; v.y += agent.y;

Like before, the computation vector is divided by the neighbor count, resulting in the position that corresponds to the center of mass. However, we don't want the center of mass itself, we want the direction towards the center of mass, so we recompute the vector as the distance from the agent to the center of mass. Finally, this value is normalized and returned.

 1 2 3 4 5 v.x /= neighborCount; v.y /= neighborCount; v =  new Point(v.x - myAgent.x, v.y - myAgent.y); v.normalize( 1 ); return v;

Image adapted from  Craig Reynolds' article

Separation is the behavior that causes an agent to steer away from all of its neighbors.

The implementation of separation is very similar to that of alignment and cohesion, so I'll only point out what is different. When a neighboring agent is found, the distance from the agent to the neighbor is added to the computation vector.

 1 2 v.x += agent.x - myAgent.x; v.y += agent.y - myAgent.y

The computation vector is divided by the corresponding neighbor count, but before normalizing, there is one more crucial step involved. The computed vector needs to be negated in order for the agent to steer away from its neighbors properly.

 1 2 v.x *= - 1 ; v.y *= - 1 ;

Once these three rules have been implemented, they need to come together. The simplest way to do this is as follows:

 1 2 3 4 5 6 7 8 var alignment = computeAlignment(agent); var cohesion = computeCohesion(agent); var separation = computeSeparation(agent);   agent.velocity.x += alignment.x + cohesion.x + separation.x; agent.velocity.y += alignment.y + cohesion.y + separation.y;   agent.velocity.normalize(AGENT_SPEED);

Here, I simply compute the three rules for a particular agent, and add them to the velocity. I then normalize the velocity and then multiply by some constant representing the default speed for an agent.It is possible to enhance this further by adding weights for each rule to tweak the behaviors:

 1 2 agent.velocity.x += alignment.x * alignmentWeight + cohesion.x * cohesionWeight + separation.x * separationWeight; agent.velocity.y += alignment.y * alignmentWeight + cohesion.y * cohesionWeight + separation.y * separationWeight;

Modifying these weights will change the way the agents flock. Be sure to experiment with the numbers until you find something you like.

Here's the demo again so you can try it out:

01-08
03-07

10-19 3000
03-10 1万+
10-15 3916
03-19 1万+
08-14 9911
01-16 4万+
05-12 8703
06-08 2万+
05-17 1956
05-26 5928
03-03 6738
12-20 53