A very basic Cocos2d tutorial, part 2¶
In this tutorial, we will add movement to all the henchmen. This means we will take a closer look at Actions and use them to produce different movements for different henchmen.
Actions are basically orders passed to a
CocosNode object, and they usually modify one or more of the object's attributes like
We have already used an Action in the previous tutorial; the Move action, which moves the target based on the parameters
gravity parameter is basically acceleration on the y axis. As such, you will usually want your gravity to be negative, so the character will fall down, not up.
Hide (hides the object),
Show (shows the object) and
Place ("teleports" the object to the specified position) are instant actions.
InstantActions aren't all that different from
IntervalActions - they just promise us that any changes that are performed on the target will be done in the
.start method and their methods
.stop do not do anything. This makes sure that
InstantActions remain "compatible" with
IntervalActions, meaning we can combine them in various ways.
Interval actions are actions that change the target's parameters over a fixed duration. For instance, the actions
MoveTo((50, 50), 10) would move the target to the position (50, 50) in 10 seconds.
Interval actions can also be degenerated with a duration of 0, practically turning them into instant actions. The player would not see a difference between
MoveTo((50, 50), 0) and `
Actions can easily be combined together to build a chain of subsequent actions. To chain actions together, you can use math operators + and *, which do exactly what you would expect them to - add and multiply actions.
Let's say that we want a character to move 50px to the right in one second and then 50px up in two seconds. We could do this with:
MoveBy((50, 0), 1) + MoveBy((0, 50), 2)
Now we want the character to move 50px to the right in half a second, wait for half a second and repeat those two actions five times. We can do this with:
(MoveBy((50, 0), 0.5) + Delay(0.5)) * 5
With combining actions like this, we can easily come up with fun and complex movements for our characters.
Sometimes you would want to repeat an action indefinitely - for instance, you might want to create a guard "patrolling" the hallway by moving from left to right, waiting a bit on each side. When we want to repeat an action indefinitely, we use the
We could create a patrolling guard like this:
Repeat(Delay(0.5) + MoveBy((200, 0), 2) + Delay(0.5) + MoveBy((-200, 0), 2))
Many times you might want to reverse an action. To do this, use the
Reverse action. If an action is complex (made out of more actions), the actions will be run from last to first, with each of the actions reversed.
For instance, the reverse of an action that would move our character 50px right, then 50px up, then wait 2 seconds would be an action that would first wait 2 seconds, then move 50px down and then 50px left.
movement = MoveBy((50, 0), 1) + MoveBy((0, 50), 1) + Delay(2) movement_reversed = Reverse(movement) movement_manual_reversed = Delay(2) + MoveBy((0, -50), 1) + MoveBy((-50, 0), 1)
movement_reversed and movement_manual_reversed do the exact same thing.
Adding movement to the NPC's¶
We will use a combination of actions to make the henchmen move. Since their positions are mirrored on the x axis, we will make each of the bottom, middle and top two henchmen have the same (but reversed) movement. All of them will repeat the same movement indefinitely.
from __future__ import division from cocos.actions import AccelDeccel from cocos.actions import Delay from cocos.actions import JumpBy from cocos.actions import Move from cocos.actions import MoveBy from cocos.actions import Repeat from cocos.actions import Reverse from cocos.actions import RotateBy from pyglet.window import key import cocos import resources class Game(cocos.layer.ColorLayer): is_event_handler = True def __init__(self): super(Game, self).__init__(102, 102, 225, 255) self.player = cocos.sprite.Sprite(resources.player) self.player.position = 400, 25 self.player.velocity = 0, 0 self.player.speed = 150 self.add(self.player, z=2) self.boss = cocos.sprite.Sprite(resources.boss) self.boss.position = 400, 600 self.boss.scale = 0.4 self.add(self.boss, z=1) self.batch = cocos.batch.BatchNode() self.enemies = [cocos.sprite.Sprite(resources.enemy) for i in range(6)] positions = ((250, 125), (550, 125), (300, 325), (500, 325), (150, 475), (650, 475)) for num, enem in enumerate(self.enemies): enem.position = positions[num] self.batch.add(enem) self.add(self.batch, z=1) self.player.do(Move()) move_basic = MoveBy((120, 0), 1) self.enemies.do(Repeat(move_basic + Reverse(move_basic))) self.enemies.do(Repeat(Reverse(move_basic) + move_basic)) move_complex = (MoveBy((-75, 75), 1) + Delay(0.5) + MoveBy((-75, -75), 1) + Delay(0.5) + MoveBy((75, -75), 1) + Delay(0.5) + MoveBy((75, 75), 1) + Delay(0.5)) self.enemies.do(Repeat(move_complex)) self.enemies.do(Repeat(Reverse(move_complex))) move_jump = AccelDeccel(JumpBy((200, 0), 75, 3, 3)) move_jump_rot = AccelDeccel(RotateBy(360, 3)) self.enemies.do(Repeat(move_jump + Reverse(move_jump))) self.enemies.do(Repeat(move_jump_rot + Reverse(move_jump_rot))) self.enemies.do(Repeat(Reverse(move_jump) + move_jump)) self.enemies.do(Repeat(Reverse(move_jump_rot) + move_jump_rot)) def on_key_press(self, symbol, modifiers): if symbol == key.LEFT: self.player.velocity = -self.player.speed, 0 elif symbol == key.RIGHT: self.player.velocity = self.player.speed, 0 elif symbol == key.UP: self.player.velocity = 0, self.player.speed elif symbol == key.DOWN: self.player.velocity = 0, -self.player.speed elif symbol == key.SPACE: self.player.velocity = 0, 0 if __name__ == '__main__': cocos.director.director.init( width=800, height=650, caption="Catch your husband!" ) game_layer = Game() game_scene = cocos.scene.Scene(game_layer) cocos.director.director.run(game_scene)
Bottom henchmen movement¶
move_basic = MoveBy((120, 0), 1) self.enemies.do(Repeat(move_basic + Reverse(move_basic))) self.enemies.do(Repeat(Reverse(move_basic) + move_basic))
The bottom two henchmen will move in a very simple way - they will just run 120px left and right.
Middle henchmen movement¶
move_complex = (MoveBy((-75, 75), 1) + Delay(0.5) + MoveBy((-75, -75), 1) + Delay(0.5) + MoveBy((75, -75), 1) + Delay(0.5) + MoveBy((75, 75), 1) + Delay(0.5)) self.enemies.do(Repeat(move_complex)) self.enemies.do(Repeat(Reverse(move_complex)))
The movement of the middle henchmen is a little more complex. They will move in a square, stopping for half a second on each of the square's vertices.
Top henchmen movement¶
move_jump = AccelDeccel(JumpBy((200, 0), 75, 3, 3)) move_jump_rot = AccelDeccel(RotateBy(360, 3)) self.enemies.do(Repeat(move_jump + Reverse(move_jump))) self.enemies.do(Repeat(move_jump_rot + Reverse(move_jump_rot))) self.enemies.do(Repeat(Reverse(move_jump) + move_jump)) self.enemies.do(Repeat(Reverse(move_jump_rot) + move_jump_rot))
Here, we want to show off some of the more fancy movements. We will also run two actions at the same time on each henchmen, making them jump around while rotating.
We use a few actions that we haven't seen before here.
The first one is
JumpBy, which makes the sprite jump around. We can specify by how much we want to move, the height and number of jumps and the duration. We used
JumpBy((200, 0), 75, 3, 3), which makes our henchman jump 200px to the right, jump to the height of 75px and jump three times, all of this in 3 seconds.
The second new action we use is
RotateBy, which rotates by an angle in degrees (clockwise, if you want to rotate counter-clockwise, pass a negative angle) in the specified duration. In our case, we rotate a full circle clockwise in 3 seconds.
Both actions are wrapped in the
AccelDeccel which can be applied to any action to change the movement so it is not linear any more, but rather starts slow, gets fast in the middle and ends slow.
We then run two actions on each henchmen - one for jumping and one for rotating. Since they both have the same duration, they will be merge to look like a single action. Of course, the duration can be different, creating a mix of different actions.
What comes next?¶
With Actions, we created a dynamic game, but we still do not have collision detection, which will finally turn our application into a real game that can be won or lost. As you can probably guess, this is exactly what we will do in the next tutorial.
This tutorial is part of a chain of tutorials:
- Pyglet - basic tutorial
- Cocos2d - basic tutorial, part 1
- Cocos2d - basic tutorial, part 2 - current
- Cocos2d - basic tutorial, part 3
Previous tutorial: Cocos2d - basic tutorial, part 1 Next tutorial: Cocos2d - basic tutorial, part 3
blog comments powered by Disqus