Flocking With Cocos2D

This is an implementation of Craig Reynold's flocking behavior based on the book Python Game Programming By Example.

Imports

# third party
from cocos.cocosnode import CocosNode
import cocos
import cocos.euclid as euclid
import cocos.particle_system as particle_system

Constants

The Boid

Boid Settings

class BoidSettings(object):
    """a settings object"""
    def __init__(self):
        self.position = None
        self.velocity = None
        self.speed = None
        self.max_force = None
        self._attributes = None
        return

    @property
    def attributes(self):
        """list of required attributes"""
        if self._attributes is None:
            self._attributes = ("position",
                                "velocity",
                                "speed",
                                "max_force")
        return self._attributes

    def x_y(self, position):
        """sets the initial x, y coordinates
       Parameters
       ----------

       position: tuple
         (x, y) coordinates
       """
        self.position = position
        self.check_types(position, "position", (list, tuple))
        for item in position:
            self.check_numeric(item, "position")
        return

    def velocity_vector(self, velocity):
        """initial velocity

       Parameters
       ----------

       velocity: Vector2
         2-d vector representing boid's velocity
       """
        self.velocity = velocity
        self.check_type(velocity, "velocity", Vector2)
        return

    def pixels_per_frame(self, speed):
        """initial speed of the boid

       Parameters
       ----------

       speed: number
         number of pixels to move per frame
       """
        self.speed = speed
        self.check_numeric(speed, "speed")
        return

    def maximum_force(self, max_force):
        """sets the max force magnitude

       Parameters
       ----------
       max_force: numeric
         upper-bound for the steering force
       """
        self.max_force = max_force
        self.check_numeric(max_force)
        return

    def maximum_velocity(self, max_velocity):
        """sets the max-velocity

       Parameters
       ----------

       max_velocity: numeric
         upper-bound for magnitude of velocity
       """
        self.max_velocity = max_velocity
        self.check_numeric(max_velocity)
        return

    def check_numeric(self, value, identifier):
        """checks value is numeric

       Parameters
       ----------

       value: object
         item to check

       identifier: string
         name for error message

       Raises
       ------

       TypeError if value is not int or float
       """
        self.check_types(value, identifier, (int, float))
        return

    def check_types(self, value, identifier, expected):
        """checks type of value
       Parameters
       ----------

       value: object
         the thing to check
       identifier: string
         id for error message
       expected: collection 
         types to check if value is one of them

       Raises
       ------

       TypeError if type of value not in expected
       """
        if type(value) not in expected:
            raise TypeError("{0} must be one of {1}, not {2}".format(
                identifier,
                expected,
                value))
        return

    def check_type(self, value, identifier, expected):
        """checks type of the value
       Parameters
       ----------

       value: object
         thing to check
       identifier: string
         id for error messages
       expected: type
         what the value should be

       Raises
       ------
       TypeError if type of value is not expected
       """
        if not isinstance(value, expected):
            raise TypeError("{0} must be {1} not {2}".format(identifier,
                                                             expected,
                                                             value))
        return

Boid Node

class Boid(CocosNode):
    """represents a boid
    Parameters
    ----------

    settings: BoidSettings
      settings for this node
    """
    def __init__(self):
        super(Boid, self).__init__()
        self.settings = settings
        self.position = settings.position
        self.velocity = euclid.Vector2(0, 0)
        self.speed = settings.speed
        self.max_force = settings.max_force
        self.max_velocity = settings.max_velocity
        self.target = None

I'm not a fan of method calls in the constructor, but these next two lines help set up the node.

self.add(particle_system.Sun())
self.schedule(self.update)
return

The add method sets the Sun instance as a child of the Boid node and the schedule method sets the Boid's update method to be called once per frame.

def update(self, delta):
    """updates the current position

    Parameters
    ----------

    delta: float
      seconds since the last clock tick
    """
    if self.target is None:
        return

The target

distance = self.target - euclid.Vector2(self.x, self.y)
steering_force = distance * self.speed - self.velocity
steering_force = truncate(self.velocity + steering, self.max_velocity)
self.position += self.velocity * delta
return

Main