KEMBAR78
Introduction to Game Programming Tutorial | KEY
Game Programming
      LCA 2010
     Richard Jones
The Basics
• Displaying something
• Controlling animation
• User input
• Gameplay mechanics
• Playing sound effects and music
• Further stuff
Displaying Something

• Opening a window
• Reading images
• Displaying images
Controlling Animation

• Image position on screen
• Updates over time
• Accounting for frame rate
User Input


• Getting events
• Distinct keyboard events vs. keyboard state
Gameplay Mechanics


• Interaction of game objects
• Detecting important events (game won or
  game over)
Detecting Collisions

• Pixel-Perfect
• Axis-Aligned Bounding Box
• Circle-Circle
• Hash Map
Sound


• Reading sound files
• Playing sounds and background music
Opening a Window

 import pyglet

 w = pyglet.window.Window()

 pyglet.app.run()
Clearing the Window
  import pyglet

  w = pyglet.window.Window()

  @w.event
  def on_draw():
     w.clear()

  pyglet.app.run()
Drawing
import pyglet

w = pyglet.window.Window()

ship_image = pyglet.image.load('data/ship.png')

@w.event
def on_draw():
   w.clear()
   ship_image.blit(100, 100)

pyglet.app.run()
Better Drawing
import pyglet

w = pyglet.window.Window()

ship_image = pyglet.image.load('data/ship.png')
ship = pyglet.sprite.Sprite(ship_image)
ship.position = (100, 100)

@w.event
def on_draw():
   w.clear()
   ship_image.blit(100, 100)
   ship.draw()

pyglet.app.run()
Animation
...
   w.clear()
   ship.draw()

def update(dt):
    ship.x += 1

pyglet.clock.schedule_interval(update, 1./30)

pyglet.app.run()
Better Animation
 ...
    w.clear()
    ship.draw()

 def update(dt):
     ship.x += 1
     ship.x += 100 * dt

 pyglet.clock.schedule_interval(update, 1./30)

 pyglet.app.run()
Adding Asteroids
 import random
 import pyglet

 w = pyglet.window.Window()

 def center_anchor(img):
     img.anchor_x = img.width / 2
     img.anchor_y = img.height / 2

 ...
Adding Asteroids
...
ship_image = pyglet.image.load('data/ship.png')
center_anchor(ship_image)
ship = pyglet.sprite.Sprite(ship_image)
ship.position = (100, 100)

big_asteroid_image = pyglet.image.load('data/big_asteroid.png')
center_anchor(big_asteroid_image)
asteroids = []
for i in range(3):
    x = random.randint(0, w.width)
    y = random.randint(0, w.height)
    s = pyglet.sprite.Sprite(big_asteroid_image, x, y)
    s.dx = random.randint(-100, 100)
    s.dy = random.randint(-100, 100)
    asteroids.append(s)

@w.event
...
Adding Asteroids

   ...
   @w.event
   def on_draw():
       w.clear()
       for asteroid in asteroids:
           asteroid.draw()
       ship.draw()
   ...
Adding Asteroids

  ...
  def update(dt):
      ship.x += 100 * dt
      for asteroid in asteroids:
          asteroid.x += asteroid.dx*dt
          asteroid.y += asteroid.dy*dt
  ...
A Little Refactoring

   ...
   ship = pyglet.sprite.Sprite(ship_image)
   ship.position = (100, 100)
   ship.dx = 100
   ship.dy = 0

   ...
Screen Wrapping
  ...
  def update(dt):
      ship.x += 100 * dt
      for asteroid in asteroids:
          asteroid.x += asteroid.dx*dt
          asteroid.y += asteroid.dy*dt
      for a in asteroids + [ship]:
          a.x += a.dx*dt
          a.y += a.dy*dt
          if a.x - a.width/2 > w.width:
              a.x -= w.width + a.width
          elif a.x + a.width/2 < 0:
              a.x += w.width + a.width
          if a.y - a.height/2 > w.height:
              a.y -= w.height + a.height
          elif a.y + a.height/2 < 0:
              a.y += w.height + a.height
  ...
Control

import math
import random
import pyglet

w = pyglet.window.Window()

...
Control
...
keys = pyglet.window.key.KeyStateHandler()
w.push_handlers(keys)

def update(dt):
    if keys[pyglet.window.key.LEFT]:
        ship.rotation -= 360*dt
    if keys[pyglet.window.key.RIGHT]:
        ship.rotation += 360*dt
    rotation = math.radians(ship.rotation)
    rotation_x = math.cos(-rotation)
    rotation_y = math.sin(-rotation)
    if keys[pyglet.window.key.UP]:
        ship.dx += 200 * rotation_x * dt
        ship.dy += 200 * rotation_y * dt

   for a in asteroids + [ship]:
   ...
Rotation

...
ship = pyglet.sprite.Sprite(ship_image)
ship.position = (100, 100)
ship.dx = 100
ship.dy = 0
ship.position = (w.width/2, w.height/2)
ship.dx = ship.dy = ship.dr = 0

...
Rotation

      ...
      s = pyglet.sprite.Sprite(big_asteroid_image, x, y)
      s.dx = random.randint(-100, 100)
      s.dy = random.randint(-100, 100)
      s.dr = random.randint(-100, 100)
      asteroids.append(s)

...
Rotation
...
def update(dt):
    if keys[pyglet.window.key.LEFT]:
        ship.rotation -= 360*dt
    if keys[pyglet.window.key.RIGHT]:
        ship.rotation += 360*dt
    ship.dr = (keys[pyglet.window.key.RIGHT] - keys[pyglet.window.key.LEFT]) * 360
    rotation = math.pi * ship.rotation / 180.0
    ...
    for a in asteroids + [ship]:
        a.x += a.dx*dt
        a.y += a.dy*dt
        a.rotation += a.dr * dt
        if a.x - a.width/2 > w.width:
            a.x -= w.width + a.width
Collision Detection
Collision Detection
Collision Detection
Collision Detection
Collision Detection

       distance
Collision Detection

  import   sys
  import   math
  import   random
  import   pyglet

  w = pyglet.window.Window()
  ...
Collision Detection

 ...
 w.push_handlers(keys)

 def distance(a, b):
     return math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2)

 def update(dt):
     ...
Collision Detection

       ...
       elif a.y + a.height/2 < 0:
           a.y += w.height + a.height

   for a in asteroids:
       if distance(a, ship) < (a.width/2 + ship.width/2):
           sys.exit('GAME OVER')

pyglet.clock.schedule_interval(update, 1./30)
...
Shooting

ship.position = (w.width/2, w.height/2)
ship.dx = ship.dy = ship.dr = 0
ship.gun_cooldown = 0

bullet_image = pyglet.image.load('data/bullet.png')
center_anchor(bullet_image)
bullets = []

...
...
             Shooting
    ship.dy += 200 * rotation_y * dt

if ship.gun_cooldown:
    ship.gun_cooldown = max(0, ship.gun_cooldown - dt)
elif keys[pyglet.window.key.SPACE] and len(bullets) < 2:
    b = pyglet.sprite.Sprite(bullet_image, ship.x, ship.y)
    b.dx = rotation_x * 500
    b.dy = rotation_y * 500
    b.dr = 0
    b.life = 1
    bullets.append(b)
    ship.gun_cooldown = .5

for b in list(bullets):
    b.life -= dt
    if b.life < 0:
        bullets.remove(b)

for a in asteroids + [ship]:
for a in asteroids + [ship] + bullets:
     a.x += a.dx*dt
     ...
Shooting

...
@w.event
def on_draw():
    w.clear()
    for asteroid in asteroids:
         asteroid.draw()
    for s in asteroids + bullets:
        s.draw()
    ship.draw()
...
Shooting ... and Hitting

   ...
   def distance(a, b):
       return math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2)
   def collide(a, b):
       return distance(a, b) < (a.width/2 + b.width/2)
   ...
Shooting ... and Hitting
        ...
        elif a.y + a.height/2 < 0:
            a.y += w.height + a.height

    for a in asteroids:
        if distance(a, ship) < (a.width/2 + ship.width/2):
            sys.exit('GAME OVER')
    for a in list(asteroids):
        if collide(a, ship):
            sys.exit('GAME OVER')
        for b in list(bullets):
            if collide(a, b):
                bullets.remove(b)
                asteroids.remove(a)

    if not asteroids:
        sys.exit('YOU WIN')

 pyglet.clock.schedule_interval(update, 1./30)
 ...
Chunks

...
center_anchor(bullet_image)
bullets = []

small_asteroid_image = pyglet.image.load('data/small_asteroid.png')
center_anchor(small_asteroid_image)
medium_asteroid_image = pyglet.image.load('data/medium_asteroid.png')
center_anchor(medium_asteroid_image)
big_asteroid_image = pyglet.image.load('data/big_asteroid.png')
center_anchor(big_asteroid_image)
...
...
                          Chunks
   for b in list(bullets):
       if collide(a, b):
           bullets.remove(b)
           asteroids.remove(a)
           if a.image is big_asteroid_image.texture:
               for i in range(2):
                   s = pyglet.sprite.Sprite(medium_asteroid_image, a.x, a.y)
                   s.dx = a.dx + random.randint(-50, 50)
                   s.dy = a.dy + random.randint(-50, 50)
                   s.dr = a.dr + random.randint(-50, 50)
                   asteroids.append(s)
           elif a.image is medium_asteroid_image.texture:
               for i in range(2):
                   s = pyglet.sprite.Sprite(small_asteroid_image, a.x, a.y)
                   s.dx = a.dx + random.randint(-50, 50)
                   s.dy = a.dy + random.randint(-50, 50)
                   s.dr = a.dr + random.randint(-50, 50)
                   asteroids.append(s)

if not asteroids:
    sys.exit('YOU WIN')
...
Sound Effects

...
      asteroids.append(s)

explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False)
bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False)

@w.event
def on_draw():
...
Sound Effects

     ...
     bullets.append(b)
     ship.gun_cooldown = .5
     bullet_sound.play()

  for b in list(bullets):
  ...
Sound Effects

...
bullets.remove(b)
asteroids.remove(a)
explosion_sound.play()
if a.image is big_asteroid_image.texture:
Further Stuff

• Opening screen
• Options menu
• Game won / game over screen
• Special effects
Other screen / menu


• Use libraries like Cocos2d (Scenes, Layers,
  Sprites, Animations)
Special Effects


• Simple image animations
• Use libraries like lepton
Where To From Here?

• http://pyglet.org
• http://pygame.org
• http://los-cocos.org
• http://inventwithpython.com/
• http://pyweek.org

Introduction to Game Programming Tutorial

  • 1.
    Game Programming LCA 2010 Richard Jones
  • 2.
    The Basics • Displayingsomething • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music • Further stuff
  • 3.
    Displaying Something • Openinga window • Reading images • Displaying images
  • 4.
    Controlling Animation • Imageposition on screen • Updates over time • Accounting for frame rate
  • 5.
    User Input • Gettingevents • Distinct keyboard events vs. keyboard state
  • 6.
    Gameplay Mechanics • Interactionof game objects • Detecting important events (game won or game over)
  • 7.
    Detecting Collisions • Pixel-Perfect •Axis-Aligned Bounding Box • Circle-Circle • Hash Map
  • 8.
    Sound • Reading soundfiles • Playing sounds and background music
  • 9.
    Opening a Window import pyglet w = pyglet.window.Window() pyglet.app.run()
  • 10.
    Clearing the Window import pyglet w = pyglet.window.Window() @w.event def on_draw(): w.clear() pyglet.app.run()
  • 11.
    Drawing import pyglet w =pyglet.window.Window() ship_image = pyglet.image.load('data/ship.png') @w.event def on_draw(): w.clear() ship_image.blit(100, 100) pyglet.app.run()
  • 12.
    Better Drawing import pyglet w= pyglet.window.Window() ship_image = pyglet.image.load('data/ship.png') ship = pyglet.sprite.Sprite(ship_image) ship.position = (100, 100) @w.event def on_draw(): w.clear() ship_image.blit(100, 100) ship.draw() pyglet.app.run()
  • 13.
    Animation ... w.clear() ship.draw() def update(dt): ship.x += 1 pyglet.clock.schedule_interval(update, 1./30) pyglet.app.run()
  • 14.
    Better Animation ... w.clear() ship.draw() def update(dt): ship.x += 1 ship.x += 100 * dt pyglet.clock.schedule_interval(update, 1./30) pyglet.app.run()
  • 15.
    Adding Asteroids importrandom import pyglet w = pyglet.window.Window() def center_anchor(img): img.anchor_x = img.width / 2 img.anchor_y = img.height / 2 ...
  • 16.
    Adding Asteroids ... ship_image =pyglet.image.load('data/ship.png') center_anchor(ship_image) ship = pyglet.sprite.Sprite(ship_image) ship.position = (100, 100) big_asteroid_image = pyglet.image.load('data/big_asteroid.png') center_anchor(big_asteroid_image) asteroids = [] for i in range(3): x = random.randint(0, w.width) y = random.randint(0, w.height) s = pyglet.sprite.Sprite(big_asteroid_image, x, y) s.dx = random.randint(-100, 100) s.dy = random.randint(-100, 100) asteroids.append(s) @w.event ...
  • 17.
    Adding Asteroids ... @w.event def on_draw(): w.clear() for asteroid in asteroids: asteroid.draw() ship.draw() ...
  • 18.
    Adding Asteroids ... def update(dt): ship.x += 100 * dt for asteroid in asteroids: asteroid.x += asteroid.dx*dt asteroid.y += asteroid.dy*dt ...
  • 19.
    A Little Refactoring ... ship = pyglet.sprite.Sprite(ship_image) ship.position = (100, 100) ship.dx = 100 ship.dy = 0 ...
  • 20.
    Screen Wrapping ... def update(dt): ship.x += 100 * dt for asteroid in asteroids: asteroid.x += asteroid.dx*dt asteroid.y += asteroid.dy*dt for a in asteroids + [ship]: a.x += a.dx*dt a.y += a.dy*dt if a.x - a.width/2 > w.width: a.x -= w.width + a.width elif a.x + a.width/2 < 0: a.x += w.width + a.width if a.y - a.height/2 > w.height: a.y -= w.height + a.height elif a.y + a.height/2 < 0: a.y += w.height + a.height ...
  • 21.
    Control import math import random importpyglet w = pyglet.window.Window() ...
  • 22.
    Control ... keys = pyglet.window.key.KeyStateHandler() w.push_handlers(keys) defupdate(dt): if keys[pyglet.window.key.LEFT]: ship.rotation -= 360*dt if keys[pyglet.window.key.RIGHT]: ship.rotation += 360*dt rotation = math.radians(ship.rotation) rotation_x = math.cos(-rotation) rotation_y = math.sin(-rotation) if keys[pyglet.window.key.UP]: ship.dx += 200 * rotation_x * dt ship.dy += 200 * rotation_y * dt for a in asteroids + [ship]: ...
  • 23.
    Rotation ... ship = pyglet.sprite.Sprite(ship_image) ship.position= (100, 100) ship.dx = 100 ship.dy = 0 ship.position = (w.width/2, w.height/2) ship.dx = ship.dy = ship.dr = 0 ...
  • 24.
    Rotation ... s = pyglet.sprite.Sprite(big_asteroid_image, x, y) s.dx = random.randint(-100, 100) s.dy = random.randint(-100, 100) s.dr = random.randint(-100, 100) asteroids.append(s) ...
  • 25.
    Rotation ... def update(dt): if keys[pyglet.window.key.LEFT]: ship.rotation -= 360*dt if keys[pyglet.window.key.RIGHT]: ship.rotation += 360*dt ship.dr = (keys[pyglet.window.key.RIGHT] - keys[pyglet.window.key.LEFT]) * 360 rotation = math.pi * ship.rotation / 180.0 ... for a in asteroids + [ship]: a.x += a.dx*dt a.y += a.dy*dt a.rotation += a.dr * dt if a.x - a.width/2 > w.width: a.x -= w.width + a.width
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
    Collision Detection import sys import math import random import pyglet w = pyglet.window.Window() ...
  • 32.
    Collision Detection ... w.push_handlers(keys) def distance(a, b): return math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2) def update(dt): ...
  • 33.
    Collision Detection ... elif a.y + a.height/2 < 0: a.y += w.height + a.height for a in asteroids: if distance(a, ship) < (a.width/2 + ship.width/2): sys.exit('GAME OVER') pyglet.clock.schedule_interval(update, 1./30) ...
  • 34.
    Shooting ship.position = (w.width/2,w.height/2) ship.dx = ship.dy = ship.dr = 0 ship.gun_cooldown = 0 bullet_image = pyglet.image.load('data/bullet.png') center_anchor(bullet_image) bullets = [] ...
  • 35.
    ... Shooting ship.dy += 200 * rotation_y * dt if ship.gun_cooldown: ship.gun_cooldown = max(0, ship.gun_cooldown - dt) elif keys[pyglet.window.key.SPACE] and len(bullets) < 2: b = pyglet.sprite.Sprite(bullet_image, ship.x, ship.y) b.dx = rotation_x * 500 b.dy = rotation_y * 500 b.dr = 0 b.life = 1 bullets.append(b) ship.gun_cooldown = .5 for b in list(bullets): b.life -= dt if b.life < 0: bullets.remove(b) for a in asteroids + [ship]: for a in asteroids + [ship] + bullets: a.x += a.dx*dt ...
  • 36.
    Shooting ... @w.event def on_draw(): w.clear() for asteroid in asteroids: asteroid.draw() for s in asteroids + bullets: s.draw() ship.draw() ...
  • 37.
    Shooting ... andHitting ... def distance(a, b): return math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2) def collide(a, b): return distance(a, b) < (a.width/2 + b.width/2) ...
  • 38.
    Shooting ... andHitting ... elif a.y + a.height/2 < 0: a.y += w.height + a.height for a in asteroids: if distance(a, ship) < (a.width/2 + ship.width/2): sys.exit('GAME OVER') for a in list(asteroids): if collide(a, ship): sys.exit('GAME OVER') for b in list(bullets): if collide(a, b): bullets.remove(b) asteroids.remove(a) if not asteroids: sys.exit('YOU WIN') pyglet.clock.schedule_interval(update, 1./30) ...
  • 39.
    Chunks ... center_anchor(bullet_image) bullets = [] small_asteroid_image= pyglet.image.load('data/small_asteroid.png') center_anchor(small_asteroid_image) medium_asteroid_image = pyglet.image.load('data/medium_asteroid.png') center_anchor(medium_asteroid_image) big_asteroid_image = pyglet.image.load('data/big_asteroid.png') center_anchor(big_asteroid_image) ...
  • 40.
    ... Chunks for b in list(bullets): if collide(a, b): bullets.remove(b) asteroids.remove(a) if a.image is big_asteroid_image.texture: for i in range(2): s = pyglet.sprite.Sprite(medium_asteroid_image, a.x, a.y) s.dx = a.dx + random.randint(-50, 50) s.dy = a.dy + random.randint(-50, 50) s.dr = a.dr + random.randint(-50, 50) asteroids.append(s) elif a.image is medium_asteroid_image.texture: for i in range(2): s = pyglet.sprite.Sprite(small_asteroid_image, a.x, a.y) s.dx = a.dx + random.randint(-50, 50) s.dy = a.dy + random.randint(-50, 50) s.dr = a.dr + random.randint(-50, 50) asteroids.append(s) if not asteroids: sys.exit('YOU WIN') ...
  • 41.
    Sound Effects ... asteroids.append(s) explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False) bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False) @w.event def on_draw(): ...
  • 42.
    Sound Effects ... bullets.append(b) ship.gun_cooldown = .5 bullet_sound.play() for b in list(bullets): ...
  • 43.
  • 44.
    Further Stuff • Openingscreen • Options menu • Game won / game over screen • Special effects
  • 45.
    Other screen /menu • Use libraries like Cocos2d (Scenes, Layers, Sprites, Animations)
  • 46.
    Special Effects • Simpleimage animations • Use libraries like lepton
  • 47.
    Where To FromHere? • http://pyglet.org • http://pygame.org • http://los-cocos.org • http://inventwithpython.com/ • http://pyweek.org

Editor's Notes