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