Designing a multiplayer game with Python and cocos2d, part 1 -- Getting started
This is the first of a series of tutorials on game development using Python and cocos2d. I'm writing these as I learn new concepts, with the hope that documenting and sharing my progress will help others.
This tutorial set will guide you from getting started with cocos2d to building a full-fledged multiplayer game. Importantly, the tutorials assume you already have a working knowledge of basic programming concepts and familiarity with the Python environment.
To get started, install the cocos2d package by running easy_install cocos2d or pip install cocos2d --process-dependency-links You can also download cocos2d from pypi and install from source by running setup.py install This will download all docs and code samples as well. More details on installation can be found in the cocos2d installation guide.
You should also download the code associated with this tutorial here, or clone the repository by running git clone https://github.com/jpwright/cocos2d-python-tutorials.git.
2. First program -- directional movement
To start, we're going to create an environment where you can control a player using the arrow keys. The output will look something like this:
The sprites for this part of the tutorial were lovingly borrowed from the amazing game Faster Than Light, which I highly recommend you go buy right now.
If you navigate to the "01 Getting Started" directory of the tutorial code and start the game by running python main.py, you should see this window appear. You should be able to move the sprite in any direction using your arrow keys. If not, check that your cocos2d installation completed successfully. If you have other issues, let me know by filing an issue.
So how does this work?
The first step is importing dependencies. At the top of main.py you'll see the following lines:
from pyglet.window import key
from cocos import actions, layer, sprite, scene
from cocos.director import director
Next, we'll initialize the director and create a scene. The director object initializes the game's main window and allows you to transition between different scenes. The scene is any part of the game's flow. For example, you might have separate scenes for different levels/cutscenes/menus of your game. In this example, we'll only use one scene.
To initialize the director: director.init(width=500, height=300, do_not_scale=True, resizable=True) The arguments allow you to adjust the initial width and height of the game's window. do_not_scale, when set to its default value of False, results in the game scaling up all coordinates and graphics when the window is resized. If you want your game to support different screen resolutions, do_not_scale will need to be set to True.
Next, we'll create a layer and add a sprite to it:
player_layer = layer.Layer()
me = sprite.Sprite('human-female.png')
player_layer.add(me) Having multiple layers makes it easier to separate different elements of the game. For example, menus and dialogs will typically need to be on the highest layer, while maps and backgrounds need to be on the lowest layer.
When initializing a Sprite object, you must include a path to its default image. This image is a property of the Sprite which can be altered later.
In order to control the movement of the sprite, we'll use the actions.Move module. This class allows you to set any of an object's position, velocity, acceleration, gravity, rotation, angular velocity, and angular acceleration, and cocos2d will update the sprite's rendering automatically based on those values.
To start off, we'll initialize our sprite's position and velocity:
me.position = (100, 100)
me.velocity = (0, 0)
In order to do anything with the sprite, we'll need a way of capturing keyboard input, and a class that will allow us to translate that keyboard input into changes in the sprite's position, velocity, etc. We'll use pyglet (upon which cocos2d is built) for its KeyStateHandler behavior, and attach that to our director:
keyboard = key.KeyStateHandler()
Next, we'll create a class for the player's movement:
def step(self, dt):
super(Me, self).step(dt) # Run step function on the parent class.
# Determine velocity based on keyboard inputs.
velocity_x = 100 * (keyboard[key.RIGHT] - keyboard[key.LEFT])
velocity_y = 100 * (keyboard[key.UP] - keyboard[key.DOWN])
# Set the object's velocity.
self.target.velocity = (velocity_x, velocity_y) When defining the class, we give it the base class of actions.Move. We then define the step function, which is called on every frame, and which includes an argument dt which is equal to the number of seconds since the last call.
Here, we're using the value of keyboard to calculate the sprite's velocity, and storing it at self.target.velocity. target refers to the sprite object that we'll bind this class to.
Note that the keyboard object must be global so that it can be accessed from within the class.
Back to our main function, we'll assign this new class Me to our sprite by using the do function: me.do(Me())
That's right, sprite, you do you and I'll do me. All that's left is to create a scene, attach our layer, and feed that scene to the director:
main_scene = scene.Scene(player_layer)
You're done! You now have a window (controlled by your director object), displaying a scene (stored at main_scene), which contains a single layer (stored at player_layer), containing a single sprite object (stored at me), which is constantly acting based on how you defined your Me class.
The next tutorial will explain how to create a map for our player to walk around in, including a scrollable tileset!
3. Full code