image = pygame.image.load("01_image.png")(if you have your image in a subdirectory then you should use os.path.join() to join your path together because if you do otherwise it could cause problems on different platforms)
screen.blit(image, (50,50))What it does is copy the pixels of the image surface to the screen surface. The position (50,50) is the top left corner of the image. If you try that now you will get a black screen. It is because we have forgot to update the screen. The full screen update is done using pygame.display.flip().
pygame.display.flip()After that it is visible on screen. We have written to the screen surface in the memory before and now we have updated it on the display.
screen.fill((r,g,b))After that you blit your image over it as shown in the first section.
screen.blit(bgd_image, (0,0))We blit it at the top left corner of the screen that is (0,0). The background image bgd_image should have the same size as the screen (or bigger but the clipped parts will not be seen).
image.set_colorkey((255,0,255))You get the source , but try it first by your own.
image.set_alpha(128)This will make the image half transparent using the per surface alpha (not per pixel alpha). Here I use a colorkey too to make the pink border transparent. Well to avoid some problems using alpha and colorkey I have looked it up in the sdl documentation what combinations will work. Its because not any combination is possible and you might then wonder why it does not do what you want. In the documentation is said:
The per-surface alpha value of 128 is considered a special case and is optimized, so it's much faster than other per-surface values.
RGBA->RGB with SDL_SRCALPHA | The source is alpha-blended with the destination, using the alpha channel. SDL_SRCCOLORKEY and the per-surface alpha are ignored. |
RGBA->RGB without SDL_SRCALPHA | The RGB data is copied from the source. The source alpha channel and the per-surface alpha value are ignored. If SDL_SRCCOLORKEY is set, only the pixels not matching the colorkey value are copied. |
RGB->RGBA with SDL_SRCALPHA | The source is alpha-blended with the destination using the per-surface alpha value. If SDL_SRCCOLORKEY is set, only the pixels not matching the colorkey value are copied. The alpha channel of the copied pixels is set to opaque. |
RGB->RGBA without SDL_SRCALPHA | The RGB data is copied from the source and the alpha value of the copied pixels is set to opaque. If SDL_SRCCOLORKEY is set, only the pixels not matching the colorkey value are copied. |
RGBA->RGBA with SDL_SRCALPHA | The source is alpha-blended with the destination using the source alpha channel. The alpha channel in the destination surface is left untouched. SDL_SRCCOLORKEY is ignored. |
RGBA->RGBA without SDL_SRCALPHA | The RGBA data is copied to the destination surface. If SDL_SRCCOLORKEY is set, only the pixels not matching the colorkey value are copied. |
RGB->RGB with SDL_SRCALPHA | The source is alpha-blended with the destination using the per-surface alpha value. If SDL_SRCCOLORKEY is set, only the pixels not matching the colorkey value are copied. |
RGB->RGB without SDL_SRCALPHA | The RGB data is copied from the source. If SDL_SRCCOLORKEY is set, only the pixels not matching the colorkey value are copied. |
# define the position of the smiley xpos = 50 ypos = 50 # how many pixels we move our smiley each frame step_x = 10 step_y = 10What we have to do next is to change the position by the step size each time it loops through the main loop. So we write in the main loop something like this:
# check if the smiley is still on screen, if not change direction if xpos>screen_width-64 or xpos<0: step_x = -step_x if ypos>screen_height-64 or ypos<0: step_y = -step_y # update the position of the smiley xpos += step_x # move it to the right ypos += step_y # move it downThe two if-statements are there to keep the smiley in the screen. Perhaps you wonder why there is an -64 in xpos>screen_width-64. Well, remember the blit position is always the top left corner of the image and what we have actually saved is the position of the top left corner into xpos/ypos. If we would just check xpos>screen_width then the smiley would slide completely out of the screen before it will change direction and come back. If you have coded that and try to run it (try it!) you will see nothing happened on screen! Why? Well, we forgot to blit the smiley at the new position! So let's do it, add the following lines in the main loop, after the position update:
# now blit the smiley on screen screen.blit(image, (xpos, ypos)) # and update the screen (don't forget that!) pygame.display.flip()But what is that? What have we done wrong? Well I think we forgot to erase the current screen. Because now there is an image of the smiley at each position it once was. So let us fix that. Before blitting the smiley we have to erase the screen. How to do it? Just blit the background over anything on the screen.
# first erase the screen #(just blit the background over anything on screen) screen.blit(bgd_image, (0,0)) # now blit the smiley on screen screen.blit(image, (xpos, ypos)) # and update the screen (don't forget that!) pygame.display.flip()So what have we learned? For a moving or somehow changing image, the basic algorithm is as follows:
First do it right, then optimize!or in other words:
First you must understand the problem exactly before you can optimize the code.
# draw method from pygame.sprites.RenderUpdates def draw(self, surface): spritedict = self.spritedict # {sprite:old_rect} surface_blit = surface.blit # speed up dirty = self.lostsprites # dirty rects (from removed sprites) self.lostsprites = [] dirty_append = dirty.append # speed up for s in self.sprites(): r = spritedict[s] # get the old_rect newrect = surface_blit(s.image, s.rect)# draw it if r is 0: # first time the old_rect is 0 dirty_append(newrect) # add the rect from the blit, nothing else to do else: if newrect.colliderect(r): # if the old_rect and the newrect overlap, case 3 dirty_append(newrect.union(r)) # append the union of these two rects else: dirty_append(newrect) # not overlapping so append both, newrect and dirty_append(r) # old_rect to the dirty list, case 5 spritedict[s] = newrect # replace the old one with the new one return dirty # return the dirty rects listIs it the best we can do? I think not. If you have many moving sprites which overlap in their movement, this approach will still update many areas twice. And it has one more major disadvantage: it has to clear and redraw every sprite (otherwise is would not work, see dirty flags ). But that leads us to the next section: dirty areas union.
_update = [] #list that contains the (or already added) dirty rects _union_rect = _rect(spr.rect) # copy the rect because it will be modified _union_rect_collidelist = _union_rect.collidelist # speed up _union_rect_union_ip = _union_rect.union_ip # speed up i = _union_rect_collidelist(_update) # check for overlapping areas while -1 < i: # as long a overlapping area is found _union_rect_union_ip(_update[i]) # union the two rects del _update[i] # remove the one from the list i = _union_rect_collidelist(_update) # check again for overlapping ares _update.append(_union_rect.clip(_clip)) # at the end add the new found rect to the list # do the same for the old rect (old position of the sprite)
for spr in sprites: if spr.dirty: # do the drawing, only for dirty sprites and reset dirty flagSo only the sprites that are marked with "dirty == 1" are drawn and the flag gets reset (important). But wait, what if a sprite intersects with another one? Even worse, what if that sprite is transparent, dirty and intersects with other sprites? Yes you guessed right, these intersecting sprites need to be redrawn too!! That is the problem I was referring in the other sections before. Any sprite in a dirty area has to be redrawn, independent how you have found the dirty area. It is because you erase the dirty area by filling it using a background and then you will redraw anything in the cleaned area. So now the algorithm changes to:
# find the dirty areas first dirty_areas = [] for spr in sprites: if spr.dirty: # add this sprite area to the dirty areas list dirty_areas.append(spr.rect) # draw the sprites for dirty_area in dirty_areas: # do the drawing of the intersecting sprites for spr in sprites: if dirty_area.collide_rect(spr.rect): if spr.dirty: # just draw the sprite, because the entire sprite is in the dirty area # reset the flag else: # find intersecting part and draw only this part of the spriteWell this code can be optimized using the colliding function from the pygame.Rect. I will put a snippet here of how it is done in the FastRenderGroup (only the drawing part, for how it finds the dirty areas see dirty areas union):
for spr in _sprites: if 1 > spr.dirty: # sprite not dirty, blit only the _spr_rect = spr.rect # intersecting part _spr_rect_clip = _spr_rect.clip for idx in _spr_rect.collidelistall(_update): # find all intersecting dirty areas # clip clip = _spr_rect_clip(_update[idx]) # find the intersecting part _surf_blit(spr.image, clip, \ # and draw only that part (clip[0]-_spr_rect[0], \ clip[1]-_spr_rect[1], \ clip[2], \ clip[3]), spr.blendmode) else: # dirty sprite # if dirty draw the entire sprite _old_rect[spr] = _surf_blit(spr.image, spr.rect, \ None, spr.blendmode) if spr.dirty == 1: # and reset the flag (well here it is spr.dirty = 0 # special because only if dirty has # value 1 it will be reset (2 not)As you have seen, optimization is sometimes good, sometimes bad. Since I want to write my things in pure python, that is all you can do. If you need even more speed, you always can consider to write a C extension for python. Before you do that try psyco. If you decide to write an extension, then there are some tools that might help (I have not tried one yet): swig, pyrex, boost.