FreeformDesignGooCanvasPyGame

Developing with Freeform Design Surfaces: GooCanvas and PyGame - Instructors: rickspencer3

   1 [18:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/08/%23ubuntu-classroom.html following the conclusion of the session.
   2 [18:02] <rickspencer3> sooo
   3 [18:02] <rickspencer3> hi everybody
   4 [18:02] <rickspencer3> you just missed my best stff
   5 [18:03] <rickspencer3> I was talking in teh wrong channel for the last 2 minutes :)
   6 [18:03] <rickspencer3> so, let's try again ...
   7 [18:03] <rickspencer3> Hello all. Today I will discuss 2 of the APIs that I have used to have a lot of fun with programming for Ubuntnu.
   8 [18:03] <rickspencer3> These APIs are GooCanvas and PyGame. They are both similar in the sense that they provide you with a 2d surface on which you can construct interactive GUIs for your users.
   9 [18:03] <rickspencer3> seriously, I have had tons of fun writing apps with thes over the years
  10 [18:04] <rickspencer3> However, I fonud them to have different strengths and weaknesses. If you choose the correct API it will be more easy and more fun to write yoru app.
  11 [18:04] <rickspencer3> so, why goocanvas or pygame at all?
  12 [18:04] <rickspencer3> A typical desktop app is composed of widgets that a user is used to. Like buttons, entry boxes, and such.
  13 [18:04] <rickspencer3> For these desktop apps, I strongly recommend sticking with PyGtk for the time being.
  14 [18:04] <rickspencer3> like the next year, I think
  15 [18:05] <rickspencer3> PyGtk is the way to go for what I call "boxy" apps
  16 [18:05] <rickspencer3> I use pygtk all the time
  17 [18:05] <rickspencer3> However, sometimes part of an app, or pretty much a whole app, won't need buttons and lists and entry boxes, but will need to display, modify, or animate images, drawings, etc...
  18 [18:05] <rickspencer3> Sometimes in response to user input, sometimes not.
  19 [18:05] <rickspencer3> My goal for this session is to help you choose the right API for those kinds of apps, and to get you started with them.
  20 [18:05] <rickspencer3> please ask questions at any time
  21 [18:05] <rickspencer3> I will check for questions often
  22 [18:06] <rickspencer3> I'll start with GooCanvas because I already did a session on this last year, so there is lots of material.
  23 [18:06] <rickspencer3> https://wiki.ubuntu.com/UbuntuOpportunisticDeveloperWeek/GooCanvas
  24 [18:06] <rickspencer3> basically, I shall copy and past from there, answering quetsions as I go
  25 [18:07] <rickspencer3> thoguh I may skip some to leave room for pygame
  26 [18:07] <rickspencer3> So what is a goocanvas?
  27 [18:07] <rickspencer3> A goocanvas is a 2d composing surface
  28 [18:07] <rickspencer3> You can use it to make pretty much any kind of image
  29 [18:08] <rickspencer3> It's kind of like an api around a drawing program
  30 [18:08] <rickspencer3> So you can have a ton of fun using a goocanvas, because you are pretty much freed from the constraints of a widget library in creating your UI
  31 [18:08] <rickspencer3> goocanvas is cairo under the covers
  32 [18:08] <rickspencer3> and is designed to easily integrate into your gtk app
  33 [18:08] <rickspencer3> So let's add a goocanvas to a pygtk app
  34 [18:09] <rickspencer3> Add it just like a normal pygtk widget
  35 [18:09] <rickspencer3> #set up the goo canvas
  36 [18:09] <rickspencer3> self.goo_canvas = goocanvas.Canvas() self.goo_canvas.set_size_request(640, 480) self.goo_canvas.show()
  37 [18:09] <rickspencer3> tada!
  38 [18:09] <rickspencer3> you have a goocanvas
  39 [18:09] <rickspencer3> Be sure to set the size, otherwise it defaults to 1000,1000, it does not default to the size alloted to it in your window.
  40 [18:09] <rickspencer3> Handle window resizing to resize your goocanvas as well
  41 [18:09] <rickspencer3> !!
  42 [18:10] <rickspencer3> the goocanvas won't automatically change size if it's container changes size
  43 [18:10] <rickspencer3> For example, if your goocanvas is in a VBox, you can do this:
  44 [18:10] <rickspencer3> rect = self.builder.get_object("vbox2").get_allocation() self.goo_canvas.set_bounds(0,0,rect.width,rect.height)
  45 [18:10] <rickspencer3> remember the root item for your goocanvas, you'll need it later often self.root = self.goo_canvas.get_root_item()
  46 [18:10] <rickspencer3> The "root" is like the root of an item tree in XML
  47 [18:10] <rickspencer3> So now that we have a goocanvas, we need to add "Items" to it.
  48 [18:10] <rickspencer3> Anything that can be added to a goocanvas is an Item. It get's it's capabilities by inheriting from ItemSimple, and by implementing the Item interface.
  49 [18:11] <rickspencer3> Let's add an item to the goocanvas to get a look at how it works in general.
  50 [18:11] <rickspencer3> We'll start by adding an image.
  51 [18:11] <rickspencer3> First, you need to get a gtk.pixbux for your image:
  52 [18:11] <rickspencer3> pb = gtk.gdk.pixbuf_new_from_file(path)
  53 [18:11] <rickspencer3> Then you calculate where you want the image to show on the goocanvas. You'll need a top and a left to place most items on a goo canvas.
  54 [18:11] <rickspencer3> For example, to center the image, I do this:
  55 [18:11] <rickspencer3> cont_left, cont_top, cont_right, cont_bottom = self.goo_canvas.get_bounds() img_w = pb.get_width() img_h = pb.get_height() img_left = (cont_right - img_w)/2 img_top = (cont_bottom - img_h)/2
  56 [18:11] <rickspencer3> it's a bit hard to read, I guess
  57 [18:12] <rickspencer3> but I basically just calculated the pixel center of the goocanvas
  58 [18:12] <rickspencer3> and stored the "bounds" that the calculation returned
  59 [18:12] <rickspencer3> Now I am ready to create the item.
  60 [18:12] <rickspencer3> Note that I create the Item, but there is nothing like goocanvas.add(item) rather, when you create the item, you set it's parent property.
  61 [18:12] <rickspencer3> The parent property is the root of the goocanvas
  62 [18:12] <rickspencer3> This is why I remember the root
  63 [18:12] <rickspencer3> goocanvas.Image(pixbuf=pb,parent=self.root, x=img_left,y=img_top)
  64 [18:13] <rickspencer3> This basic pattern is how you add all other types of items.
  65 [18:13] <rickspencer3> decide where to put the item, and set it's parent property to the root of the goocanvas.
  66 [18:13] <rickspencer3> To remove the item from the goocanvas, you don't tell the goocanvas to remove it
  67 [18:13] <rickspencer3> rather you tell the item to remove itself
  68 [18:13] <rickspencer3> item.remove()
  69 [18:13] <rickspencer3> any questions at all so far?
  70 [18:14] <rickspencer3> In a moment, I'll go on to discuss the types of things that you can add to a goocanvas
  71 [18:14] <rickspencer3> In my mind, there are really 3 types of items
  72 [18:14] <rickspencer3> normal items that you add to draw the stuff you want
  73 [18:14] <rickspencer3> this includes:
  74 [18:14] <rickspencer3> Ellipse, Image, Path, Polyline, Rect, and Text
  75 [18:15] <rickspencer3> the second type is for layout
  76 [18:15] <rickspencer3> Layout and gruop items include:
  77 [18:15] <rickspencer3> Group, Grid, and Table
  78 [18:15] <rickspencer3> then finally,
  79 [18:15] <rickspencer3> there is also Widget. Widget is pretty cool.
  80 [18:15] <rickspencer3> You can add a gtk widget to your goocanvas, but note that it will live in a world seperate from the goocanvas
  81 [18:15] <rickspencer3> In other words, gtk.Widgets won't be rendered if you create images form our goocanvas and such
  82 [18:15] <rickspencer3> However, this is a cool way to add in situ editing to your goocanvas
  83 [18:15] <rickspencer3> We'll just be talking about normal items for the rest of this class though
  84 [18:15] <rickspencer3> So what are some of the things that you do with an item? Well, you compose with it. So you scale it, move it, rotate it, change it's z-order and such
  85 [18:16] <rickspencer3> For a lot of things that you want to do with an item, you use set_property and get_property
  86 [18:16] <rickspencer3> For example, to set the a might make a Text item like this:
  87 [18:16] <rickspencer3> txt = goocanvas.Text(parent=self.root,text="some text", x=100, y=100, fill_color=self.ink_color)
  88 [18:16] <rickspencer3> then change the text in it like this:
  89 [18:16] <rickspencer3> txt.set_property("text","new text")
  90 [18:16] <rickspencer3> Let's look at colors for a moment. There are generally two color properties to work with, stork-color, and fill-color
  91 [18:16] <rickspencer3> If you've ever used a tool ink inkscape, this will make sense you to
  92 [18:16] <rickspencer3> for something like a rect, stroke-color is the outline of the rectangle, and fill-color is the inside of the rectangle
  93 [18:17] <rickspencer3> any questions so far?
  94 [18:17] <rickspencer3> okay, moving on
  95 [18:17] <rickspencer3> You can move, rotate, resize, and skew items
  96 [18:17] <rickspencer3> The APIs for doing this are intuitive, imho
  97 [18:17] <rickspencer3> To grow something by 10%
  98 [18:17] <rickspencer3> item.scale(1.1,1.1)
  99 [18:17] <rickspencer3> And to shrink it a bit:
 100 [18:17] <rickspencer3> item.scale(.9,.9)
 101 [18:18] <rickspencer3> Note that the items always consider themeselves to be their original size and orientation, so doing this will cause an item to grow twice: item.scale(1.1,1.1) item.scale(1.1,1.1)
 102 [18:18] <rickspencer3> Now, when you start rotating and skewing items, some pretty confusing stuff can start happening
 103 [18:18] <rickspencer3> Essentially, an item tracks it's own coordinate system, and doesn't much care about the goocanvas's coordinate system
 104 [18:18] <rickspencer3> So if you rotate an item, for example, the coordinate systems are totally out of whack
 105 [18:18] <rickspencer3> So if you pass the x/ys to an item based on the canvas's coordinate system, it can get waaaay out of whack
 106 [18:19] <rickspencer3> Fortunately, goocanvas has some functions on it that just do these transforms for me
 107 [18:19] <rickspencer3> let's say I catch a mouse click event on an item
 108 [18:19] <rickspencer3> and I want to know where on the item the click happened
 109 [18:19] <rickspencer3> well, the click coordinate are reported in the goocanvas's coordinate system, so I need to do a quick calculation to determine where the click happened on the item:
 110 [18:19] <rickspencer3> e_x, e_y = self.goo_canvas.convert_to_item_space(self.selected_item,event.x,event.y)
 111 [18:19] <rickspencer3>  
 112 [18:19] <rickspencer3> so, I used all of these facilities and more to make Photobomb
 113 [18:20] <rickspencer3> you can check out Photobomb if you want to see some of the things that you can do with a GooCanvas
 114 [18:20] <rickspencer3> Photobomb is essentially an image editor
 115 [18:20] <rickspencer3> that made it a good candidate for GooCanvas
 116 [18:20] <rickspencer3> however, I've also written games
 117 [18:20] <rickspencer3> and PyGame is a better API for that
 118 [18:21] <rickspencer3> before I go on to PyGame, any questions on Googcanvas?
 119 [18:21] <rickspencer3> GooCanvas*
 120 [18:21] <ClassBot> bUbu87 asked: how do you work with svg and gooCanvas? is there a simple way to load an svg to a canvas and keep it scaled all the time?
 121 [18:21] <rickspencer3> indeed!
 122 [18:21] <rickspencer3> there are shapes and paths that are all described with svg
 123 [18:22] <rickspencer3> I've actually exported content from InkScape into a Goocanvas in the past
 124 [18:22] <rickspencer3> let's look at paths and clipping for an example
 125 [18:22] <rickspencer3> A path is essentially a "squiggle"
 126 [18:22] <rickspencer3> It is defiened by a string that gets parsed into x,y coords, and then drawn with a bezier curve formula applied
 127 [18:22] <rickspencer3> ^for those not totally familiar with svg
 128 [18:22] <rickspencer3> here is a string that described a scribble:
 129 [18:22] <rickspencer3> line_data = "M 4.0 4.0C4.0 4.0 5.0 4.0 5.0 4.0 5.0 4.0 6.0 4.0 6.0 3.0 10.0 1.0 13.0 2.0 9.0 15.0 6.0 36.0 28.0 11.0 28.0 11.0 29.0 11.0 33.0 12.0 33.0 15.0 32.0 19.0 27.0 51.0 27.0 53.0 27.0 54.0 27.0 54.0 27.0 54.0 36.0 49.0 37.0 49.0"
 130 [18:22] <rickspencer3> then I can make a path out of this:
 131 [18:22] <rickspencer3> path = goocanvas.Path(data=line_data, parent=self.root, line_width=self.ink_width, stroke_color=self.ink_color)
 132 [18:23] <rickspencer3> so this will draw the path in the goocancas
 133 [18:23] <rickspencer3> Now, a path is also useful because you can use it to clip another object
 134 [18:23] <rickspencer3> You don't use a path object for this, just the string item.set_property("clip-path",line_data)
 135 [18:23] <rickspencer3> shall I move on to PyGame?
 136 [18:24] <rickspencer3> I put the Pygame notes here:
 137 [18:24] <rickspencer3> https://wiki.ubuntu.com/UbuntuOpportunisticDeveloperWeek/PyGame
 138 [18:24] <rickspencer3> PyGame is an API that is also for 2d surfaces.
 139 [18:24] <rickspencer3> It is best for applications where there is a lot of updating of animation without user input (especially as it uses blitting).
 140 [18:24] <rickspencer3> It has a set of baseclasses that make it easier to manage and change teh state of objects.
 141 [18:24] <rickspencer3> It also has collision detection routines, which is very useful in game programming.
 142 [18:25] <rickspencer3> So, net/net if you are doing something that is a game, or game-like, you're likely to want to use pygame, not GooCanvas
 143 [18:25] <rickspencer3> pygame has fairly good reference documentation here: http://pygame.org/docs/ref/index.html
 144 [18:25] <rickspencer3> There are also lots of tutorials available on the web. However, it's important to note that I use pygame a bit differently than they do in the typical tutorials.
 145 [18:25] <rickspencer3> ^^WARNING WARNING^^
 146 [18:25] <rickspencer3> Tutorials typically have you create a pygame window to display you game in, and then create a loop with a pygame clock object.
 147 [18:25] <rickspencer3> I don't do it this way anymore. Now I prefer to embed a pygame surface into a Gtk app. This has some benefits to me:
 148 [18:26] <rickspencer3>  I can use menus for the GUI for things like starting games, pausing etc...
 149 [18:26] <rickspencer3> I can use dialog boxes for things like hight scores or collecting information from users
 150 [18:26] <rickspencer3> If you try to do these things from within a pygame loop, the gtk.main lool clashes with your pygame loop, and everything is just really hard to use.
 151 [18:26] <rickspencer3> So, for the approach I take, I have three samples that you can look at at your leisure:
 152 [18:26] <rickspencer3> 1. sample_game code:
 153 [18:26] <rickspencer3> http://bazaar.launchpad.net/~rick-rickspencer3/+junk/pygame-pygtk-example/view/head:/game.py
 154 [18:26] <rickspencer3> blog posting:
 155 [18:26] <rickspencer3> http://theravingrick.blogspot.com/2011/08/using-pygame-in-pygtk-app.html
 156 [18:27] <rickspencer3> This is the simplest code that I could make to demonstrate how to embed pygame and handle input.
 157 [18:27] <rickspencer3>  
 158 [18:27] <rickspencer3> 2. jumper:
 159 [18:27] <rickspencer3> http://bazaar.launchpad.net/~rick-rickspencer3/+junk/jumper/view/head:/jumper/JumperWindow.py
 160 [18:27] <rickspencer3> This is only slightly more complex. It show how animate a sprite by changing the image, and show collision detection and playing a sound.
 161 [18:27] <rickspencer3>  
 162 [18:27] <rickspencer3> 3. smashies:
 163 [18:27] <rickspencer3> http://bazaar.launchpad.net/~rick-rickspencer3/+junk/smashies/files/head:/smashies/
 164 [18:27] <rickspencer3> This is a full blown game which I have almost completed. I'm considering selling it in the software center when I am done. This one handles all the complexity of lives, scores, pausing, etc...
 165 [18:27] <rickspencer3>  
 166 [18:27] <rickspencer3> smashies is essentially an asteroids clone
 167 [18:27] <rickspencer3> I'm stilling thinking of a good name, and I need to replace some artwork
 168 [18:28] <rickspencer3> anywho ...
 169 [18:28] <rickspencer3> For this tutorial, we'll focus on jumper since it has an animated Sprite.
 170 [18:28] <rickspencer3> before I dive in, any general questions about PyGame?
 171 [18:28] <rickspencer3> okee let's go
 172 [18:28] <rickspencer3> The overall approach is simple
 173 [18:28] <rickspencer3> 1. set up a drawing area in Gtk Window
 174 [18:29] <rickspencer3> 2. add pygame sprites to it
 175 [18:29] <rickspencer3> 3. handle keyboard input from the gtk window
 176 [18:29] <rickspencer3> 4. periodically call an update function to:
 177 [18:29] <rickspencer3> a. update the data for the sprites
 178 [18:29] <rickspencer3> b. update the view
 179 [18:29] <rickspencer3> c. detect collisions and respond to them
 180 [18:30] <rickspencer3> A game typically needs a background image. I put a background image and the other images and sounds in the data/media directory. Once you get the background image painting, it means you've got the main part of the game set up. So, we'll go through this part with patience.
 181 [18:30] <rickspencer3> ^note that jumper is a Quickly app
 182 [18:30] <rickspencer3> I put all the code in JumperWindow, so it's easy to see in one place.
 183 [18:30] <rickspencer3> let's start making it work
 184 [18:30] <rickspencer3> You need to import 3 modules:
 185 [18:30] <rickspencer3> import pygame
 186 [18:30] <rickspencer3> import os
 187 [18:30] <rickspencer3> import gobject
 188 [18:31] <rickspencer3> You'll see why you need these each in turn.
 189 [18:31] <rickspencer3> First we want to create a a pygame.Image object to hold the background. Once we have that, we can use pygame functions to paint it.
 190 === Odd-rationale_ is now known as Odd-rationale
 191 [18:31] <rickspencer3> Since Jumper is a Quickly app, it I can use "get_media_file" to load it.
 192 [18:31] <rickspencer3> I mean load it from the disk
 193 [18:31] <rickspencer3> So I make the background in these 2 lines of code in the finish_initializing function:
 194 [18:31] <rickspencer3>         bg_image = get_media_file("background.png")
 195 [18:31] <rickspencer3>         self.background = pygame.image.load(bg_image)
 196 [18:31] <rickspencer3> Before I use it, I have to set up the pygame environment though. I do this by adding a gtk.DrawingArea to the gtk.Window, and telling the os module to use the windows xid as a drawing surface.
 197 [18:32] <rickspencer3> You can't just do that in the finish_initializing function, though. This is because drawingarea1 may not actually have an xid yet. This is easy to handle by connecting to the drawing area's "realize" signal. At that point, it will have an xid, and you can set up the environment.
 198 [18:32] <rickspencer3> basically, you need to make sure that the drawingarea has been put on the screen, otherwise, it has no xid
 199 [18:32] <rickspencer3> So, connect to the signal in finish initalizing:
 200 [18:32] <rickspencer3>         self.ui.drawingarea1.connect("realize",self.realized)
 201 [18:32] <rickspencer3> and then write the self.realized function:
 202 [18:32] <rickspencer3>     def realized(self, widget, data=None):
 203 [18:32] <rickspencer3>         os.putenv('SDL_WINDOWID', str(self.ui.drawingarea1.window.xid))
 204 [18:32] <rickspencer3>         pygame.init()
 205 [18:32] <rickspencer3>         pygame.display.set_mode((300, 300), 0, 0)
 206 [18:32] <rickspencer3>         self.screen = pygame.display.get_surface()
 207 [18:33] <rickspencer3> This function intializes pygame, and also create a pygame.Screen object that you need for drawing.
 208 [18:33] <rickspencer3> so now we have a Gtk.DrawingArea ready to be a PyGame surface
 209 [18:33] <rickspencer3> any questions before I show how to put the game background on it?
 210 [18:34] <rickspencer3> ok, moving on
 211 [18:34] <rickspencer3> So now that the drawing area is set up as a pygame surface, we need to actually draw to it.
 212 [18:35] <rickspencer3> Actually, we'll want to periodically update the drawing so that it appears animated. So we want to update it over and over again.
 213 [18:35] <rickspencer3> So after setting up pygame in tghe realized function, add a gobject timeout to recurringly call a function to update the game:
 214 [18:35] <rickspencer3>         gobject.timeout_add(200, self.update_game)
 215 [18:35] <rickspencer3> the funciton update_game will be called every 200 millliseconds. For a real game, you might want to make it update more often.
 216 [18:35] <rickspencer3> So, now we need write the udpate_game function. Eventually it will do a lot more, but for now, it will just tell the game to draw. So we need to write the draw_game function as well.
 217 [18:35] <rickspencer3>     def update_game(self):
 218 [18:35] <rickspencer3>         self.draw_game()
 219 [18:35] <rickspencer3>         return True
 220 [18:35] <rickspencer3>     def draw_game(self):
 221 [18:35] <rickspencer3>         self.screen.blit(self.background, [0,0])
 222 [18:35] <rickspencer3>         pygame.display.flip()
 223 [18:35] <rickspencer3> Note that update_game returns True. This is important, because if it returns anything else, gobject will stop calling it.
 224 [18:36] <rickspencer3> Looking at draw_game a little more, the first line tells the Screen object to "blit" the background. This means to only update the parts that have changed.
 225 [18:36] <rickspencer3>  This keeps the game from flickering on slower systems. We also pass in x/y coordinates to tell it to update the whole background.
 226 [18:36] <rickspencer3> This doesn't paint to the screen yet, though. It just prepares it in memory.
 227 [18:36] <rickspencer3> You can call blit a whole bunch of times for different sprites, but until you call pygame.display.flip() they won't actually be painted to the screen.
 228 [18:36] <rickspencer3> In this way, the screen only gets update once, and the animation is smooth.
 229 [18:37] <rickspencer3> Now if you run the game, you should see the background painted.
 230 [18:37] <rickspencer3> before I go on to animating a sprite, any questions?
 231 [18:37]  * rickspencer3 drums fingers
 232 [18:38]  * rickspencer3 scratches head
 233 [18:38]  * rickspencer3 twiddles thumbs
 234 [18:38] <rickspencer3> ok
 235 [18:38] <rickspencer3> At this point you have a drawing surface set up, and you are drawing to it in a loop.
 236 [18:38] <rickspencer3> Now let's add an animated sprite.
 237 [18:38] <rickspencer3> I put 2 png's in the data/media director. One called "guy1.png" and called "guy2.png". We will animate the game by swapping these images back and forth every time the game paints.
 238 [18:38] <rickspencer3> WARNING: I am doing something very wrong!
 239 [18:38] <rickspencer3> Jumper loads the images as needed from disk. In a real game, this is a bad idea. This is a bad idea because that takes IO time, which can slow the game down.
 240 [18:39] <rickspencer3> DON'T DO IT THIS WAY!!!
 241 [18:39] <rickspencer3> It's better to load all the images and sounds at once when the game loads. See smashies for how I do that in the __init__.py file.
 242 [18:39] <rickspencer3> anyway
 243 [18:39] <rickspencer3> I mentioned before that pygame has some useful base classes. One of those base classes is called "Sprite" which is a really old game programming term.
 244 [18:39] <rickspencer3> When adding an object to your game, it's best to derive from sprite. It's easier to manage the data for a sprite that way, and also there are useful pygame functions that expect a Sprite object.
 245 [18:39] <rickspencer3> So, first create the sprite class and an initialization function:
 246 [18:39] <rickspencer3> class Guy(pygame.sprite.Sprite):
 247 [18:39] <rickspencer3>     def __init__(self):
 248 [18:39] <rickspencer3>         pygame.sprite.Sprite.__init__(self)
 249 [18:39] <rickspencer3>         self.animation_stage = 1
 250 [18:39] <rickspencer3>         self.x = 35
 251 [18:39] <rickspencer3>         self.y = 180
 252 [18:39] <rickspencer3>         self.direction = 0
 253 [18:40] <rickspencer3> Next, we'll write a function called "update". You'll see in a bit why it's important to call it "update".
 254 [18:40] <rickspencer3> For this function, check which animation stage to use, and then use that image:
 255 [18:40] <rickspencer3>     def update(self):
 256 [18:40] <rickspencer3>         img = get_media_file("""guy%i.png""" % self.animation_stage)
 257 [18:40] <rickspencer3>         self.image = pygame.image.load(img)
 258 [18:40] <rickspencer3> ^remember don't do it like this
 259 [18:40] <rickspencer3> ^load the image from disk once at the beginning fo the program
 260 [18:40] <rickspencer3> Next, you need to set the "rect" for the Sprite. The rect will be used in any collision detection functions you might use:
 261 [18:40] <rickspencer3>         self.rect = self.image.get_rect()
 262 [18:40] <rickspencer3>         self.rect.x = self.x
 263 [18:40] <rickspencer3>         self.rect.y = self.y
 264 [18:41] <rickspencer3> Finally, update the animation stage.
 265 [18:41] <rickspencer3>         self.animation_stage += 1
 266 [18:41] <rickspencer3>         if self.animation_stage > 2:
 267 [18:41] <rickspencer3>             self.animation_stage = 1
 268 [18:41] <rickspencer3> Now you just need to a "Guy" to your background.
 269 [18:41] <rickspencer3> First, create a guy in the finish_initializing function.
 270 [18:41] <rickspencer3>         self.guy = Guy()
 271 [18:41] <rickspencer3> Since a game will have a lot of sprites, it's easiest to manage sprites as a group
 272 [18:41] <rickspencer3> There is a pygame class for this called a SpriteGroup, which you create by called RenderUpdates.
 273 [18:41] <rickspencer3> So, crate a SpriteGroup and the guy to it:
 274 [18:41] <rickspencer3>         self.sprites =pygame.sprite.RenderUpdates()
 275 [18:41] <rickspencer3>         self.sprites.add(self.guy)
 276 [18:42] <rickspencer3> Remember when we created the update_game function?
 277 [18:42] <rickspencer3> Now you can see how useful the SpriteGroup is.
 278 [18:43] <rickspencer3> You can call "update" on the sprite group, and it will in turn call update on every sprite in it. So add that call to the update_game function:
 279 [18:43] <rickspencer3>         self.sprites.update()
 280 [18:43] <rickspencer3> Now, you also need to tell the Guy to draw. That's easy too with the SpriteGroup. Add this line to draw_game function:
 281 [18:43] <rickspencer3>         self.sprites.draw(self.screen)
 282 [18:43] <rickspencer3> Now when you run the game, each tick the guy will swap images, and it will look like it's moving.
 283 [18:43]  * rickspencer3 phew
 284 [18:44] <rickspencer3> ok, almost done
 285 [18:44] <rickspencer3> any questions?
 286 [18:44] <rickspencer3> WARNING
 287 [18:44] <rickspencer3> Note that I handle keyboard and mouse input very differently than they describe in most pygame tutorials
 288 [18:44] <rickspencer3> Responding to keyboard input is really easy, because you can just use gtk events.
 289 [18:44] <rickspencer3> I have found that you need to attach to the key events for the window, not the drawing area.
 290 [18:45] <rickspencer3> So, to make the guy jump when the user clicks the space bar, I make a key_press_event signal handler, that calls "jump()" on the guy:
 291 [18:45] <rickspencer3>     def key_pressed(self, widget, event, data=None):
 292 [18:45] <rickspencer3>         if event.keyval == 32:
 293 [18:45] <rickspencer3>             self.guy.jump()
 294 [18:45] <rickspencer3> You can look at the jump and update functions in the Guy class to see how a jump was implemented.
 295 [18:45] <rickspencer3> you can track mouse events, key up events, etc.. this way too
 296 [18:46] <rickspencer3> Pygame also has functions for joysticks and stuff, but I haven't used that
 297 [18:46] <rickspencer3> So, that's the essence of creating an animated sprite, which gets you a lot of the way toward making a game.
 298 [18:46] <rickspencer3> We don't have time to delve into everything, but I did want to touch on collisions.
 299 [18:46] <rickspencer3> Assuming that you've added another sprite called self.apple that tries to hit the guy, you can use one of the many pygame collision detection functions in every call to update_game to see if the apple hit the guy:
 300 [18:46] <rickspencer3>         if pygame.sprite.collide_rect(self.guy,self.apple):
 301 [18:46] <rickspencer3>             self.guy.kill()
 302 [18:46] <rickspencer3> BELIEVE ME
 303 [18:46] <rickspencer3> you don't want to write your own collision detection routines
 304 [18:47] <rickspencer3> there are lots of good functions
 305 [18:47] <rickspencer3> ones that compare whole groups of sprites, for example
 306 [18:47] <rickspencer3> If you set the rect for your Sprite subclass, functions like this work well, and are easy.
 307 [18:47] <rickspencer3> I also mentioned sounds.
 308 [18:47] <rickspencer3> Pygame has a really rich set of sound functions.
 309 [18:47] <rickspencer3> The easiest thing to demo is playing a sound from a file, like this:
 310 [18:47] <rickspencer3>             sound_path = get_media_file("beep_1.wav")
 311 [18:47] <rickspencer3>             sound = pygame.mixer.Sound(sound_path)
 312 [18:47] <rickspencer3>             sound.play()
 313 [18:48] <rickspencer3> ....
 314 [18:48] <rickspencer3> and
 315 [18:48] <rickspencer3> that's everything I prepared for this session
 316 [18:48] <rickspencer3> I'm happy to take some questions
 317 [18:48] <rickspencer3> or maybe everyone is busy playing smashies right now
 318 [18:50] <ClassBot> There are 10 minutes remaining in the current session.
 319 [18:51] <rickspencer3> thanks ClassBot

MeetingLogs/appdevweek1109/FreeformDesignGooCanvasPyGame (last edited 2011-09-09 18:26:22 by dpm)