Опубликован: 06.08.2013 | Уровень: для всех | Доступ: свободно
Лекция 8:

Squirrel Eat Squirrel

< Лекция 7 || Лекция 8: 12345 || Лекция 9 >
Аннотация: Programming the game "Squirrel Eat Squirrel".
Ключевые слова: EAT, player, hop, screen, AND, avoidance, TIME, if, hit, life, point, NOT, WHERE, IDEA, video game, data structure, this, AS, dictionary, VALUES, ONE, Object, object-oriented programming, python, OOP, book, surface, term, ALL, Top, Left, FROM, pixel, difference, explain, concept, addition, comment, source coding, CAN, GET, error, look, Line, CHECK, Copy, paste, web form, SEE, download, image file, constant variable, half, length, Width, Height, store, camera, begin, center, NEXT, used, string, DESCRIBE, First, function, setup, previous, game programming, icon, title bar, LIKE, caption, text, argument, size, PNG, folder, picture, right, second, call, flip, with, horizontal, vertical, PARAMETER, original, unchangeable, track, State, restart, space, course, fit, draw, portion, area, front, Green, field, Origin, world coordinates, change, pass, EXCEPT, ITS, find, subtract, coordinate, calculation, active area, explanation, chapter, create, new, pop, FAR, wanderer, border, DELETE, memory, back, super, video, system, hold, list, arrow, off, return, event handling, drawing, die, coupling, constant, set, damage, mode, positive, negative, iteration, apply, bounce, very, equal, end, Full, speed, direction, integer, proper, determine, CASE, operator, numbering, INDEX, Last, item, decrement, reverse order, Write, RAN, let, walk-through, cause, ITERATE, loop iteration, statement, CONDITION, valid, problem, reason, Checked, Position, mean, equation, EDGE, logic, color, background, paint, scratch, information, blit, method call, identical, constructor, skip, BIT, indicate, temporarily, long, flash, example, function call, digit, decimal point, mod, even, odd, fact, while, indicator, corner, event, down arrow, equivalence, ETC, right arrow, left arrow, terminate, go, important, Reverse, section, key in, Calculated, graph, Chart, enlargement, true, prevent, attack, setting, health, amount, continue, animated, USER, execution, RED, rectangle, mathematical function, programming, evaluate, sine, sign, math, class, MODULE, int, FLOAT, interactive, shell, hard, predict, useful, pattern, connect, data point, wave, decrease, special, mathematize, attention, exact, Increment, split, floating-point, division, part, sine wave, Shape, conception, convert, floating point, rounding, software, compatibility, running, backward, try, error message, fast, velocity, random, generate, bottom, acceptable, Data, random number, short, general, variation, location, popping, MATCH, method, finally, board, HAVING, entire, coordinate system, realistic, MOST, multiplication, study, mathematics, cooler, additional, programming practice, FIX

How to Play Squirrel Eat Squirrel


Рис. 8.1.

Squirrel Eat Squirrel is loosely based on the game "Katamari Damacy". The player controls a small squirrel that must hop around the screen eating smaller squirrels and avoiding larger squirrels. Each time the player’s squirrel eats a squirrel that is smaller than it, it grows larger. If the player’s squirrel gets hit by a larger squirrel larger than it, it loses a life point. The player wins when the squirrel becomes a monstrously large squirrel called the Omega Squirrel. The player loses if their squirrel gets hit three times.

I’m not really sure where I got the idea for a video game where squirrels eat each other. I’m a little strange sometimes.

The Design of Squirrel Eat Squirrel

There are three types of data structures in this game, which are represented as dictionary values. The types are player squirrels, enemy squirrels, and grass objects. There is only one player squirrel object at a time in the game.

Note: Technically, "object" means something specific in Object-Oriented Programming. Python does have OOP features, but they aren’t covered in this book. Technically the Pygame objects such as "Rect object" or "Surface object" are objects. But I’m going to use the term "object" in this book to refer to "things that exist in the game world". But really, the player squirrel, enemy squirrels, and grass "objects" are just dictionary values.

All the objects have the following keys in their dictionary value: 'x', 'y', and 'rect'. The 'x' and 'y' key’s value give the coordinates of the top left of the object in game world coordinates. These are different from pixel coordinates (which is what the 'rect' key’s value tracks). The difference between game world and pixel coordinates will be explained when you learn about the concept of cameras.

In addition, the player squirrel, enemy squirrel, and grass objects have other keys which are explained in a large comment at the start of the source code.

Source Code to Squirrel Eat Squirrel

This source code can be downloaded from http://invpy.com/squirrel.py. If you get any error messages, look at the line number that is mentioned in the error message and check your code for any typos. You can also copy and paste your code into the web form at http://invpy.com/diff/squirrel to see if the differences between your code and the code in the book.

You will also need to download the following image files:

The Usual Setup Code

1. # Squirrel Eat Squirrel (a 2D Katamari Damacy clone)
2. # By Al Sweigart al@inventwithpython.com
3. # http://inventwithpython.com/pygame
4. # Creative Commons BY-NC-SA 3.0 US
5.
6. import random, sys, time, math, pygame
7. from pygame.locals import *
8.
9. FPS = 30 # frames per second to update the screen
10. WINWIDTH = 640 # width of the program's window, in pixels
11. WINHEIGHT = 480 # height in pixels
12. HALF_WINWIDTH = int(WINWIDTH / 2)
13. HALF_WINHEIGHT = int(WINHEIGHT / 2)
14.
15. GRASSCOLOR = (24, 255, 0)
16. WHITE = (255, 255, 255)
17. RED = (255, 0, 0)

The start of the program assigns several constant variables. This program frequently makes use of the half length of the width and height of the window so much that the HALF_WINWIDTH and HALF_WINHEIGHT variables store these numbers.

19. CAMERASLACK = 90     # how far from the center the squirrel moves before
moving the camera

The "camera slack" is described later. Basically, it means that the camera will begin following the player squirrel when it moves 90 pixels away from the center of the window.

20. MOVERATE = 9         # how fast the player moves
21. BOUNCERATE = 6       # how fast the player bounces (large is slower)
22. BOUNCEHEIGHT = 30    # how high the player bounces
23. STARTSIZE = 25       # how big the player starts off
24. WINSIZE = 300        # how big the player needs to be to win
25. INVULNTIME = 2       # how long the player is invulnerable after being hit
in seconds
26. GAMEOVERTIME = 4     # how long the "game over" text stays on the screen
in seconds
27. MAXHEALTH = 3        # how much health the player starts with
28.
29. NUMGRASS = 80        # number of grass objects in the active area
30. NUMSQUIRRELS = 30    # number of squirrels in the active area
31. SQUIRRELMINSPEED = 3 # slowest squirrel speed
32. SQUIRRELMAXSPEED = 7 # fastest squirrel speed
33. DIRCHANGEFREQ = 2    # % chance of direction change per frame
34. LEFT = 'left'
35. RIGHT = 'right'

The comments next to these constants explains what the constant variable is used for.

Describing the Data Structures

37. """
38. This program has three data structures to represent the player, enemy
squirrels, and grass background objects. The data structures are dictionaries
with the following keys:
39.
40. Keys used by all three data structures:
41.     'x' - the left edge coordinate of the object in the game world (not a
pixel coordinate on the screen)
42.     'y' - the top edge coordinate of the object in the game world (not a
pixel coordinate on the screen)
43.     'rect' - the pygame.Rect object representing where on the screen the
object is located.
44. Player data structure keys:
45.     'surface' - the pygame.Surface object that stores the image of the
squirrel which will be drawn to the screen.
46.     'facing' - either set to LEFT or RIGHT, stores which direction the
player is facing.
47.     'size' - the width and height of the player in pixels. (The width &
height are always the same.)
48.     'bounce' - represents at what point in a bounce the player is in. 0
means standing (no bounce), up to BOUNCERATE (the completion of the bounce)
49.     'health' - an integer showing how many more times the player can be
hit by a larger squirrel before dying.
50. Enemy Squirrel data structure keys:
51.     'surface' - the pygame.Surface object that stores the image of the
squirrel which will be drawn to the screen.
52.     'movex' - how many pixels per frame the squirrel moves horizontally. A
negative integer is moving to the left, a positive to the right.
53.     'movey' - how many pixels per frame the squirrel moves vertically. A
negative integer is moving up, a positive moving down.
54.     'width' - the width of the squirrel's image, in pixels
55.     'height' - the height of the squirrel's image, in pixels
56.     'bounce' - represents at what point in a bounce the player is in. 0
means standing (no bounce), up to BOUNCERATE (the completion of the bounce)
57.     'bouncerate' - how quickly the squirrel bounces. A lower number means
a quicker bounce.
58.     'bounceheight' - how high (in pixels) the squirrel bounces
59. Grass data structure keys:
60.     'grassImage' - an integer that refers to the index of the
pygame.Surface object in GRASSIMAGES used for this grass object
61. """

The comments from lines 37 to 61 are in one large, multi-line string. They describe the keys in the player squirrel, enemy squirrel, and grass objects. In Python, a multi-line string value by itself works as a multi-line comment.

The main() Function

63. def main():
64.     global FPSCLOCK, DISPLAYSURF, BASICFONT, L_SQUIR_IMG, R_SQUIR_IMG,
GRASSIMAGES
65.
66.     pygame.init()
67.     FPSCLOCK = pygame.time.Clock()
68.     pygame.display.set_icon(pygame.image.load('gameicon.png'))
69.     DISPLAYSURF = pygame.display.set_mode((WINWIDTH, WINHEIGHT))
70.     pygame.display.set_caption('Squirrel Eat Squirrel')
71.     BASICFONT = pygame.font.Font('freesansbold.ttf', 32)

The first several lines of the main() function are the same setup code that we’ve seen in our previous game programs. The pygame.display.set_icon() is a Pygame function that sets the icon in the window’s title bar (just like pygame.display.set_caption() sets the caption text in the title bar). The single argument to pygame.display.set_icon() is a Surface object of a small image. The ideal image size is 32 x 32 pixels, although you can use other sized images. The image will just be compressed into a smaller size to be used as the window’s icon.

The pygame.transform.flip() Function

73.     # load the image files
74.     L_SQUIR_IMG = pygame.image.load('squirrel.png')
75.     R_SQUIR_IMG = pygame.transform.flip(L_SQUIR_IMG, True, False)
76.     GRASSIMAGES = []
77.     for i in range(1, 5):
78.         GRASSIMAGES.append(pygame.image.load('grass%s.png' % i))

The image for the player and enemy squirrels is loaded from squirrel.png on line 74. Make sure that this PNG file is in the same folder as squirrel.py, otherwise you will get the error pygame.error: Couldn't open squirrel.png.

The image in squirrel.png (which you can download from http://invpy.com/squirrel.png) is of a squirrel facing to the left. We also need a Surface object that contains a picture of the squirrel facing to the right. Instead of creating a second PNG image file, we can call the pygame.transform.flip() function. This function has three parameters: the Surface object with the image to flip, a Boolean value to do a horizontal flip, and a Boolean value to do a vertical flip. By passing True for the second parameter and False for the third parameter, the Surface object that returns has the image of the squirrel facing to the right. The original Surface object in L_SQUIR_IMG that we passed in is unchanged.

Here are examples of images being horizontally and vertically flipped:


Рис. 8.2.
80.     while True:
81.         runGame()

After the setup in main() is complete, the game begins with runGame() being called.

A More Detailed Game State than Usual

84. def runGame():
85.     # set up variables for the start of a new game
86.     invulnerableMode = False  # if the player is invulnerable
87.     invulnerableStartTime = 0 # time the player became invulnerable
88.     gameOverMode = False      # if the player has lost
89.     gameOverStartTime = 0     # time the player lost
90.     winMode = False           # if the player has won

The Squirrel Eat Squirrel game has quite a few variables that track the game state. These variables will be explained in more detail later when they are used in the code.

The Usual Text Creation Code

92.     # create the surfaces to hold game text
93.     gameOverSurf = BASICFONT.render('Game Over', True, WHITE)
94.     gameOverRect = gameOverSurf.get_rect()
95.     gameOverRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
96.
97.     winSurf = BASICFONT.render('You have achieved OMEGA SQUIRREL!', True,
WHITE)
98.     winRect = winSurf.get_rect()
99.     winRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
100.
101.     winSurf2 = BASICFONT.render('(Press "r" to restart.)', True, WHITE)
102.     winRect2 = winSurf2.get_rect()
103.     winRect2.center = (HALF_WINWIDTH, HALF_WINHEIGHT + 30)

These variables contain Surface objects with the "Game Over", "You have achieved OMEGA SQUIRREL!"and "(Press "r" to restart.)" text that appears on the screen after the game ends (with either the player losing or winning).

Cameras

105.     # camerax and cameray are where the middle of the camera view is
106.     camerax = 0
107.     cameray = 0

The camerax and cameray variables track the game coordinates of the "camera". Imagine the game world as an infinite 2D space. This could, of course, never fit on any screen. We can only draw a portion of the infinite 2D space on the screen. We call the area of this portion a camera, because it is as though our screen is just the area of the game world in front what a camera would see. Here’s a picture of the game world (an infinite green field) and the area that the camera can view:


Рис. 8.3.

As you can see, the game world XY coordinates keep getting bigger and smaller forever. The game world origin is where the (0, 0) game world coordinates are. You can see that the three squirrels are located (in game world coordinates) at (-384, -84), (384, 306), and (585, -234).

But we can only display 640 x 480 pixel area on the screen (though this can change if we pass different numbers to the pygame.display.set_mode() function), so we need to track where the camera’s origin is located in game world coordinates. In the picture above, the camera is placed at (-486, -330) in game world coordinates.

The picture below shows the same field and squirrels, except everything is given in camera coordinates:


Рис. 8.4.

The area that the camera can see (called the camera view) has it’s center (that is, its origin) at the game world coordinates (-486, -330). Since what the camera sees is displayed on the player’s screen, the "camera" coordinates are the same as the "pixel" coordinates. To find out the pixel coordinates of the squirrels (that is, where on the screen they appear), take the game coordinates of the squirrel and subtract the game coordinates of the camera’s origin.

So the squirrel on the left has game world coordinates of (-384, -84) but appears at (102, 246) on the screen in pixel coordinates. (For the X coordinate, -384 - -486 = 102 and for the Y coordinate, -84 - -330 = 246.)

When we do the same calculation to find the pixel coordinates of the other two squirrels, we find that they exist outside of the range of the screen. This is why they don’t appear in the camera’s view.

< Лекция 7 || Лекция 8: 12345 || Лекция 9 >
Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004