What is flocking? Flocking is an emergent behavior and is the resultant of the following forces:

cohesion – finds average position of neighbors and tries to move to that position separation – object keeps a certain distance between itself and its neighbor alignment – finds average direction in which all neighbors are moving and tries to move in that direction

Each NPC has a “visibility cone” and this is used to compute it’s neighbors. The neighbors contribute towards the forces mentioned above.


  1. The angle and length of each NPC’s “visibility cone”.

  2. Weight of cohesion, separation, and alignment (how much each sub-behavior of flock affects the overall flocking behavior).


Flocking behavior is NOT a standalone behavior. It needs to be combined with other steering behaviors such as seek, pursue, flee, evade etc. to function.


Using PandAI’s flocking system:

// To create the flock
flockObject = Flock(unsigned int flock_id, double vcone_angle,
                    double vcone_radius, unsigned int cohesion_wt,
                    unsigned int separation_wt, unsigned int alignment_wt)

“flock_id” is a value identifying the flock.

“vcone_angle” is the visibility angle of the character (represented by a cone around it)

“vcone_radius” is the length of the visibility cone.

“cohesion_wt”, “separation_wt” and “alignment_wt” is the amount of separation force that contributes to the overall flocking behavior.

Some standard values to start you off with:

Type vcone_angle vcone_radius separation_wt cohesion_wt alignment_wt

Normal Pack 270 10 2 4 1

Loose Pack 180 10 2 4 5

Tight Pack 45 5 2 4 5

You could try experimenting with your own values to customize your flock.

To add your AI Character to the above created flock

flockObject.addAiChar(aiChar)     # aiChar is an AICharacter object.

After all the AI Characters are added to the flock, add the flock to the world.

aiWorld.addFlock(flockObject)    # aiWorld is an AIWorld object.

Specify the flock behavior priority. As mentioned earlier, flock behavior works with other steering behaviors.

# aiBehaviors is an AIBehaviors object.
aiBehaviors.flock(float priority)

# Turns the flock behavior off.
aiWorld.flockOff(unsigned int flock_id)

# Turns the flock behavior on.
aiWorld.flockOn(unsigned int flock_id)

# Removes the flock behavior.
# Note: This does NOT remove the AI characters of the flock.
aiWorld.removeFlock(unsigned int flock_id)

# Returns a handle to the flock object.
aiWorld.getFlock(unsigned int flock_id)

The full working code in Panda3D :

import direct.directbase.DirectStart
from panda3d.core import *
from direct.showbase.DirectObject import DirectObject
from direct.task import Task
from direct.actor.Actor import Actor
#for Pandai
from panda3d.ai import *
#for Onscreen GUI
from direct.gui.OnscreenText import OnscreenText

# Globals
speed = 0.75

# Function to put instructions on the screen.
font = loader.loadFont("cmss12")
def addInstructions(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), font=font,
                        pos=(-1.3, pos), align=TextNode.ALeft, scale=.05)

class World(DirectObject):

    def __init__(self):
        base.cam.setPosHpr(0, 0, 85, 0, -90, 0)


    def loadModels(self):
        # Seeker
        self.flockers = []
        for i in range(10):
            ralphStartPos = Vec3(-10+i, 0, 0)
                                     {"run": "models/ralph-run"}))

        # Target
        self.target = loader.loadModel("models/arrow")

    def setAI(self):
        #Creating AI World
        self.AIworld = AIWorld(render)

        #Flock functions
        self.MyFlock = Flock(1, 270, 10, 2, 4, 0.2)

        self.AIchar = []
        self.AIbehaviors = []
        for i in range(10):
            char = AICharacter("flockers" + str(i), self.flockers[i], 100, 0.05, 5)
            self.AIbehaviors[i].pursue(self.target, 0.5)

        #AI World update
        taskMgr.add(self.AIUpdate, "AIUpdate")

    #to update the AIWorld
    def AIUpdate(self, task):
        return Task.cont

    # All the movement functions for the Target
    def setMovement(self):
        self.keyMap = {"left": 0, "right": 0, "up": 0, "down": 0}
        self.accept("arrow_left", self.setKey, ["left", 1])
        self.accept("arrow_right", self.setKey, ["right", 1])
        self.accept("arrow_up", self.setKey, ["up", 1])
        self.accept("arrow_down", self.setKey, ["down", 1])
        self.accept("arrow_left-up", self.setKey, ["left", 0])
        self.accept("arrow_right-up", self.setKey, ["right", 0])
        self.accept("arrow_up-up", self.setKey, ["up", 0])
        self.accept("arrow_down-up", self.setKey, ["down", 0])
        #movement task
        taskMgr.add(self.Mover, "Mover")

        addInstructions(0.9, "Use the Arrow keys to move the Red Target")

    def setKey(self, key, value):
        self.keyMap[key] = value

    def Mover(self,task):
        startPos = self.target.getPos()
        if self.keyMap["left"] != 0:
                self.target.setPos(startPos + Point3(-speed, 0, 0))
        if self.keyMap["right"] != 0:
                self.target.setPos(startPos + Point3(speed, 0, 0))
        if self.keyMap["up"] != 0:
                self.target.setPos(startPos + Point3(0, speed, 0))
        if self.keyMap["down"] != 0:
                self.target.setPos(startPos + Point3(0, -speed, 0))

        return Task.cont

w = World()

To get the full working demo, please visit: