Introduction To Game Programming EuroPython 2010 Richard Jones
The Basics
The Basics • Displaying something
The Basics • Displaying something • Controlling animation
The Basics • Displaying something • Controlling animation • User input
The Basics • Displaying something • Controlling animation • User input • Gameplay mechanics
The Basics • Displaying something • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music
The Basics • Displaying something • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music • Game architecture
Displaying Something
Displaying Something • Opening a window
Displaying Something • Opening a window • Reading images
Displaying Something • Opening a window • Reading images • Displaying images
Displaying Something • Opening a window • Reading images • Displaying images • Organising the display of images (scene composition)
Sky fixed
Distance parallax=.5
Midfield parallax=.8
Foreground 1
Foreground 2
Game Triggers
Opening a Window import cocos from cocos.director import director director.init() scene = cocos.scene.Scene() director.run(scene)
Drawing import cocos from cocos.director import director director.init() scene = cocos.scene.Scene() sprite = cocos.sprite.Sprite('kitten.jpg') scene.add(sprite) director.run(scene)
Anchoring (256, 256) 512 px 512 px
Anchoring (256, 256)
Anchoring (0,0)
Anchoring import cocos from cocos.director import director director.init() scene = cocos.scene.Scene() sprite = cocos.sprite.Sprite('kitten.jpg', anchor=(0,0)) scene.add(sprite) director.run(scene)
Using a Layer import cocos from cocos.director import director director.init() scene = cocos.scene.Scene() layer = cocos.layer.Layer() scene.add(layer) sprite = cocos.sprite.Sprite('kitten.jpg', anchor=(0,0)) layer.add(sprite) director.run(scene)
Animation import cocos from cocos import actions from cocos.director import director ... scene = cocos.scene.Scene() layer = cocos.layer.Layer() scene.add(layer) sprite = cocos.sprite.Sprite('kitten.jpg', anchor=(0,0)) layer.add(sprite) sprite.do(actions.MoveBy((100, 0))) director.run(scene)
Getting Organised import cocos from cocos import actions from cocos.director import director class AsteroidsGame(cocos.scene.Scene): def __init__(self): super(AsteroidsGame, self).__init__() self.player = cocos.layer.Layer() self.add(self.player) self.ship = cocos.sprite.Sprite('data/ship.png', (width/2, height/2)) velocity = (100, 0) self.ship.do(actions.MoveBy(velocity)) self.player.add(self.ship) director.init() width, height = director.get_window_size() director.run(AsteroidsGame())
Adding Asteroids import random import cocos from cocos import actions from cocos.director import director def create_asteroid(layer, size, position, speed): '''Create an asteroid of a certain size and speed. ''' s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size layer.add(s) velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) dr = random.randint(-speed, speed) s.do(actions.Repeat(actions.MoveBy(velocity, 1) | actions.RotateBy(dr, 1))) ...
Adding Asteroids ... super(AsteroidsGame, self).__init__() # place the asteroids in a layer by themselves self.asteroids = cocos.layer.Layer() self.add(self.asteroids) for i in range(3): # place asteroids at random positions around the screen position = (random.randint(0, width), random.randint(0, height)) create_asteroid(self.asteroids, 'big', position, 100) # place the player ship in another layer ...
Better action ... s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size layer.add(s) s.velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) s.dr = random.randint(-speed, speed) s.do(actions.Move()) ...
Screen Wrapping ... s.velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) s.dr = random.randint(-speed, speed) s.do(actions.WrappedMove(width, height)) ... self.ship.velocity = (100, 0) # move the ship kinematically and wrapped to the screen size self.ship.do(actions.WrappedMove(width, height)) self.player.add(self.ship) ...
Gameplay Mechanics
Gameplay Mechanics • Player Controls
Gameplay Mechanics • Player Controls • Timed rules (when objects appear; moving the play field)
Gameplay Mechanics • Player Controls • Timed rules (when objects appear; moving the play field) • Interaction of game objects
Gameplay Mechanics • Player Controls • Timed rules (when objects appear; moving the play field) • Interaction of game objects • Detecting important events (game won or game over)
User Input
User Input • Getting events
User Input • Getting events • Distinct keyboard events vs. keyboard state
Control import math import random from pyglet.window import key import cocos from cocos import actions from cocos.director import director
Control y = speed x sin(rotation) ng) ho oti rs n, o er atio l a cce d (or s pee rotation x = speed x cos(rotation)
Control x = speed x cos(rotation) y = speed x sin(-rotation) rotation spe ed (or acc ele rat ion , or sho oti ng)
Control class MoveShip(actions.WrappedMove): def step(self, dt): super(MoveShip, self).step(dt) # set the rotation using the left and right arrow keys self.target.dr = (keys[key.RIGHT] - keys[key.LEFT]) * 360 # figure the x and y heading components from the rotation rotation = math.radians(self.target.rotation) rotation_x = math.cos(rotation) rotation_y = math.sin(-rotation) # accelerate along the heading acc = keys[key.UP] * 200 self.target.acceleration = (acc * rotation_x, acc * rotation_y)
Control ... self.ship = cocos.sprite.Sprite('data/ship.png', (width/2, height/2)) self.ship.velocity = (0, 0) # animate the ship with our custom action self. ship.do(MoveShip(width, height)) self.player.add(self. ship) ...
Control ... # open the window, get dimensions, watch the keyboard and run our scene director.init() width, height = director.get_window_size() keys = key.KeyStateHandler() director.window.push_handlers(keys) director.run(AsteriodsGame())
Detecting Collisions
Detecting Collisions • Pixel-Perfect
Detecting Collisions • Pixel-Perfect • Axis-Aligned Bounding Box
Detecting Collisions • Pixel-Perfect • Axis-Aligned Bounding Box • Circle-Circle
Detecting Collisions • Pixel-Perfect • Axis-Aligned Bounding Box • Circle-Circle • Hash Map
Detecting Collisions Pixel-Perfect
Detecting Collisions Axis-Aligned Bounding Box
Detecting Collisions Axis-Aligned Bounding Box
Detecting Collisions Axis-Aligned Bounding Box
Detecting Collisions Circle-Circle
Detecting Collisions Circle-Circle
Detecting Collisions Circle-Circle
Detecting Collisions Circle-Circle
Detecting Collisions Hash Map d = {(42,42): [ship], (43, 42): [ship], (44, 42): ship, ... (52, 45): [asteroid], (53, 45): [asteroid], ...}
Collision Detection
Collision Detection
Collision Detection
Collision Detection
Collision Detection distance
... Collision self.target.acceleration = (acc * rotation_x, acc * rotation_y) def collide(a, b): '''Determine whether two objects with a center point and width (diameter) are colliding.''' distance = math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2) return distance < (a.width/2 + b.width/2) class MoveAsteroid(actions.WrappedMove): def step(self, dt): super(MoveAsteroid, self).step(dt) # detect collision between this asteroid and the ship if collide(self.target, director.scene.ship): quit('GAME OVER') def create_asteroid(layer, size, position, speed): ...
Collision def create_asteroid(layer, size, position, speed): '''Create an asteroid of a certain size and speed. ''' s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size layer.add(s) s.velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) s.dr = random.randint(-speed, speed) s.do(MoveAsteroid(width, height)) ...
Shooting ... self.target.acceleration = (acc * rotation_x, acc * rotation_y) if self.target.gun_cooldown: # still limiting the gun rate of fire self.target.gun_cooldown = max(0, self.target.gun_cooldown - dt) elif keys[key.SPACE]: # fire a bullet from the ship b = cocos.sprite.Sprite('data/bullet.png', (self.target.x, self.target.y)) # send it in the same heading as the ship b.velocity = (rotation_x * 400, rotation_y * 400) # the bullet has a lifespan of 1 second b.life = 1 director.scene.player.add(b) # move the bullet with its custom action b.do(MoveBullet(width, height)) # ship may only shoot twice per second self.target.gun_cooldown = .5 ...
Shooting class MoveBullet(actions.WrappedMove): def step(self, dt): super(MoveBullet, self).step(dt) # age the bullet self.target.life -= dt if self.target.life < 0: # remove from play if it's too old self.target.kill() return # see if the bullet hits any asteroids for asteroid in director.scene.asteroids.get_children(): if collide(self.target, asteroid): # remove the bullet and asteroid self.target.kill() asteroid.kill() return
... and winning ... super(MoveShip, self).step(dt) # if there's no asteroids left then the player has won if not director.scene.asteroids.children: quit('YOU WIN') # set the rotation using the left and right arrow keys self.target.dr = (keys[key.RIGHT] - keys[key.LEFT]) * 360 ...
Creating chunks def create_asteroid(layer, size, position, velocity, rotation, speed): '''Create an asteroid of a certain size, possibly inheriting its parent's velocity and rotation. ''' s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size dx, dy = velocity s.velocity = (dx + random.randint(-speed, speed), dy + random.randint(-speed, speed)) s.dr = rotation + random.randint(-speed, speed) layer.add(s) s.do(MoveAsteroid(width, height))
Creating chunks for asteroid in director.scene.asteroids.get_children(): if collide(self.target, asteroid): # remove the bullet and asteroid, create new smaller # asteroid self.target.kill() create_smaller_asteroids(asteroid) asteroid.kill() return
Creating chunks ... position = (random.randint(0, width), random.randint(0, height)) create_asteroid(self.asteroids, 'big', position, (0, 0), 0, 100) def create_smaller_asteroids(asteroid): asteroids = director.scene.asteroids if asteroid.size == 'big': # if it's a big asteroid then make two medium asteroids in # its place for i in range(2): create_asteroid(asteroids, 'medium', asteroid.position, asteroid.velocity, asteroid.dr, 50) elif asteroid.size == 'medium': # if it's a medium asteroid then make two small asteroids in # its place for i in range(2): create_asteroid(asteroids, 'small', asteroid.position, asteroid.velocity, asteroid.dr, 50)
Sound
Sound • Reading sound files
Sound • Reading sound files • Playing sounds and background music
Sound Effects import math import random import pyglet from pyglet.window import key import cocos from cocos import actions from cocos.director import director # load our sound effects explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False) bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False) ...
Sound Effects ... # ship may only shoot twice per second self.target.gun_cooldown = .5 bullet_sound.play() ...
Sound Effects ... create_smaller_asteroids(asteroid) asteroid.kill() explosion_sound.play() return
Game Architecture
Game Architecture • Player lives
Game Architecture • Player lives • Game won / game over screen
Game Architecture • Player lives • Game won / game over screen • Opening screen
Game Architecture • Player lives • Game won / game over screen • Opening screen • Options menu
Invulnerability class Ship(cocos.sprite.Sprite): gun_cooldown = 0 velocity = (0, 0) is_invulnerable = False def set_invulnerable(self): self.do(actions.Blink(10, 1) + actions.CallFunc(self.set_vulnerable)) self.is_invulnerable = True def set_vulnerable(self): self.is_invulnerable = False
Invulnerability self.player = cocos.layer.Layer() self.add(self.player) self.ship = Ship('data/ship.png', (width/2, height/2)) self.player.add(self.ship) # animate the ship with our custom action self.ship.do(MoveShip(width, height)) self.ship.set_invulnerable()
Invulnerability super(MoveAsteroid, self).step(dt) if director.scene.ship.is_invulnerable: return # detect collision between this asteroid and the ship if collide(self.target, director.scene.ship): quit('GAME OVER')
Lives self.ship.set_invulnerable() self.player.add(self.ship) # another layer for the "HUD" or front informational display self.hud = cocos.layer.Layer() self.add(self.hud) self.lives = cocos.text.Label('Lives: 3', font_size=20, y=height, anchor_y='top') self.lives.counter = 3 self.hud.add(self.lives)
Lives # detect collision between this asteroid and the ship if collide(self.target, director.scene.ship): lives = director.scene.lives lives.counter -= 1 director.scene.ship.set_invulnerable() if not lives.counter: quit('GAME OVER') else: lives.element.text = 'Lives: %d' % lives.counter
Scenes class MessageScene(cocos.scene.Scene): def __init__(self, text): super(MessageScene, self).__init__() class UserActivity(cocos.layer.Layer): is_event_handler = True def on_mouse_press(*args): director.pop() def on_text(*args): director.pop() layer = UserActivity() self.add(layer) t = cocos.text.Label(text, font_size=40, x=width/2, anchor_x='center', y=height/2, anchor_y='center') layer.add(t)
Scenes # if there's no asteroids left then the player has won if not director.scene.asteroids.children: director.replace(MessageScene('You Win')) # set the rotation using the left and right arrow keys self.target.dr = (keys[key.RIGHT] - keys[key.LEFT]) * 360 ... lives.counter -= 1 director.scene.ship.set_invulnerable() if not lives.counter: director.replace(MessageScene('Game Over')) else: lives.element.text = 'Lives: %d' % lives.counter
Menu class MenuScene(cocos.scene.Scene): def __init__(self): super(MenuScene, self).__init__() # opening menu menu = cocos.menu.Menu("Asteroids!!!!") menu.create_menu([ cocos.menu.MenuItem('Play', lambda: director.push(AsteroidsGame())), cocos.menu.MenuItem('Quit', pyglet.app.exit), ]) menu.on_quit = pyglet.app.exit self.add(menu) # open the window, get dimensions, watch the keyboard and run our scene director.init() width, height = director.get_window_size() keys = key.KeyStateHandler() director.window.push_handlers(keys) director.run(MenuScene())
Special Effects
Special Effects • Simple image animations
Special Effects • Simple image animations • Use libraries like lepton
Controlling Animation
Controlling Animation • Image position on screen
Controlling Animation • Image position on screen • Updates over time
Controlling Animation • Image position on screen • Updates over time • Accounting for frame rate
Controlling Animation • Image position on screen • Updates over time • Accounting for frame rate • Frame to use from multiple images
Animation from multiple images
Animation from multiple images
Special Effects
Special Effects ... explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False) bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False) # load our explosion animation explosion = pyglet.image.load('data/explosion.png') explosion_grid = pyglet.image.ImageGrid(explosion, 2, 8) explosion = explosion_grid.get_animation(.05, False) ...
Special Effects # remove the bullet and asteroid, create new smaller # asteroid self.target.kill() s = cocos.sprite.Sprite(explosion, self.target.position) s.velocity = asteroid.velocity s.do(actions.WrappedMove(width, height) | (actions.Delay(.05 * 16) + actions.CallFuncS(lambda s: s.kill()))) self.target.parent.add(s) create_smaller_asteroids(asteroid) asteroid.kill()
Other Game Types • minesweeper • car driving • platformer • tower defence
Minesweeper • rendering a grid of cells • detecting mouse clicks on cells • exposing contents
Car Driving • different movement model • detecting correct circuit • detecting collision with edge of track (pixel-based collision detection)
Platformer • rendering game layers • handling triggers • platformer physics
Tower Defence • tower placement (selection & position) • movement solution for creeps
Other Things • kytten GUI controls for pyglet / cocos2d • cocograph map editing
Where To From Here? • http://los-cocos.org • http://pyglet.org • http://pygame.org • http://inventwithpython.com/ • http://pyweek.org

Intro to Game Programming

  • 1.
    Introduction To Game Programming EuroPython 2010 Richard Jones
  • 2.
  • 3.
  • 4.
    The Basics • Displayingsomething • Controlling animation
  • 5.
    The Basics • Displayingsomething • Controlling animation • User input
  • 6.
    The Basics • Displayingsomething • Controlling animation • User input • Gameplay mechanics
  • 7.
    The Basics • Displayingsomething • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music
  • 8.
    The Basics • Displayingsomething • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music • Game architecture
  • 9.
  • 10.
  • 11.
    Displaying Something • Openinga window • Reading images
  • 12.
    Displaying Something • Openinga window • Reading images • Displaying images
  • 13.
    Displaying Something • Openinga window • Reading images • Displaying images • Organising the display of images (scene composition)
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
    Opening a Window import cocos from cocos.director import director director.init() scene = cocos.scene.Scene() director.run(scene)
  • 21.
    Drawing import cocos from cocos.directorimport director director.init() scene = cocos.scene.Scene() sprite = cocos.sprite.Sprite('kitten.jpg') scene.add(sprite) director.run(scene)
  • 22.
    Anchoring (256, 256) 512 px 512 px
  • 23.
  • 24.
  • 25.
    Anchoring import cocos from cocos.directorimport director director.init() scene = cocos.scene.Scene() sprite = cocos.sprite.Sprite('kitten.jpg', anchor=(0,0)) scene.add(sprite) director.run(scene)
  • 26.
    Using a Layer importcocos from cocos.director import director director.init() scene = cocos.scene.Scene() layer = cocos.layer.Layer() scene.add(layer) sprite = cocos.sprite.Sprite('kitten.jpg', anchor=(0,0)) layer.add(sprite) director.run(scene)
  • 27.
    Animation import cocos from cocosimport actions from cocos.director import director ... scene = cocos.scene.Scene() layer = cocos.layer.Layer() scene.add(layer) sprite = cocos.sprite.Sprite('kitten.jpg', anchor=(0,0)) layer.add(sprite) sprite.do(actions.MoveBy((100, 0))) director.run(scene)
  • 28.
    Getting Organised import cocos fromcocos import actions from cocos.director import director class AsteroidsGame(cocos.scene.Scene): def __init__(self): super(AsteroidsGame, self).__init__() self.player = cocos.layer.Layer() self.add(self.player) self.ship = cocos.sprite.Sprite('data/ship.png', (width/2, height/2)) velocity = (100, 0) self.ship.do(actions.MoveBy(velocity)) self.player.add(self.ship) director.init() width, height = director.get_window_size() director.run(AsteroidsGame())
  • 29.
    Adding Asteroids import random importcocos from cocos import actions from cocos.director import director def create_asteroid(layer, size, position, speed): '''Create an asteroid of a certain size and speed. ''' s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size layer.add(s) velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) dr = random.randint(-speed, speed) s.do(actions.Repeat(actions.MoveBy(velocity, 1) | actions.RotateBy(dr, 1))) ...
  • 30.
    Adding Asteroids ... super(AsteroidsGame, self).__init__() #place the asteroids in a layer by themselves self.asteroids = cocos.layer.Layer() self.add(self.asteroids) for i in range(3): # place asteroids at random positions around the screen position = (random.randint(0, width), random.randint(0, height)) create_asteroid(self.asteroids, 'big', position, 100) # place the player ship in another layer ...
  • 31.
    Better action ... s =cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size layer.add(s) s.velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) s.dr = random.randint(-speed, speed) s.do(actions.Move()) ...
  • 32.
    Screen Wrapping ... s.velocity =(random.randint(-speed, speed), random.randint(-speed, speed)) s.dr = random.randint(-speed, speed) s.do(actions.WrappedMove(width, height)) ... self.ship.velocity = (100, 0) # move the ship kinematically and wrapped to the screen size self.ship.do(actions.WrappedMove(width, height)) self.player.add(self.ship) ...
  • 33.
  • 34.
  • 35.
    Gameplay Mechanics • PlayerControls • Timed rules (when objects appear; moving the play field)
  • 36.
    Gameplay Mechanics • PlayerControls • Timed rules (when objects appear; moving the play field) • Interaction of game objects
  • 37.
    Gameplay Mechanics • PlayerControls • Timed rules (when objects appear; moving the play field) • Interaction of game objects • Detecting important events (game won or game over)
  • 38.
  • 39.
  • 40.
    User Input • Gettingevents • Distinct keyboard events vs. keyboard state
  • 41.
    Control import math import random frompyglet.window import key import cocos from cocos import actions from cocos.director import director
  • 42.
    Control y = speed x sin(rotation) ng) ho oti rs n, o er atio l a cce d (or s pee rotation x = speed x cos(rotation)
  • 43.
    Control x = speed x cos(rotation) y = speed x sin(-rotation) rotation spe ed (or acc ele rat ion , or sho oti ng)
  • 44.
    Control class MoveShip(actions.WrappedMove): def step(self, dt): super(MoveShip, self).step(dt) # set the rotation using the left and right arrow keys self.target.dr = (keys[key.RIGHT] - keys[key.LEFT]) * 360 # figure the x and y heading components from the rotation rotation = math.radians(self.target.rotation) rotation_x = math.cos(rotation) rotation_y = math.sin(-rotation) # accelerate along the heading acc = keys[key.UP] * 200 self.target.acceleration = (acc * rotation_x, acc * rotation_y)
  • 45.
    Control ... self.ship = cocos.sprite.Sprite('data/ship.png',(width/2, height/2)) self.ship.velocity = (0, 0) # animate the ship with our custom action self. ship.do(MoveShip(width, height)) self.player.add(self. ship) ...
  • 46.
    Control ... # open thewindow, get dimensions, watch the keyboard and run our scene director.init() width, height = director.get_window_size() keys = key.KeyStateHandler() director.window.push_handlers(keys) director.run(AsteriodsGame())
  • 47.
  • 48.
  • 49.
  • 50.
    Detecting Collisions • Pixel-Perfect •Axis-Aligned Bounding Box • Circle-Circle
  • 51.
    Detecting Collisions • Pixel-Perfect •Axis-Aligned Bounding Box • Circle-Circle • Hash Map
  • 52.
    Detecting Collisions Pixel-Perfect
  • 53.
    Detecting Collisions Axis-Aligned Bounding Box
  • 54.
    Detecting Collisions Axis-Aligned Bounding Box
  • 55.
    Detecting Collisions Axis-Aligned Bounding Box
  • 56.
    Detecting Collisions Circle-Circle
  • 57.
    Detecting Collisions Circle-Circle
  • 58.
    Detecting Collisions Circle-Circle
  • 59.
    Detecting Collisions Circle-Circle
  • 60.
    Detecting Collisions Hash Map d = {(42,42): [ship], (43, 42): [ship], (44, 42): ship, ... (52, 45): [asteroid], (53, 45): [asteroid], ...}
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
    ... Collision self.target.acceleration = (acc * rotation_x, acc * rotation_y) def collide(a, b): '''Determine whether two objects with a center point and width (diameter) are colliding.''' distance = math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2) return distance < (a.width/2 + b.width/2) class MoveAsteroid(actions.WrappedMove): def step(self, dt): super(MoveAsteroid, self).step(dt) # detect collision between this asteroid and the ship if collide(self.target, director.scene.ship): quit('GAME OVER') def create_asteroid(layer, size, position, speed): ...
  • 67.
    Collision def create_asteroid(layer, size,position, speed): '''Create an asteroid of a certain size and speed. ''' s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size layer.add(s) s.velocity = (random.randint(-speed, speed), random.randint(-speed, speed)) s.dr = random.randint(-speed, speed) s.do(MoveAsteroid(width, height)) ...
  • 68.
    Shooting ... self.target.acceleration = (acc * rotation_x, acc * rotation_y) if self.target.gun_cooldown: # still limiting the gun rate of fire self.target.gun_cooldown = max(0, self.target.gun_cooldown - dt) elif keys[key.SPACE]: # fire a bullet from the ship b = cocos.sprite.Sprite('data/bullet.png', (self.target.x, self.target.y)) # send it in the same heading as the ship b.velocity = (rotation_x * 400, rotation_y * 400) # the bullet has a lifespan of 1 second b.life = 1 director.scene.player.add(b) # move the bullet with its custom action b.do(MoveBullet(width, height)) # ship may only shoot twice per second self.target.gun_cooldown = .5 ...
  • 69.
    Shooting class MoveBullet(actions.WrappedMove): def step(self, dt): super(MoveBullet, self).step(dt) # age the bullet self.target.life -= dt if self.target.life < 0: # remove from play if it's too old self.target.kill() return # see if the bullet hits any asteroids for asteroid in director.scene.asteroids.get_children(): if collide(self.target, asteroid): # remove the bullet and asteroid self.target.kill() asteroid.kill() return
  • 70.
    ... and winning ... super(MoveShip,self).step(dt) # if there's no asteroids left then the player has won if not director.scene.asteroids.children: quit('YOU WIN') # set the rotation using the left and right arrow keys self.target.dr = (keys[key.RIGHT] - keys[key.LEFT]) * 360 ...
  • 71.
    Creating chunks def create_asteroid(layer,size, position, velocity, rotation, speed): '''Create an asteroid of a certain size, possibly inheriting its parent's velocity and rotation. ''' s = cocos.sprite.Sprite('data/%s_asteroid.png' % size, position) s.size = size dx, dy = velocity s.velocity = (dx + random.randint(-speed, speed), dy + random.randint(-speed, speed)) s.dr = rotation + random.randint(-speed, speed) layer.add(s) s.do(MoveAsteroid(width, height))
  • 72.
    Creating chunks for asteroidin director.scene.asteroids.get_children(): if collide(self.target, asteroid): # remove the bullet and asteroid, create new smaller # asteroid self.target.kill() create_smaller_asteroids(asteroid) asteroid.kill() return
  • 73.
    Creating chunks ... position = (random.randint(0, width), random.randint(0, height)) create_asteroid(self.asteroids, 'big', position, (0, 0), 0, 100) def create_smaller_asteroids(asteroid): asteroids = director.scene.asteroids if asteroid.size == 'big': # if it's a big asteroid then make two medium asteroids in # its place for i in range(2): create_asteroid(asteroids, 'medium', asteroid.position, asteroid.velocity, asteroid.dr, 50) elif asteroid.size == 'medium': # if it's a medium asteroid then make two small asteroids in # its place for i in range(2): create_asteroid(asteroids, 'small', asteroid.position, asteroid.velocity, asteroid.dr, 50)
  • 74.
  • 75.
  • 76.
    Sound • Reading soundfiles • Playing sounds and background music
  • 77.
    Sound Effects import math importrandom import pyglet from pyglet.window import key import cocos from cocos import actions from cocos.director import director # load our sound effects explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False) bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False) ...
  • 78.
    Sound Effects ... # ship may only shoot twice per second self.target.gun_cooldown = .5 bullet_sound.play() ...
  • 79.
    Sound Effects ... create_smaller_asteroids(asteroid) asteroid.kill() explosion_sound.play() return
  • 80.
  • 81.
  • 82.
    Game Architecture • Playerlives • Game won / game over screen
  • 83.
    Game Architecture • Playerlives • Game won / game over screen • Opening screen
  • 84.
    Game Architecture • Playerlives • Game won / game over screen • Opening screen • Options menu
  • 85.
    Invulnerability class Ship(cocos.sprite.Sprite): gun_cooldown = 0 velocity = (0, 0) is_invulnerable = False def set_invulnerable(self): self.do(actions.Blink(10, 1) + actions.CallFunc(self.set_vulnerable)) self.is_invulnerable = True def set_vulnerable(self): self.is_invulnerable = False
  • 86.
    Invulnerability self.player = cocos.layer.Layer() self.add(self.player) self.ship= Ship('data/ship.png', (width/2, height/2)) self.player.add(self.ship) # animate the ship with our custom action self.ship.do(MoveShip(width, height)) self.ship.set_invulnerable()
  • 87.
    Invulnerability super(MoveAsteroid, self).step(dt) if director.scene.ship.is_invulnerable: return # detect collision between this asteroid and the ship if collide(self.target, director.scene.ship): quit('GAME OVER')
  • 88.
    Lives self.ship.set_invulnerable() self.player.add(self.ship) #another layer for the "HUD" or front informational display self.hud = cocos.layer.Layer() self.add(self.hud) self.lives = cocos.text.Label('Lives: 3', font_size=20, y=height, anchor_y='top') self.lives.counter = 3 self.hud.add(self.lives)
  • 89.
    Lives # detect collisionbetween this asteroid and the ship if collide(self.target, director.scene.ship): lives = director.scene.lives lives.counter -= 1 director.scene.ship.set_invulnerable() if not lives.counter: quit('GAME OVER') else: lives.element.text = 'Lives: %d' % lives.counter
  • 90.
    Scenes class MessageScene(cocos.scene.Scene): def __init__(self, text): super(MessageScene, self).__init__() class UserActivity(cocos.layer.Layer): is_event_handler = True def on_mouse_press(*args): director.pop() def on_text(*args): director.pop() layer = UserActivity() self.add(layer) t = cocos.text.Label(text, font_size=40, x=width/2, anchor_x='center', y=height/2, anchor_y='center') layer.add(t)
  • 91.
    Scenes # if there's no asteroids left then the player has won if not director.scene.asteroids.children: director.replace(MessageScene('You Win')) # set the rotation using the left and right arrow keys self.target.dr = (keys[key.RIGHT] - keys[key.LEFT]) * 360 ... lives.counter -= 1 director.scene.ship.set_invulnerable() if not lives.counter: director.replace(MessageScene('Game Over')) else: lives.element.text = 'Lives: %d' % lives.counter
  • 92.
    Menu class MenuScene(cocos.scene.Scene): def __init__(self): super(MenuScene, self).__init__() # opening menu menu = cocos.menu.Menu("Asteroids!!!!") menu.create_menu([ cocos.menu.MenuItem('Play', lambda: director.push(AsteroidsGame())), cocos.menu.MenuItem('Quit', pyglet.app.exit), ]) menu.on_quit = pyglet.app.exit self.add(menu) # open the window, get dimensions, watch the keyboard and run our scene director.init() width, height = director.get_window_size() keys = key.KeyStateHandler() director.window.push_handlers(keys) director.run(MenuScene())
  • 93.
  • 94.
  • 95.
    Special Effects • Simpleimage animations • Use libraries like lepton
  • 96.
  • 97.
  • 98.
    Controlling Animation • Imageposition on screen • Updates over time
  • 99.
    Controlling Animation • Imageposition on screen • Updates over time • Accounting for frame rate
  • 100.
    Controlling Animation • Imageposition on screen • Updates over time • Accounting for frame rate • Frame to use from multiple images
  • 101.
  • 102.
  • 103.
  • 104.
    Special Effects ... explosion_sound =pyglet.media.load('data/explosion.wav', streaming=False) bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False) # load our explosion animation explosion = pyglet.image.load('data/explosion.png') explosion_grid = pyglet.image.ImageGrid(explosion, 2, 8) explosion = explosion_grid.get_animation(.05, False) ...
  • 105.
    Special Effects # removethe bullet and asteroid, create new smaller # asteroid self.target.kill() s = cocos.sprite.Sprite(explosion, self.target.position) s.velocity = asteroid.velocity s.do(actions.WrappedMove(width, height) | (actions.Delay(.05 * 16) + actions.CallFuncS(lambda s: s.kill()))) self.target.parent.add(s) create_smaller_asteroids(asteroid) asteroid.kill()
  • 106.
    Other Game Types •minesweeper • car driving • platformer • tower defence
  • 107.
    Minesweeper • rendering agrid of cells • detecting mouse clicks on cells • exposing contents
  • 108.
    Car Driving • differentmovement model • detecting correct circuit • detecting collision with edge of track (pixel-based collision detection)
  • 109.
    Platformer • rendering gamelayers • handling triggers • platformer physics
  • 110.
    Tower Defence • towerplacement (selection & position) • movement solution for creeps
  • 111.
    Other Things • kytten GUI controls for pyglet / cocos2d • cocograph map editing
  • 112.
    Where To FromHere? • http://los-cocos.org • http://pyglet.org • http://pygame.org • http://inventwithpython.com/ • http://pyweek.org

Editor's Notes

  • #20 A Sprite is an image that knows how to draw itself on the screen.
  • #21 The default anchor point in cocos2d is the center of the sprite image.
  • #40 Unfortunately cocos2d rotates clockwise around Z, whereas pyglet (and basic trigenometry) rotates anti-clockwise, so we must negate the rotation to determine the correct y value.
  • #93 Make life easier for yourself - make all your animation frames the same size!
  • #94 Make life easier for yourself - make all your animation frames the same size!
  • #96 pyglet will sequence a grid of images from the bottom left corner taking each row, and then each cell in turn