Россия |
Graphics and Animation
The pygame.draw.line() Function
33. # draw some blue lines onto the surface 34. pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4) 35. pygame.draw.line(windowSurface, BLUE, (12 0, 60), (60, 120) ) 36. pygame.draw.line(windowSurface, BLUE, (60, 120), (12 0, 120), 4)
The pygame.draw.line() function will draw a line on the Surface object that you provide. Notice that the last parameter (the width of the line) is optional. If you pass 4 for the width, the line will be four pixels thick. If you do not specify the width parameter it will take on the default value of 1.
The pygame.draw.circle() Function
38. # draw a blue circle onto the surface 39. pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
The pygame.draw.circle() function will draw a circle on the Surface object you provide. The third parameter is for the X and Y coordinates of the center of the circle as a tuple of two ints. The fourth parameter is an int for the radius (that is, size) of the circle in pixels. A width of 0 means that the circle will be filled in.
The pygame.draw.ellipse() Function
41. # draw a red ellipse onto the surface 42. pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
The pygame.draw.ellipse() function will draw an ellipse. It is similar to the pygame.draw.circle() function, except that instead of specifying the center of the circle, a tuple of four ints is passed for the left, top, width, and height of the ellipse.
The pygame.draw.rect() Function
44. # draw the text's background rectangle onto the surface 45. pygame.draw.rect(windowSurface, RED, (textRect.left - 20, textRect.top - 20, textRect.width + 40, textRect.height + 40) )
The pygame.draw.rect() function will draw a rectangle. The third parameter is a tuple of four ints for the left, top, width, and height of the rectangle. Instead of a tuple of four ints for the third parameter, you can also pass a Rect object. In line 45, we want the rectangle we draw to be 20 pixels around all the sides of the text. This is why we want the drawn rectangle's left and top to be the left and top of textRect minus 20. (Remember, we subtract because coordinates decrease as you go left and up.) And the width and height will be equal to the width and height of the textRect plus 40 (because the left and top were moved back 20 pixels, so we need to make up for that space).
The pygame. Pixel Array Data Type
47. # get a pixel array of the surface 48. pixArray = pygame.PixelArray(windowSurface) 49. pixArray[480] [380] = BLACK
On line 48 we create a pygame.PixelArray object (which we will just call a PixelArray object for short). The PixelArray object is a list of lists of color tuples that represents the Surface object you passed it. We passed windowSurface object when we called the PixelArray() constructor function on line 48, so assigning BLACK to pixArray[480][380] will change the pixel at the coordinates (480, 380) to be a black pixel. Pygame will automatically modify the windowSurface object with this change.
The first index in the PixelArray object is for the X-coordinate. The second index is for the Y-coordinate. PixelArray objects make it easy to set individual pixels on a PixelArray object to a specific color.
50. del pixArray
Creating a PixelArray object from a Surface object will lock that Surface object. Locked means that no blit() function calls (described next) can be made on that Surface object. To unlock the Surface object, you must delete the PixelArray object with the del operator. If you forget to delete the Surface object, you will get an error message that says pygame.error: Surfaces must not be locked during blit.
The blit() Method for Surface Objects
52. # draw the text onto the surface 53. windowSurface.blit(text, textRect)
The blit() method will draw the contents of one Surface object onto another Surface object. Line 54 will draw the "Hello world!" text (which was drawn on the Surface object stored in the text variable) and draws it to the Surface object stored in the windowSurface variable.
Remember that the text object had the "Hello world!" text drawn on it on line 22 by the render() method. Surface objects are just stored in the computer's memory (like any other variable) and not drawn on the screen. The Surface object in windowSurface is drawn on the screen (when we call the pygame.display.update() function on line 56 below) because this was the Surface object created by the pygame.display.set_mode() function.
The second parameter to blit() specifies where on the windowSurface surface the text surface should be drawn. We will just pass the Rect object we got from calling text.get_rect() (which was stored in textRect on line 23).
The pygame.display.update() Function
55. # draw the window onto the screen 56. pygame.display.update()
In Pygame, nothing is drawn to the screen until the pygame.display.update() function is called. This is done because drawing to the screen is a slow operation for the computer compared to drawing on the Surface objects while they are in memory. You do not want to draw to the screen after each drawing function is called, but only draw the screen once after all the drawing functions have been called.
You will need to call pygame.display.update() each time you want to update the screen to display the contents of the Surface object returned by pygame.display.set_mode(). (In this program, that object is the one stored in windowSurface.) This will become more important in our next program which covers animation.
Events and the Game Loop
In our previous games, all of the programs print out everything immediately until they reach a input() function call. At that point, the program stops and waits for the user to type something in and press Enter. Pygame programs do not work this way. Instead, Pygame programs are constantly running through a loop called the game loop. (In this program, we execute all the lines of code in the game loop about one hundred times a second.)
The game loop is a loop that constantly checks for new events, updates the state of the window, and draws the window on the screen. Events are values of the pygame.event.Event data type that are generated by Pygame whenever the user presses a key, clicks or moves the mouse, or makes some other event occur. Calling pygame.event.get() retrieves any new pygame.event.Event objects that have been generated since the last call to pygame.event.get().
58. # run the game loop 59. while True:
This is the start of our game loop. The condition for the while statement is set to True so that we loop forever. The only time we exit the loop is if an event causes the program to terminate.
The pygame.event.get() Function
60. for event in pygame.event.get(): 61. if event.type == QUIT:
The pygame.event.get() function returns a list of pygame.event.Event objects. This list has every single event that has occurred since the last time pygame.event.get() was called. All pygame.event.Event objects have an attribute called type which tell us what type of event it is. (A list of event types is given in the next chapter. In this chapter we only deal with the QUIT event.)
Pygame comes supplied with its own constant variables in the pygame.locals module. Remember that we have imported the pygame.locals module with the line from pygame.locals import *, which means we do not have to type pygame.locals in front of the variables and functions in that module.
On line 60 we set up a for loop to check each pygame.event.Event object in the list returned by pygame.event.get(). If the type attribute of the event is equal to the value of the constant variable QUIT (which is provided by the pygame.locals module), then we know the user has closed the window and wants to terminate the program.
Pygame generates the QUIT event when the user clicks on the X button at the top right of the program's window. It is also generated if the computer is shutting down and tries to terminate all the programs running. For whatever reason the QUIT event was generated, we know that we should run any code that we want to happen to stop the program. You could choose to ignore the QUIT event entirely, but that may cause the program to be confusing to the user.
The pygame.quit() Function
62. pygame.quit() 63. sys.exit()
If the QUIT event has been generated, then we can know that the user has tried to close the window. In that case, we should call the exit functions for both Pygame (pygame.quit() ) and Python (sys.exit() ).
This has been the simple "Hello world!" program from Pygame. We've covered many new topics that we didn't have to deal with in our previous games. Even though they are more complicated, the Pygame programs can also be much more fun and engaging than our previous text games. Let's learn how to create games with animated graphics that move.
Animation
In this program we have several different blocks bouncing off of the edges of the window. The blocks are different colors and sizes and move only in diagonal directions. In order to animate the blocks (that is, make them look like they are moving) we will move the blocks a few pixels over on each iteration through the game loop. By drawing new blocks that are located a little bit differently then the blocks before, we can make it look like the blocks are moving around the screen.
The Animation Program's Source Code
Type the following program into the file editor and save it as animation.py. You can also download this source code from http://inventwithpython.com/chapter17.
This code can be downloaded from http://inventwithpython.com/animation.py
If you get errors after typing this code in, compare it to the book's code with the online
diff tool at http://inventwithpython.com/diff or email the author at
1. import pygame, sys, time 2. from pygame.locals import * 3 . 4. # set up pygame 5. pygame.init() 6 . 7. # set up the window 8. WINDOWWIDTH = 400 9. WINDOWHEIGHT = 400 10. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32) 11. pygame.display.set_caption('Animation') 12. 13. # set up direction variables 14. DOWNLEFT = 1 15. DOWNRIGHT = 3 16. UPLEFT = 7 17. UPRIGHT = 9 18. 19. MOVESPEED = 4 20. 21. # set up the colors 22. BLACK = (0, 0, 0) 23. RED = (255, 0, 0) 24. GREEN = (0, 255, 0) 25. BLUE = (0, 0, 255) 26. 27. # set up the block data structure 28. b1 = {'rect':pygame.Rect(300, 80, 50, 100), 'color':RED, 'dir':UPRIGHT} 29. b2 = {'rect':pygame.Rect(200, 200, 20, 20), 'color':GREEN, 'dir':UPLEFT} 30. b3 = {'rect':pygame.Rect(100, 150, 60, 60), 'color':BLUE, 'dir':DOWNLEFT} 31. blocks = [b1, b2, b3] 32. 33. # run the game loop 34. while True: 35. # check for the QUIT event 36. for event in pygame.event.get(): 37. if event.type == QUIT: 38. pygame.quit() 39. sys.exit() 40. 41. # draw the black background onto the surface 42. windowSurface.fill(BLACK) 43. 44. for b in blocks: 45. # move the block data structure 46. if b['dir'] == DOWNLEFT: 47. b['rect'].left -= MOVESPEED 48. b['rect'].top += MOVESPEED 49. if b['dir'] == DOWNRIGHT: 50. b['rect'].left += MOVESPEED 51. b['rect'].top += MOVESPEED 52. if b['dir'] == UPLEFT: 53. b['rect'].left -= MOVESPEED 54. b['rect'].top -= MOVESPEED 55. if b['dir'] == UPRIGHT: 56. b['rect'].left += MOVESPEED 57. b['rect'].top -= MOVESPEED 58. 59. # check if the block has move out of the window 60. if b ['rect'] .top < 0: 61. # block has moved past the top 62. if b['dir'] == UPLEFT: 63. b['dir'] = DOWNLEFT 64. if b['dir'] == UPRIGHT: 65. b['dir'] = DOWNRIGHT 66. if b [ 'rect'] .bottom > WINDOWHEIGHT: 67. # block has moved past the bottom 68. if b['dir'] == DOWNLEFT: 69. b['dir'] = UPLEFT 70. if b['dir'] == DOWNRIGHT: 71. b['dir'] = UPRIGHT 72. if b ['rect'] .left < 0: 73 . # block has moved past the left side 74. if b['dir'] == DOWNLEFT: 75. b['dir'] = DOWNRIGHT 76. if b['dir'] == UPLEFT: 77. b['dir'] = UPRIGHT 78. if b [ 'rect'] .right > WINDOWWIDTH: 79. # block has moved past the right side 80. if b['dir'] == DOWNRIGHT: 81. b['dir'] = DOWNLEFT 82. if b['dir'] == UPRIGHT: 83. b['dir'] = UPLEFT 84 . 85. # draw the block onto the surface 86. pygame.draw.rect(windowSurface, b['color'], b ['rect' ]) 87 . 88. # draw the window onto the screen 89. pygame.display.update() 90. time.sleep(0.02)