SlideShare a Scribd company logo
1 of 112
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

More Related Content

What's hot

What's hot (20)

Corona sdk
Corona sdkCorona sdk
Corona sdk
 
Making Games in JavaScript
Making Games in JavaScriptMaking Games in JavaScript
Making Games in JavaScript
 
ES6(ES2015) is beautiful
ES6(ES2015) is beautifulES6(ES2015) is beautiful
ES6(ES2015) is beautiful
 
Proga 0622
Proga 0622Proga 0622
Proga 0622
 
The Ring programming language version 1.2 book - Part 35 of 84
The Ring programming language version 1.2 book - Part 35 of 84The Ring programming language version 1.2 book - Part 35 of 84
The Ring programming language version 1.2 book - Part 35 of 84
 
Mobile Game and Application with J2ME - Collision Detection
Mobile Gameand Application withJ2ME  - Collision DetectionMobile Gameand Application withJ2ME  - Collision Detection
Mobile Game and Application with J2ME - Collision Detection
 
Mobile Game and Application with J2ME
Mobile Gameand Application with J2MEMobile Gameand Application with J2ME
Mobile Game and Application with J2ME
 
Expand/Collapse animation on Android
Expand/Collapse animation on AndroidExpand/Collapse animation on Android
Expand/Collapse animation on Android
 
The Ring programming language version 1.10 book - Part 71 of 212
The Ring programming language version 1.10 book - Part 71 of 212The Ring programming language version 1.10 book - Part 71 of 212
The Ring programming language version 1.10 book - Part 71 of 212
 
The Ring programming language version 1.10 book - Part 64 of 212
The Ring programming language version 1.10 book - Part 64 of 212The Ring programming language version 1.10 book - Part 64 of 212
The Ring programming language version 1.10 book - Part 64 of 212
 
pptuni1
pptuni1pptuni1
pptuni1
 
Pybelsberg — Constraint-based Programming in Python
Pybelsberg — Constraint-based Programming in PythonPybelsberg — Constraint-based Programming in Python
Pybelsberg — Constraint-based Programming in Python
 
Homemade GoTo mount for Telescopes using Nylon wheels, GT2 belts and 100:1 ge...
Homemade GoTo mount for Telescopes using Nylon wheels, GT2 belts and 100:1 ge...Homemade GoTo mount for Telescopes using Nylon wheels, GT2 belts and 100:1 ge...
Homemade GoTo mount for Telescopes using Nylon wheels, GT2 belts and 100:1 ge...
 
Cocos2dx 7.1-7.2
Cocos2dx 7.1-7.2Cocos2dx 7.1-7.2
Cocos2dx 7.1-7.2
 
SwiftUI Animation - The basic overview
SwiftUI Animation - The basic overviewSwiftUI Animation - The basic overview
SwiftUI Animation - The basic overview
 
The Ring programming language version 1.2 book - Part 38 of 84
The Ring programming language version 1.2 book - Part 38 of 84The Ring programming language version 1.2 book - Part 38 of 84
The Ring programming language version 1.2 book - Part 38 of 84
 
The Ring programming language version 1.5.2 book - Part 52 of 181
The Ring programming language version 1.5.2 book - Part 52 of 181The Ring programming language version 1.5.2 book - Part 52 of 181
The Ring programming language version 1.5.2 book - Part 52 of 181
 
Functional Systems @ Twitter
Functional Systems @ TwitterFunctional Systems @ Twitter
Functional Systems @ Twitter
 
modm
modmmodm
modm
 
The Ring programming language version 1.5 book - Part 9 of 31
The Ring programming language version 1.5 book - Part 9 of 31The Ring programming language version 1.5 book - Part 9 of 31
The Ring programming language version 1.5 book - Part 9 of 31
 

Similar to Intro to Game Programming

Html5 game programming overview
Html5 game programming overviewHtml5 game programming overview
Html5 game programming overview
민태 김
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
Giordano Scalzo
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
Laurence Svekis ✔
 
Pointer Events in Canvas
Pointer Events in CanvasPointer Events in Canvas
Pointer Events in Canvas
deanhudson
 
circ.db.dbcircleserver(1).py#!usrlocalbinpython3im.docx
circ.db.dbcircleserver(1).py#!usrlocalbinpython3im.docxcirc.db.dbcircleserver(1).py#!usrlocalbinpython3im.docx
circ.db.dbcircleserver(1).py#!usrlocalbinpython3im.docx
christinemaritza
 

Similar to Intro to Game Programming (20)

Shootting Game
Shootting GameShootting Game
Shootting Game
 
Game dev 101 part 3
Game dev 101 part 3Game dev 101 part 3
Game dev 101 part 3
 
Макс Грибов — Использование SpriteKit в неигровых приложениях
Макс Грибов — Использование SpriteKit в неигровых приложенияхМакс Грибов — Использование SpriteKit в неигровых приложениях
Макс Грибов — Использование SpriteKit в неигровых приложениях
 
Testing a 2D Platformer with Spock
Testing a 2D Platformer with SpockTesting a 2D Platformer with Spock
Testing a 2D Platformer with Spock
 
You will learn RxJS in 2017
You will learn RxJS in 2017You will learn RxJS in 2017
You will learn RxJS in 2017
 
Game development with Cocos2d
Game development with Cocos2dGame development with Cocos2d
Game development with Cocos2d
 
Writing a Space Shooter with HTML5 Canvas
Writing a Space Shooter with HTML5 CanvasWriting a Space Shooter with HTML5 Canvas
Writing a Space Shooter with HTML5 Canvas
 
Game age ppt
Game age pptGame age ppt
Game age ppt
 
The Ring programming language version 1.6 book - Part 62 of 189
The Ring programming language version 1.6 book - Part 62 of 189The Ring programming language version 1.6 book - Part 62 of 189
The Ring programming language version 1.6 book - Part 62 of 189
 
Swift, via "swift-2048"
Swift, via "swift-2048"Swift, via "swift-2048"
Swift, via "swift-2048"
 
Html5 game programming overview
Html5 game programming overviewHtml5 game programming overview
Html5 game programming overview
 
[Ultracode Munich #4] Demo on Animatron by Anton Kotenko
[Ultracode Munich #4] Demo on Animatron by Anton Kotenko[Ultracode Munich #4] Demo on Animatron by Anton Kotenko
[Ultracode Munich #4] Demo on Animatron by Anton Kotenko
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
 
Silverlight as a Gaming Platform
Silverlight as a Gaming PlatformSilverlight as a Gaming Platform
Silverlight as a Gaming Platform
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
 
Rxjs kyivjs 2015
Rxjs kyivjs 2015Rxjs kyivjs 2015
Rxjs kyivjs 2015
 
Processing and Processing.js
Processing and Processing.jsProcessing and Processing.js
Processing and Processing.js
 
Intro to Python (High School) Unit #3
Intro to Python (High School) Unit #3Intro to Python (High School) Unit #3
Intro to Python (High School) Unit #3
 
Pointer Events in Canvas
Pointer Events in CanvasPointer Events in Canvas
Pointer Events in Canvas
 
circ.db.dbcircleserver(1).py#!usrlocalbinpython3im.docx
circ.db.dbcircleserver(1).py#!usrlocalbinpython3im.docxcirc.db.dbcircleserver(1).py#!usrlocalbinpython3im.docx
circ.db.dbcircleserver(1).py#!usrlocalbinpython3im.docx
 

More from Richard Jones

More from Richard Jones (11)

Angboard
AngboardAngboard
Angboard
 
Don't do this
Don't do thisDon't do this
Don't do this
 
Introduction to Game Programming
Introduction to Game ProgrammingIntroduction to Game Programming
Introduction to Game Programming
 
Message Queueing - by an MQ noob
Message Queueing - by an MQ noobMessage Queueing - by an MQ noob
Message Queueing - by an MQ noob
 
Message queueing
Message queueingMessage queueing
Message queueing
 
Web micro-framework BATTLE!
Web micro-framework BATTLE!Web micro-framework BATTLE!
Web micro-framework BATTLE!
 
State of Python (2010)
State of Python (2010)State of Python (2010)
State of Python (2010)
 
What's New In Python 2.6
What's New In Python 2.6What's New In Python 2.6
What's New In Python 2.6
 
What's New In Python 2.5
What's New In Python 2.5What's New In Python 2.5
What's New In Python 2.5
 
What's New In Python 2.4
What's New In Python 2.4What's New In Python 2.4
What's New In Python 2.4
 
Tkinter Does Not Suck
Tkinter Does Not SuckTkinter Does Not Suck
Tkinter Does Not Suck
 

Recently uploaded

Beyond the EU: DORA and NIS 2 Directive's Global Impact
Beyond the EU: DORA and NIS 2 Directive's Global ImpactBeyond the EU: DORA and NIS 2 Directive's Global Impact
Beyond the EU: DORA and NIS 2 Directive's Global Impact
PECB
 
1029-Danh muc Sach Giao Khoa khoi 6.pdf
1029-Danh muc Sach Giao Khoa khoi  6.pdf1029-Danh muc Sach Giao Khoa khoi  6.pdf
1029-Danh muc Sach Giao Khoa khoi 6.pdf
QucHHunhnh
 
The basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxThe basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptx
heathfieldcps1
 

Recently uploaded (20)

TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
 
ICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptxICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptx
 
INDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptx
INDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptxINDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptx
INDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptx
 
Class 11th Physics NEET formula sheet pdf
Class 11th Physics NEET formula sheet pdfClass 11th Physics NEET formula sheet pdf
Class 11th Physics NEET formula sheet pdf
 
Beyond the EU: DORA and NIS 2 Directive's Global Impact
Beyond the EU: DORA and NIS 2 Directive's Global ImpactBeyond the EU: DORA and NIS 2 Directive's Global Impact
Beyond the EU: DORA and NIS 2 Directive's Global Impact
 
General Principles of Intellectual Property: Concepts of Intellectual Proper...
General Principles of Intellectual Property: Concepts of Intellectual  Proper...General Principles of Intellectual Property: Concepts of Intellectual  Proper...
General Principles of Intellectual Property: Concepts of Intellectual Proper...
 
psychiatric nursing HISTORY COLLECTION .docx
psychiatric  nursing HISTORY  COLLECTION  .docxpsychiatric  nursing HISTORY  COLLECTION  .docx
psychiatric nursing HISTORY COLLECTION .docx
 
Micro-Scholarship, What it is, How can it help me.pdf
Micro-Scholarship, What it is, How can it help me.pdfMicro-Scholarship, What it is, How can it help me.pdf
Micro-Scholarship, What it is, How can it help me.pdf
 
Basic Civil Engineering first year Notes- Chapter 4 Building.pptx
Basic Civil Engineering first year Notes- Chapter 4 Building.pptxBasic Civil Engineering first year Notes- Chapter 4 Building.pptx
Basic Civil Engineering first year Notes- Chapter 4 Building.pptx
 
1029-Danh muc Sach Giao Khoa khoi 6.pdf
1029-Danh muc Sach Giao Khoa khoi  6.pdf1029-Danh muc Sach Giao Khoa khoi  6.pdf
1029-Danh muc Sach Giao Khoa khoi 6.pdf
 
Nutritional Needs Presentation - HLTH 104
Nutritional Needs Presentation - HLTH 104Nutritional Needs Presentation - HLTH 104
Nutritional Needs Presentation - HLTH 104
 
Web & Social Media Analytics Previous Year Question Paper.pdf
Web & Social Media Analytics Previous Year Question Paper.pdfWeb & Social Media Analytics Previous Year Question Paper.pdf
Web & Social Media Analytics Previous Year Question Paper.pdf
 
Sociology 101 Demonstration of Learning Exhibit
Sociology 101 Demonstration of Learning ExhibitSociology 101 Demonstration of Learning Exhibit
Sociology 101 Demonstration of Learning Exhibit
 
The basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxThe basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptx
 
ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.
 
On National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan FellowsOn National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan Fellows
 
Unit-IV- Pharma. Marketing Channels.pptx
Unit-IV- Pharma. Marketing Channels.pptxUnit-IV- Pharma. Marketing Channels.pptx
Unit-IV- Pharma. Marketing Channels.pptx
 
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
 
Advanced Views - Calendar View in Odoo 17
Advanced Views - Calendar View in Odoo 17Advanced Views - Calendar View in Odoo 17
Advanced Views - Calendar View in Odoo 17
 
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
 

Intro to Game Programming

  • 1. Introduction To Game Programming EuroPython 2010 Richard Jones
  • 4. The Basics • Displaying something • Controlling animation
  • 5. The Basics • Displaying something • Controlling animation • User input
  • 6. The Basics • Displaying something • Controlling animation • User input • Gameplay mechanics
  • 7. The Basics • Displaying something • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music
  • 8. The Basics • Displaying something • Controlling animation • User input • Gameplay mechanics • Playing sound effects and music • Game architecture
  • 11. Displaying Something • Opening a window • Reading images
  • 12. Displaying Something • Opening a window • Reading images • Displaying images
  • 13. Displaying Something • Opening a window • Reading images • Displaying images • Organising the display of images (scene composition)
  • 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.director import 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
  • 25. 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)
  • 26. 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)
  • 27. 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)
  • 28. 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())
  • 29. 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))) ...
  • 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) ...
  • 35. Gameplay Mechanics • Player Controls • Timed rules (when objects appear; moving the play field)
  • 36. Gameplay Mechanics • Player Controls • Timed rules (when objects appear; moving the play field) • Interaction of game objects
  • 37. 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)
  • 40. User Input • Getting events • Distinct keyboard events vs. keyboard state
  • 41. Control import math import random from pyglet.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 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())
  • 49. Detecting Collisions • Pixel-Perfect • Axis-Aligned Bounding Box
  • 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], ...}
  • 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 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
  • 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. Sound
  • 76. Sound • Reading sound files • Playing sounds and background music
  • 77. 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) ...
  • 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
  • 82. Game Architecture • Player lives • Game won / game over screen
  • 83. Game Architecture • Player lives • Game won / game over screen • Opening screen
  • 84. Game Architecture • Player lives • 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 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
  • 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())
  • 94. Special Effects • Simple image animations
  • 95. Special Effects • Simple image animations • Use libraries like lepton
  • 97. Controlling Animation • Image position on screen
  • 98. Controlling Animation • Image position on screen • Updates over time
  • 99. Controlling Animation • Image position on screen • Updates over time • Accounting for frame rate
  • 100. Controlling Animation • Image position on screen • Updates over time • Accounting for frame rate • Frame to use from multiple images
  • 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 # 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()
  • 106. Other Game Types • minesweeper • car driving • platformer • tower defence
  • 107. Minesweeper • rendering a grid of cells • detecting mouse clicks on cells • exposing contents
  • 108. Car Driving • different movement model • detecting correct circuit • detecting collision with edge of track (pixel-based collision detection)
  • 109. Platformer • rendering game layers • handling triggers • platformer physics
  • 110. Tower Defence • tower placement (selection & position) • movement solution for creeps
  • 111. Other Things • kytten GUI controls for pyglet / cocos2d • cocograph map editing
  • 112. Where To From Here? • http://los-cocos.org • http://pyglet.org • http://pygame.org • http://inventwithpython.com/ • http://pyweek.org

Editor's Notes

  1. A Sprite is an image that knows how to draw itself on the screen.
  2. The default anchor point in cocos2d is the center of the sprite image.
  3. 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.
  4. Make life easier for yourself - make all your animation frames the same size!
  5. Make life easier for yourself - make all your animation frames the same size!
  6. pyglet will sequence a grid of images from the bottom left corner taking each row, and then each cell in turn