CocosNodes

CocosNode Functionality

The fact that all objects depicting the scene are CocosNodes subclasses means that they share a common core of characteristics and functionalities. You know for one and you know for all.

The common core can be broken by functionality in

Parent-child

  • .add(self, child, z=0, name=None ): Adds a child, raises exception Name already exists if duplicated
  • .remove(self, name_or_obj): Removes a child given its name or object, raises exception Child not found if not present
  • .kill(self): Remove itself from its parent
  • .parent: property giving the parent node or None
  • .get_ancestor(self, klass): Returns the first ancestor that isinstance of klass, or None
  • .get_children(self): Returns a list with the node children, order is back to front (ascending z)
  • .get(self, name): Gets a child given its name, raises exception Child not found if not present
  • operator in , as ‘in node1 in node2’: returns True if and only if node1 is child of node 2

Parent-Child examples

Spatial Placement

A node position is defined relative to the parent node, most precisely with respect the parent position.

  • .x, .y .position: properties giving position relative to parent origin; updating x or y automatically updates position, the reverse is also true.
  • .anchor_x, .anchor_y, anchor: properties giving the center for the scale and rotation, they automatically maintain the relation anchor == (anchor_x, anchor_y). Notice that different CocosNodes subclasses can have different default values for anchor.
  • .scale : 1.0 is the default scale, with 2.0 the node will double its apparent size.
  • .scale_x, .scale_y : per axis scaling factors. The total scaling in axis x is .scale * .scale_x , and analogous for axis y
  • .rotation : in degrees

Spatial Placement snippets ; also most of scripts in directory tests do explicit placement.

Going in or out of the active scene

  • .on_enter(self): called if added to a parent while parent in the active scene or if the entire scene goes active
  • .on_exit(self): called if removed from parent while parent in the active scene or if the entire scene

Render

  • .visit(self): renders itself and its children. Render order is children with z <= 0, itself, children with z > 0
  • .draw(self): draws itself
  • .transform(self): helper for draw and visit, handles the openGL coordinate changes needed to render mandated by position, anchor, scale and rotation
  • .camera: instead of the stock position, anchor, scale and rotation uses a custom gluLookAt that also allows z. This is rarely used, usually by special effects in transitions.

Time management

  • .schedule_interval(self, callback, interval, *args, **kwargs): Schedule a function to be called every interval seconds.
  • .schedule(self, callback, *args, **kwargs): Schedule a function to be called every frame.
  • .unschedule(self, callback): Remove a function from the schedule.
  • .pause_scheduler(self): Time will stop passing for this node: scheduled callbacks will not be called, worker actions will not be called
  • .resume_scheduler(self): Time will continue/start passing for this node and callbacks will be called, worker actions will be called

Automated changes along the time (actions management)

  • .do(self, template_action, target=None): most of the time it is called with target=None, which result in target being self. The action will perform a potentially gradual change in target. Returns the worker action, which is the object doing the changes. You must save a reference to the worker if you plan to call later to .action_remove
  • .action_remove(self, worker_action): the worker action will terminate
  • .pause(self): Suspends the execution of actions.
  • .resume(self): Resumes the execution of actions.
  • .stop(self): All actions scheduled in this node are removed, stop method will be called for each action.
  • .are_actions_running(self): True or False

The automated changes are performed by a collaboration between CocosNode instances and Action instances.

Automated changes along the time snippets Also the test directory has many samples like test_accel*.py, test_blink.py, test_callfunc*.py and others.

Brief tour of built-in CocosNode subclasses

Scene

The root of the tree depicting the full content to be displayed. To change between scenes you use director.

References: guide Scene , api Scene

TransitionScene

Base class to implement a gradual change from one screen (scene) to other. Typical examples are fade in and fade out.

Cocos provides a bunch of ready made scene transitions (sample code in test/test_transition_*.py)

References: api Scene

Layer

Helps to organize the view in the depth axis. Subclasses often offer specialized services to the childs (like ScrollingManager), and can be used to build higher order functionality (like Menu)

References: guide Layer , api Layer

ColorLayer

The primary use is to provide a solid color background to a scene, also used to draw solid color rectangles.

References: api ColorLayer

MultiplexLayer

Allows to show only one of his childs, and to select which one. An use case is when you want to show one of a number of menus.

References: api MultiplexLayer

ScrollingManager

Coordinates a number of ScrollableLayer to do scroll and parallax so that the scroll don’t goes out of the the world.

References: api ScrollingManager

ScrollableLayer

Used as childs of ScrollingManager, helps to manage scroll limits and parallax. You must set attributes px_with and px_height in a ScrollableLayer instance so restrictions in the scroll are enforced. If using parallax, it must be provided in the constructor.

References: api ScrollableLayer

RectMapLayer and HexMapLayer

A tilemap is a world description where
  • the space is partitioned in a regular grid (rectangular or hexagonal in cocos)
  • it is known which associated image and game properties has each cell in the grid.

RectMapLayer and HexMapLayer are usually created by tiles.load, and they care to display the world and give access to the cells properties.

Examples are test/test_tiles.py and test/test_platformer.py

References: api RectMapLayer , api HexMapLayer

PythonInterpreterLayer

Used by director to show a python console that allows to inspect and modify the objects in the application using director. You don’t instantiate manually an PythonInterpreterLayer, simply press ctrl + i (cmd + i on mac) to toggle the layer’s visibility.

References: guide PythonInterpreterLayer , api PythonInterpreterLayer

Sprite

Sprites allows to display an image in a rectangular area, which can be rotated, scaled and moved.

References: api Sprite

Animating a sprite

Animation as in cartoon style animation, that is, replacing the image fast enough to give the illusion of movement, can be accomplished by:

  • using an animated .gif file as source for the image
  • passing a pyglet.image.Animation as image, which collects a number of images
  • have an array of images and let your code assign to the sprite image member

Performance Considerations

Sprites are not suitable for effects that piles a lot of semitransparent images to render things like smoke or poisoning fog. Use particle systems or custom CocosNode s managing vertex lists for that.

If your scene has less than 25 sprites, you can add directly to the scene, like:

class TLayer(cocos.layer.Layer):
    is_event_handler = True
    def __init__(self):
        cocos.layer.Layer.__init__(self)
        world_width, world_height = director.get_window_size()
        rand_color = [255, 0, 0]
        icolor = 0
        for i in range(qty_balls):
            ball = Ball((world_width*random.random(), world_height*random.random()), color=rand_color)
            rand_color[icolor] = random.randint(50, 255)
            icolor = (icolor + 1)%len(rand_color)
            self.add(ball)
self.time = 0.0
self.schedule(self.update)

When you have between 25 and 100 visible sprites, moving at each frame, you should add one or more BatchNode s at some level in the scene and add the sprites to them. Even only one batch can give you 4x fps:

class TLayer(cocos.layer.Layer):
    is_event_handler = True
        def __init__(self):
            cocos.layer.Layer.__init__(self)
            batch = cocos.batch.BatchNode()
            self.add(batch) # we add a batch to the layer ...
            world_width, world_height = director.get_window_size()
            rand_color = [255, 0, 0]
            icolor = 0
            for i in range(100):
                ball = Ball((world_width*random.random(), world_height*random.random()), color=rand_color)
                rand_color[icolor] = random.randint(50, 255)
                icolor = (icolor + 1)%len(rand_color)
                batch.add(ball) # see ? we add sprites to the batch, not the layer
            self.batch = batch
            self.time = 0.0
            self.schedule(self.update)

With more than 100 visible, moving at each frame sprites, you will need to optimize further to run at 60 fps in weak hardware like netbooks, old desktops, cheap new desktops. You can look at cocos particle systems and the section graphics in pyglet programing guide for ideas.

For a ballpark data point, a 2008 low gamming spec machine (athon 5200, radeon 4650) gives:

numballs    fps
    250     98
    500     54
    750     36

Notice that sprites out of view has a fraction of the cost that visible sprites: they are discarded at the openGL geometry stage (in most of openGL implementations)

Particle Systems

Certain effects are best rendered as a big number of translucent, colored images, by example explosions and smoke. Cocos provides the base class ParticleSystem to efficiently render those entities, and some specialized subclasses in module cocos.particle_systems like Fireworks, Spiral, Meteor, Sun, Fire, Galaxy, Flower, Explosion, Smoke.

The most common method to customize particle systems is to subclass one of the cocos particle systems and modify some of the class members defining its behavior. If you change the particle texture remember to load it with pyglet.image.load, not pyglet.resource.image Of course, deeper customization can be achieved with subclassing and code customization.

Look at test/test_particle_*.py for sample code.

TextElement, Label, HTMLLabel, RichLabel

Overall opacity, Family and Font size can be specified for all.

TextElement: base class for all cocos text. Holds internally (member ‘element’) a suitable pyglet text object, providing the CocosNode interface and pyglet Batch to store parts. Features other than opacity and the common cocosnode ones should be accessed trough the instance.element.

Label: provide the most simple text display; color can also be set.

HTMLLabel: a slightly more powerful label, it can use mixed styles. Uses HTML syntax for text and styles; opacity as a whole can be set.

RichLabel: Allows rich text attributes.

Look for examples at test/test_label_*.py, test/test_htmllabel_*.py, test/test_rich_label.py

Creating your CocosNode subclasses

TODO: explain how to build custom cocosnode objects, how to use transform , etc.

CocosNodes Examples

Parent-Child examples

Building a scene, object instantiation omitted

# a scene with 2 layers; the background would be seen behind the game layer because of z values
scene.add( background_layer, z=0 )
scene.add( game_layer, z=1 )

# the layer with 1 bloody pacman sprite
blody_pacman_sprite.position = (100, 100)
game_layer.add( bloody_pacman_sprite )

# the bloody pacman has two daggers !!!
sprite.add( dagger_far_sprite, z=-1 )
sprite.add( dagger_near_sprite, z=1  )
# the draw order is dagger_far, bloody_pacman, dagger_near

# also they are zombie alien cowboys in the game,
# so we add a bunch of them to the game_layer
...

A dagger has touched a zombie alien cowboy so it wants to spawn a nasty blotch of green blood. We could add the blotch_sprite to the zac_sprite, an then the blotch will follow the zombie:

zac_zprite.add(blotch_sprite, z = 1)

or we could add to the game_layer, and the blotch will not follow the zombie:

layer = zac_sprite.parent # should be game_layer, because our staggering
layer.add(blotch_sprite)

If for some ridiculous reason a dagger needs to grab a reference to the game_layer we could use ‘get_ancestor’. Due to to way we staged the scene, we know game_layer is parent of bloody_pacman_sprite which is parent of the dagger. So:

layer = dagger_near.get_ancestor(Layer)

should produce game_layer.

A zombie is killed, we can retire from the scene with:

zac_sprite.kill()

or with:

game_layer.remove(zac_sprite)

or if we named it “T’chkss” when we added to the game_layer, like in:

game_layer.add(zac_sprite, name = "T'chkss", z=4)

then we can retire it with:

game_layer.remove("T'chkss")

Note that in current cocos if you add with name then you should remove by name, else the name will become unusable for new additions.

Spatial Placement snippets

Example:

# place the sprite in 320,240
sprite.position = (320,240)

or by using actions. Example:

# move the sprite to 320,240 in 5 seconds
sprite.do( MoveTo( (320,240, duration=2 ) )

Automated changes along the time snippets

You can modify attributes to any CocosNode object. By example:

scene.do( ScaleTo(2, duration=2) )

layer.do( RotateBy(360, duration=3) )

sprite.do( Blink(5, duration=1 ) )

label.do( JumpBy( (100,100), 50, 5, duration=3) )