PyGame

Ubuntu Opportunistic Developers Week March 2010 - Create games with PyGame - Rick Spencer - Mar 3 2010

(02:01:34 PM) rickspencer3: hi all
(02:01:40 PM) rickspencer3: ready for some pygame stuff?
(02:02:10 PM) rickspencer3: great
(02:02:40 PM) rickspencer3: as usual, I got my notes ready before hand and stuck them on the wiki:
(02:02:41 PM) rickspencer3: https://wiki.ubuntu.com/UbuntuOpportunisticDeveloperWeek/PyGameClassNotes
(02:03:02 PM) rickspencer3: so I'll go through each section and take questions
(02:03:24 PM) rickspencer3: Pygame is a library for making arcade style sprite games
(02:03:34 PM) rickspencer3: It features very easy blitting and colision detection
(02:03:43 PM) rickspencer3: I have some sample code to help with understanding pygame here:
(02:03:51 PM) rickspencer3: https://code.edge.launchpad.net/~rick-rickspencer3/pygame-template/trunk
(02:03:57 PM) rickspencer3: I think you can go:
(02:04:39 PM) rickspencer3: bzr branch lp:pygame-template too
(02:05:01 PM) rickspencer3: note that some of this code is a bit old, and I need to reformat it and such
(02:05:03 PM) rickspencer3: but it
(02:05:07 PM) rickspencer3: s failry readable
(02:05:14 PM) rickspencer3: however, it will eventually be a quickly template, hopefully in time for lucid
(02:05:20 PM) rickspencer3: if you want to grab that it might help you follow the class a bit, but it is not required
(02:05:28 PM) rickspencer3: it comes with some basic game play that you can study and modify
(02:05:37 PM) rickspencer3: essentially, it is kind of like asteroids
(02:05:42 PM) rickspencer3: you control the black triangle in the middle, and there are two enemies
(02:05:49 PM) rickspencer3: Of course you will want some docs
(02:05:54 PM) rickspencer3: Pygame has good docs here:
(02:05:59 PM) rickspencer3: http://www.pygame.org/docs/
(02:06:15 PM) rickspencer3: before we discuss how to set up a game
(02:06:17 PM) rickspencer3: any questions?
(02:06:38 PM) rickspencer3: ok, then let us rock
(02:06:54 PM) rickspencer3: so, you're going to set up a game
(02:06:55 PM) rickspencer3: first thing you need to do is set up a game surface
(02:07:08 PM) rickspencer3: and do other initalizations
(02:07:10 PM) rickspencer3: so ..
(02:07:11 PM) rickspencer3: initialize pygame libraries
(02:07:16 PM) rickspencer3: pygame.font.init()
(02:07:17 PM) rickspencer3: pygame.mixer.init()
(02:07:28 PM) rickspencer3: (assuming you are using fonts and sound of course)
(02:07:36 PM) rickspencer3: then create a screen
(02:07:43 PM) rickspencer3: the screen will be the surface on which your sprites will be drawn, essentially where your game will occur
(02:07:50 PM) rickspencer3: screen = pygame.display.set_mode((800, 480))
(02:08:04 PM) rickspencer3: that's for an 800 x 400 pixel game
(02:08:17 PM) rickspencer3: also, create a couple of variable you will need later
(02:08:22 PM) rickspencer3: like a background image
(02:08:29 PM) rickspencer3: background = pygame.image.load(path_to_background)
(02:08:39 PM) rickspencer3: notice the image is a PyGame image
(02:08:45 PM) rickspencer3: and a clock
(02:08:50 PM) rickspencer3: clock = pygame.time.Clock()
(02:08:59 PM) rickspencer3: you'll see why we need the clock next
(02:09:07 PM) rickspencer3: pygame games use a good old fashioned input loop
(02:09:20 PM) rickspencer3: don't know if you all ever programmed with an event loop
(02:09:43 PM) rickspencer3: but it was very common for writing GUI apps back around when dinosaurs used computers
(02:09:50 PM) rickspencer3: essentially, you tell your app to keep looping
(02:09:58 PM) rickspencer3: and whenever it goes through the loop do a few things
(02:10:03 PM) rickspencer3: 1. respond to user input
(02:10:11 PM) rickspencer3: 2.update data for any sprites on the screen
(02:10:18 PM) rickspencer3: this includes seeing if any sprites have collided
(02:10:33 PM) rickspencer3: 3. redraw everything
(02:10:39 PM) rickspencer3: let's look at setting up a game loop
(02:10:46 PM) rickspencer3:  while 1:
(02:10:46 PM) rickspencer3:   clock.tick(15)
(02:10:46 PM) rickspencer3:   if ControllerTick() == 0:
(02:10:46 PM) rickspencer3:    return
(02:10:46 PM) rickspencer3:   if not game.paused:
(02:10:46 PM) rickspencer3:    ViewTick()
(02:10:48 PM) rickspencer3:    if CheckCollisions() == 0:
(02:10:50 PM) rickspencer3:     return
(02:10:57 PM) rickspencer3: you can see this code in the crashteroids file at line 169
(02:11:05 PM) rickspencer3: so first, the loop just runs and runs until etiher ControllerTick or CheckCollisions returns 0
(02:11:14 PM) rickspencer3: the first line in the loop basically says to have 15 frames per second
(02:11:21 PM) rickspencer3: this is not like a sleep function where the argument says how long to sleep
(02:11:30 PM) rickspencer3: make sure that you pick a number that is low enough that a lower powered computer can keep up, if you care about that kind of thing
(02:11:40 PM) rickspencer3: The next line calls ControllerTick()
(02:11:45 PM) rickspencer3: This function checks for user input, which I will explain in a moment, and the updates all the game data
(02:11:57 PM) rickspencer3: this is a function that *you* write
(02:12:03 PM) rickspencer3: it's not built into PyGame
(02:12:15 PM) rickspencer3: then next in the loop
(02:12:22 PM) rickspencer3: assuming that the game is not paused, it will go ahead and update the view and do collision detection
(02:12:30 PM) rickspencer3: The reason that controllertick is always called is because this function controlls pausing and unpausing of the game
(02:12:41 PM) rickspencer3: if you didn't call it every time through, the user couldn't unpause
(02:12:49 PM) rickspencer3: we'll look at each of these functions in more detail
(02:12:56 PM) rickspencer3: but first, questions?
(02:13:10 PM) ClassBot: mhall119 asked: what file is that in?
(02:13:25 PM) rickspencer3: it's in crashteroids/bin/crashteroids
(02:13:36 PM) ClassBot: ryzrecreel asked: What kind of image formats can you use?
(02:13:39 PM) rickspencer3: dunno for sure
(02:13:43 PM) rickspencer3: I always use png
(02:14:02 PM) rickspencer3: ok, no more questions, so moving on
(02:14:13 PM) rickspencer3: let's start by handling user input
(02:14:20 PM) rickspencer3: pygame basically queues up input events
(02:14:26 PM) rickspencer3: you can then loop through them and respond to each event
(02:14:36 PM) rickspencer3: at line 93, you can see how the loop is set up
(02:14:41 PM) rickspencer3:  for event in pygame.event.get():
(02:14:41 PM) rickspencer3:   if event.type == pygame.QUIT:
(02:14:41 PM) rickspencer3:    return 0
(02:14:51 PM) rickspencer3: pygame.QUIT is called when the window closes or such
(02:14:57 PM) rickspencer3: of course you could also do something smart and save game state or such
(02:15:04 PM) rickspencer3: but this code returns 0, which causes the main event loop to quit
(02:15:13 PM) rickspencer3: now, let's say what I want to do is make the ship go forward when the user presses the j key
(02:15:25 PM) rickspencer3: (which is what crashteroids does by default)
(02:15:36 PM) rickspencer3: first I ask what kind of event it was
(02:15:44 PM) rickspencer3:   if event.type == pygame.KEYDOWN:
(02:15:55 PM) rickspencer3: yup, I want to respond to key down events
(02:16:06 PM) rickspencer3: so this I'll write some code here
(02:16:10 PM) rickspencer3: and then if it was a key event, what key was pressed
(02:16:17 PM) rickspencer3:     if event.key == pygame.K_l:
(02:16:18 PM) rickspencer3:      #do something
(02:16:25 PM) rickspencer3: oops
(02:16:31 PM) rickspencer3: that's for l key :)
(02:16:41 PM) rickspencer3: I guess l is for move, and j is to shoot
(02:17:06 PM) rickspencer3: in any case, you can probably guess that the correct event.key is pygame.k_j to respond to the j key
(02:17:07 PM) rickspencer3: ;)
(02:17:12 PM) rickspencer3: you can see the whole tree of if, else statements starting at line 89
(02:17:23 PM) rickspencer3: there are also events for the mouse and joysticks even
(02:17:29 PM) rickspencer3: so it's easy to create games that aren't only using the keyboard if you want
(02:17:34 PM) rickspencer3: so that's how to handle input
(02:17:39 PM) rickspencer3: let's look at sprites, but first, any questions?
(02:18:08 PM) ClassBot: mhall119 asked: any kind of vector graphics?
(02:18:17 PM) rickspencer3: could be, but PyGame is really sprite based
(02:18:42 PM) rickspencer3: vector graphics is how games like asteroids and battlezone, and starcastle and stuff were created
(02:18:50 PM) rickspencer3: but sprites are when you use images
(02:19:05 PM) ClassBot: M49 asked: Does pygame have any dialog for key configuration?
(02:19:09 PM) rickspencer3: not that I know of
(02:19:22 PM) rickspencer3: that would be a nice thing to add to a quickly template though, wouldn't it
(02:19:32 PM) rickspencer3: I would guess that you would just use PyGtk for this
(02:19:40 PM) rickspencer3: create it yourself
(02:19:50 PM) rickspencer3: ok
(02:19:55 PM) rickspencer3: let's move on to sprites
(02:20:08 PM) rickspencer3: A sprite is basically an image in an arcade game
(02:20:13 PM) rickspencer3: pygame handles blitting for you, which is quite nice
(02:20:17 PM) rickspencer3: I'll discuss blitting in a bit
(02:20:29 PM) rickspencer3: If you wrote games long ago, you may remember rendering a sprite strip to an offscreen graphics world
(02:20:37 PM) rickspencer3: and then selecting bits of that strip and copying them to the onscreen graphics world
(02:20:48 PM) rickspencer3: pygame does not reqiure that at all
(02:20:55 PM) rickspencer3: it is much more fun and easy
(02:21:03 PM) rickspencer3: you just subclass the Sprite class
(02:21:38 PM) rickspencer3: one key thing a sprite has is an Pygame.Image member, called image
(02:21:44 PM) rickspencer3: I usually like to keep the original image around, as if you keep rotating the image and such, it will get more and more distorted over time
(02:22:08 PM) rickspencer3: so when I create a sprite object, I set up the image member like this:
(02:22:14 PM) rickspencer3:   self.masterImage = pygame.image.load(path_to_image_file)
(02:22:14 PM) rickspencer3:   self.image = self.masterImage
(02:22:30 PM) rickspencer3: this is all in the BastSprite class, btw, if you want to see the code in situ
(02:22:37 PM) rickspencer3: a sprite also has a "virtual" function, called update
(02:22:43 PM) rickspencer3: update is supposed to be called to tell a sprite to update it's data for a tick
(02:22:50 PM) rickspencer3: you set things like the sprite's x, y coordinates in this function, and such
(02:22:59 PM) rickspencer3: all your sprites should use this because you can then add a sprite to a RenderUpdates sprite group
(02:23:23 PM) rickspencer3: don't add your own custom named method to do this, or you will lose a lot of benefits of PyGame
(02:23:42 PM) rickspencer3: call update for that group, which will call update for each sprite
(02:23:48 PM) rickspencer3: you create a sprite group like this:
(02:23:54 PM) rickspencer3: enemies = pygame.sprite.RenderUpdates()
(02:24:01 PM) rickspencer3: and then you can add an sprite to that group
(02:24:05 PM) rickspencer3: enemies.add(create_enemy())
(02:24:13 PM) rickspencer3: you can also empty a sprite group
(02:24:17 PM) rickspencer3: enemies.empty()
(02:24:24 PM) rickspencer3: useful when you are resetting a level or something
(02:24:29 PM) rickspencer3: as we'll see later, sprite groups also help lots with collision detection
(02:24:43 PM) rickspencer3: back to sprites though
(02:24:47 PM) rickspencer3: so you have an image on a sprite
(02:24:53 PM) rickspencer3: you also need to define an x and y for your sprite
(02:25:03 PM) rickspencer3: I in my sprite subclass, I might set x and y randomly like this:
(02:25:03 PM) rickspencer3:    self.x = random.randint(0,300)
(02:25:03 PM) rickspencer3:    self.y = random.randint(0,140)
(02:25:14 PM) rickspencer3: (only when the sprite is created, of course)
(02:25:22 PM) rickspencer3: but then on each call to update(), just change the x and/or y if the sprite is moving
(02:25:31 PM) rickspencer3: sprites also have a "kill()" member function
(02:25:38 PM) rickspencer3: calling kill causes the sprite to be removed from the playing surface
(02:25:43 PM) rickspencer3: questions before we talk about updating the display?
(02:26:14 PM) ClassBot: mhall119 asked: can you change the image on update?
(02:26:17 PM) rickspencer3: yes
(02:26:20 PM) rickspencer3: absolutely
(02:26:30 PM) rickspencer3: you can also rotate it and other things
(02:26:38 PM) rickspencer3: my sprites usually have an "explode" method
(02:27:00 PM) rickspencer3: which causes a stage of an explosion to be displayed with each tick in update
(02:27:20 PM) rickspencer3: so if you look at crashteroids, changing images on update is how I get the explosion effect
(02:27:31 PM) ClassBot: mhall119 asked: Like, to animate your sprite
(02:27:34 PM) rickspencer3: totally
(02:27:38 PM) rickspencer3: like if you had a tank
(02:27:44 PM) rickspencer3: and it was going to shoot a missle
(02:28:06 PM) rickspencer3: you could have swap in images that make it look like a little guy loading the turret or something
(02:28:18 PM) ClassBot: mhall119 asked: can you layer images to make a single sprite image?
(02:28:21 PM) rickspencer3: hmmm
(02:28:23 PM) rickspencer3: not that I know of
(02:28:40 PM) rickspencer3: I don't think a sprite can composit images like that
(02:29:13 PM) rickspencer3: ok
(02:29:14 PM) rickspencer3: so now you have your event loop running, your game surface set up, and some sprites created
(02:29:27 PM) rickspencer3: now we need to actually draw the sprites
(02:29:34 PM) rickspencer3: for sprites that are not in a sprite group, we need to blit them
(02:29:41 PM) rickspencer3: fortunately this does not involve the crazy gworld hackery of days of hold
(02:29:48 PM) rickspencer3: pygame gives us a blit method
(02:29:52 PM) rickspencer3: blitting is essentially the act of repainting parts of the screen that have changed
(02:29:58 PM) rickspencer3: without blitting, the whole screen repaints and appears to flash
(02:30:03 PM) rickspencer3: so to blit the blackgroud, we'll use the screen to blit
(02:30:13 PM) rickspencer3: remember when we created teh screen object? now it's time to use it
(02:30:20 PM) rickspencer3: screen.blit(background, [0,0])
(02:30:33 PM) rickspencer3: I think that blitting the background like this basically repaints the whole background image over everything
(02:30:46 PM) rickspencer3: if you are tuning for a very slow platform, or a game with lots and lots of sprites, you may not want do it this way or you get flashing
(02:30:57 PM) rickspencer3: in those cases you might need to track the parts of the background exposed by moving sprites
(02:31:03 PM) rickspencer3: this is a bit of work, but probably will not be necessary for you to do
(02:31:11 PM) rickspencer3: I certainly have never run into trouble with this
(02:31:12 PM) mohi1 is now known as mohi_
(02:31:26 PM) rickspencer3: for all I know, blit() is smart enough to not cause flashing
(02:31:31 PM) mohi_ is now known as mohi1
(02:31:32 PM) rickspencer3: to blit an individual sprite, you have to tell the screen the image to use and the rect, or xy coords and dimensions, for hte sprite to blit
(02:31:41 PM) rickspencer3: So if we have a sprite object called "g", we blit it like this:
(02:31:47 PM) rickspencer3: screen.blit(g.image, g.rect)
(02:31:56 PM) rickspencer3: sprites in a sprite group are a bit easier to blit
(02:32:01 PM) rickspencer3: you can tell the sprite group to blit them all:
(02:32:06 PM) rickspencer3:  enemies.draw(screen)
(02:32:11 PM) rickspencer3: this blits all the enemies for you
(02:32:25 PM) rickspencer3: we do this each time through the loop
(02:33:03 PM) rickspencer3: in ViewTick()
(02:33:10 PM) rickspencer3: question before we discuss collision detection?
(02:33:25 PM) ClassBot: w1nGNUtz asked: will you explain anitmations? I'd like to understand how to animate an sprite using the clock
(02:33:28 PM) rickspencer3: yeah
(02:33:39 PM) rickspencer3: so let's say you want have an animation that shows a guy walking
(02:33:54 PM) rickspencer3: create enough images to show taking a step smoothly
(02:34:05 PM) rickspencer3: I'll guess it will take 8 frames to show smooth step
(02:34:13 PM) rickspencer3: in the sprites update method
(02:34:28 PM) rickspencer3: swap in the next image each time through
(02:34:31 PM) rickspencer3: for example
(02:34:37 PM) rickspencer3: when I show a guy exploding
(02:34:49 PM) rickspencer3: I have 8 frames or so showing the explosion
(02:35:05 PM) rickspencer3: named ship_explode_1.png to ship_explode_8.png
(02:35:21 PM) rickspencer3: (or named similary, I forget exactly what I named them)
(02:35:34 PM) rickspencer3: then I keep track in the update method of a counting variable
(02:35:42 PM) rickspencer3: let me get the exact code, hold on
(02:36:13 PM) rickspencer3:   if self.exploding:
(02:36:13 PM) rickspencer3:    #do an explosion image for each tick
(02:36:13 PM) rickspencer3:    self.explodestage += 1
(02:36:13 PM) rickspencer3:    e = self.explodestage
(02:36:13 PM) rickspencer3:    if e < 8:#there are 7 explosion images
(02:36:13 PM) rickspencer3:     e = str(e)
(02:36:15 PM) rickspencer3:     self.masterImage = pygame.image.load(crashteroidsconfig.guy_explode_stage + e  + ".png")
(02:36:17 PM) rickspencer3:     self.updateImage()
(02:36:45 PM) rickspencer3: updateImage is a function I wrote that rotates the image and does some other stuff
(02:37:18 PM) rickspencer3: but essentially sets the sprites image member, like this:
(02:37:18 PM) rickspencer3: self.image = pygame.transform.rotate(self.masterImage,self.orientation)
(02:37:39 PM) rickspencer3: (if you weren't doing rotation and stuff, you could just assign the new image directly)
(02:37:57 PM) ClassBot: mhall119 asked: does pygame offer any kind of "damage" data to tell what parts of the screen should be repainted?
(02:38:02 PM) rickspencer3: I don't know
(02:38:17 PM) rickspencer3: I always let the sprites blit() method take care of that for me
(02:38:24 PM) rickspencer3: I don't think you'll to track this
(02:38:39 PM) rickspencer3: back in the day, you would always track the previous rect for the sprites location
(02:38:52 PM) rickspencer3: so that you could repaint it with the background
(02:39:01 PM) rickspencer3: but I don't think PyGame requires this
(02:39:22 PM) ClassBot: gooomba asked: Is pygame also suited for non-arcade games like chess for example?
(02:39:25 PM) rickspencer3: yes
(02:39:36 PM) rickspencer3: it wuold be good for board games for sure
(02:40:09 PM) rickspencer3: ok, so collision detection
(02:40:14 PM) rickspencer3: so let's say when an enemy rams our guy, we want both to be killed
(02:40:24 PM) rickspencer3: but how to tell when an ememy hits?
(02:40:32 PM) rickspencer3: detecting overlapping sprites is called "collision detection" and is handled quite well by PyGame
(02:40:36 PM) rickspencer3: this saves you tons of work
(02:40:57 PM) rickspencer3: (believe me, tracking rects and writing code to detect overlapping rects is not fun or easy!)
(02:41:03 PM) rickspencer3: you can detect if two sprites have collided using pygame.sprite.spritecollide (), collideany, or groupcollide
(02:41:13 PM) rickspencer3: so let's see if our guy, called g, has collided with an enemy
(02:41:13 PM) rickspencer3:   e = pygame.sprite.spritecollideany(g, enemies)
(02:41:29 PM) rickspencer3: call this each tick
(02:41:47 PM) rickspencer3: I always have specific function to handle collision detection
(02:41:56 PM) rickspencer3: if the guy has not collided with any enemies, e will be None
(02:42:02 PM) rickspencer3: but when e is not None, that means boom
(02:42:05 PM) rickspencer3: "boom"
(02:42:10 PM) rickspencer3: let's say your guys shoots bullets
(02:42:15 PM) rickspencer3: those bullets will most likely be managed in a sprite group
(02:42:21 PM) rickspencer3: so we should also see if any enemies were killed by bullets
(02:42:26 PM) rickspencer3: hits_dict = pygame.sprite.groupcollide(bullets, enemies, False, False)
(02:42:41 PM) rickspencer3: since we are asking for a collision between groups of sprites
(02:42:46 PM) rickspencer3: here you get back a dictionary
(02:42:50 PM) rickspencer3: the keys are sprites from the first sprite group
(02:42:54 PM) rickspencer3: and the values are keys from the second sprite group
(02:42:59 PM) rickspencer3: so then we can just go through and kill each bullet and enemy for the collision:
(02:43:06 PM) rickspencer3:    for b in hits_dict:
(02:43:07 PM) rickspencer3:     for e in hits_dict[b]:
(02:43:07 PM) rickspencer3:      e.kill()
(02:43:07 PM) rickspencer3:      b.kill()
(02:43:17 PM) rickspencer3: ok, next and last section is on playing sounds
(02:43:21 PM) rickspencer3: any questions first?
(02:43:33 PM) ClassBot: mhall119 asked: does pygame have any kind of scene-graph, to animate sprites without writing a whole bunch of python code?
(02:43:44 PM) rickspencer3: if I know what that meant, I would give you a pithy answer
(02:43:54 PM) rickspencer3: unfortunately, I have to say "I don't know"
(02:44:06 PM) ClassBot: M49 asked: Does it track only bounding boxes or does it use trasparency information from sprites?
(02:44:13 PM) rickspencer3: the bounding box one
(02:44:22 PM) rickspencer3: actually, you can set the rect for the sprite specifically
(02:44:38 PM) rickspencer3: I often set the rect for the sprite to be slightly smaller than the image itself
(02:44:49 PM) rickspencer3: this give a nice effect where two sprites can just touch
(02:44:52 PM) rickspencer3: and not explode
(02:44:59 PM) rickspencer3: it gives a nice "near miss" effect
(02:45:18 PM) rickspencer3: it also lets you tune each rect to different shaped sprites
(02:45:30 PM) rickspencer3: note that the rect is only used for collision detection so far as I know
(02:45:47 PM) rickspencer3: so the whole image will always be displayed
(02:46:25 PM) rickspencer3: ok, finally, sounds
(02:46:26 PM) rickspencer3: we use the Pygame.mixer module to manage sounds
(02:46:31 PM) rickspencer3: we need to initialize it before we use it:
(02:46:34 PM) rickspencer3: pygame.mixer.init()
(02:46:43 PM) rickspencer3: now I'll load an explosion sound to play when my guy explodes:
(02:46:49 PM) rickspencer3: self.explosionSound = pygame.mixer.Sound(path_to_sound_file)
(02:47:03 PM) rickspencer3: let's assume this sound is a bit loud, we can adjust the volume as we like:
(02:47:07 PM) rickspencer3:   self.explosionSound.set_volume(.2)
(02:47:16 PM) rickspencer3: then we can play the sound:
(02:47:21 PM) rickspencer3:    self.explosionSound.play()
(02:47:28 PM) rickspencer3: we can stop it too if it's going too long:
(02:47:38 PM) rickspencer3:    self.explosionSound.stop()
(02:47:46 PM) rickspencer3: the mixer module is quite rich
(02:47:52 PM) rickspencer3: there are channels, fades, and all kinds of stuff
(02:48:01 PM) rickspencer3: so
(02:48:12 PM) rickspencer3: that was the last "chapter" for the class
(02:48:14 PM) rickspencer3: we're a bit early
(02:48:17 PM) rickspencer3: any questions?
(02:48:30 PM) ClassBot: gooomba asked: what are the "False"-arguments in groupcollide() for?
(02:48:37 PM) rickspencer3: shucks, I forget
(02:48:41 PM) ***rickspencer3 looks at docs
(02:49:42 PM) rickspencer3: huh
(02:49:43 PM) rickspencer3: http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.groupcollide
(02:49:48 PM) rickspencer3: no longer seems part of the API
(02:49:58 PM) rickspencer3: I guess I can just remove that from the template
(02:50:10 PM) ClassBot: mhall119 asked: what audio formats does mixer support?
(02:50:14 PM) rickspencer3: I know ogg and wav
(02:50:19 PM) rickspencer3: don't know what else, sorry

MeetingLogs/OpWeek1003/PyGame (last edited 2010-03-03 19:59:59 by pool-71-182-100-128)