Contents [show/hide]
Source [show/hide]
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¶
Actions are basically orders passed to a CocosNode
object, and they usually modify one or more of the object's attributes like position
, rotation
, scale
etc.
Actions are split in two basic types - actions that are instant (InstantActions) and actions that modify the attributes over a period of time (IntervalActions).
We have already used an Action in the previous tutorial; the Move action, which moves the target based on the parameters velocity
, acceleration
and gravity
. The 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.
Instant actions¶
Actions like Hide
(hides the object), Show
(shows the object) and Place
("teleports" the object to the specified position) are instant actions.
Programmatically speaking, 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 .step
, .update
and .stop
do not do anything. This makes sure that InstantActions
remain "compatible" with IntervalActions
, meaning we can combine them in various ways.
Interval actions¶
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 `Place((50, 50))
.
Combining actions¶
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.
Repeating actions¶
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 Repeat
action.
We could create a patrolling guard like this:
Repeat(Delay(0.5) + MoveBy((200, 0), 2) + Delay(0.5) + MoveBy((-200, 0), 2))
Reversing actions¶
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.
Full code¶
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[0].do(Repeat(move_basic + Reverse(move_basic)))
self.enemies[1].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[2].do(Repeat(move_complex))
self.enemies[3].do(Repeat(Reverse(move_complex)))
move_jump = AccelDeccel(JumpBy((200, 0), 75, 3, 3))
move_jump_rot = AccelDeccel(RotateBy(360, 3))
self.enemies[4].do(Repeat(move_jump + Reverse(move_jump)))
self.enemies[4].do(Repeat(move_jump_rot + Reverse(move_jump_rot)))
self.enemies[5].do(Repeat(Reverse(move_jump) + move_jump))
self.enemies[5].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[0].do(Repeat(move_basic + Reverse(move_basic)))
self.enemies[1].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[2].do(Repeat(move_complex))
self.enemies[3].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[4].do(Repeat(move_jump + Reverse(move_jump)))
self.enemies[4].do(Repeat(move_jump_rot + Reverse(move_jump_rot)))
self.enemies[5].do(Repeat(Reverse(move_jump) + move_jump))
self.enemies[5].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.
Related tutorials
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