Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Python - From Novice To Professional (2005)

.pdf
Скачиваний:
139
Добавлен:
17.08.2013
Размер:
13.91 Mб
Скачать

540

C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

screen.fill(config.background_color)

# Remember to call flip, to make the changes visible: pygame.display.flip()

def display(self, screen):

"""

Used to display the State after it has already been displayed once. The default behavior is to do nothing.

"""

pass

class Level(State):

"""

A game level. Takes care of counting how many weights have been dropped, moving the sprites around, and other tasks relating to game logic.

"""

def __init__(self, number=1): self.number = number

# How many weights remain to dodge in this level? self.remaining = config.weights_per_level

speed = config.drop_speed

#One speed_increase added for each level above 1: speed += (self.number-1) * config.speed_increase

#Create the weight and banana:

self.weight = weight = objects.Weight(speed) self.banana = banana = objects.Banana()

both = self.weight, self.banana # This could contain more sprites...

self.sprites = pygame.sprite.RenderUpdates(both)

def update(self, game):

"Updates the game state from the previous frame."

#Update all sprites: self.sprites.update()

#If the banana touches the weight, tell the game to switch to

#a GameOver state:

if self.banana.touches(self.weight): game.nextState = GameOver()

#Otherwise, if the weight has landed, reset it. If all the

#weights of this level have been dodged, tell the game to

#switch to a LevelCleared state:

elif self.weight.landed: self.weight.reset()

C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

541

self.remaining -= 1

if self.remaining == 0:

game.nextState = LevelCleared(self.number)

def display(self, screen):

"""

Displays the state after the first display (which simply wipes the screen). As opposed to firstDisplay, this method uses pygame.display.update with a list of rectangles that need to be updated, supplied from self.sprites.draw.

"""

screen.fill(config.background_color) updates = self.sprites.draw(screen) pygame.display.update(updates)

class Paused(State):

"""

A simple, paused game state, which may be broken out of by pressing either a keyboard key or the mouse button.

"""

finished = 0 # Has the user ended the pause?

image = None # Set this to a file name if you want an image text = '' # Set this to some informative text

def handle(self, event):

"""

Handles events by delegating to State (which handles quitting in general) and by reacting to key presses and mouse

clicks. If a key is pressed or the mouse is clicked, self.finished is set to true.

"""

State.handle(self, event)

if event.type in [MOUSEBUTTONDOWN, KEYDOWN]: self.finished = 1

def update(self, game):

"""

Update the level. If a key has been pressed or the mouse has been clicked (i.e., self.finished is true), tell the game to move to the state represented by self.nextState() (should be implemented by subclasses).

"""

if self.finished:

game.nextState = self.nextState()

542

C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

def firstDisplay(self, screen):

"""

The first time the Paused state is displayed, draw the image (if any) and render the text.

"""

#First, clear the screen by filling it with the background color: screen.fill(config.background_color)

#Create a Font object with the default appearance, and specified size: font = pygame.font.Font(None, config.font_size)

#Get the lines of text in self.text, ignoring empty lines at

#the top or bottom:

lines = self.text.strip().splitlines()

#Calculate the height of the text (using font.get_linesize()

#to get the height of each line of text):

height = len(lines) * font.get_linesize()

#Calculate the placement of the text (centered on the screen): center, top = screen.get_rect().center

top -= height // 2

#If there is an image to display...

if self.image:

# load it:

image = pygame.image.load(self.image).convert()

# get its rect:

r = image.get_rect()

#move the text down by half the image height: top += r.height // 2

#place the image 20 pixels above the text: r.midbottom = center, top - 20

#blit the image to the screen: screen.blit(image, r)

antialias = 1 # Smooth the text black = 0, 0, 0 # Render it as black

#Render all the lines, starting at the calculated top, and

#move down font.get_linesize() pixels for each line:

for line in lines:

text = font.render(line.strip(), antialias, black) r = text.get_rect()

r.midtop = center, top screen.blit(text, r)

top += font.get_linesize()

C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

543

# Display all the changes: pygame.display.flip()

class Info(Paused):

"""

A simple paused state that displays some information about the game. It is followed by a Level state (the first level).

"""

nextState = Level text = '''

In this game you are a banana, trying to survive a course in

self-defense against fruit, where the participants will "defend" themselves against you with a 16 ton weight.'''

class StartUp(Paused):

"""

A paused state that displays a splash image and a welcome message. It is followed by an Info state.

"""

nextState = Info

image = config.splash_image text = '''

Welcome to Squish,

the game of Fruit Self-Defense'''

class LevelCleared(Paused):

"""

A paused state that informs the user that he or she has cleared a given level. It is followed by the next level state.

"""

def __init__(self, number): self.number = number

self.text = '''Level %i cleared

Click to start next level''' % self.number

544 C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

def nextState(self):

return Level(self.number+1)

class GameOver(Paused):

"""

A state that informs the user that he or she has lost the game. It is followed by the first level.

"""

nextState = Level text = '''

Game Over

Click to Restart, Esc to Quit'''

class Game:

"""

A game object that takes care of the main event loop, including changing between the different game states.

"""

def __init__(self, *args):

#Get the directory where the game and the images are located: path = os.path.abspath(args[0])

dir = os.path.split(path)[0]

#Move to that directory (so that the images files may be

#opened later on):

os.chdir(dir)

#Start with no state: self.state = None

#Move to StartUp in the first event loop iteration: self.nextState = StartUp()

def run(self):

"""

This method sets things in motion. It performs some vital initialization tasks, and enters the main event loop.

"""

pygame.init() # This is needed to initialize all the pygame modules

#Decide whether to display the game in a window or to use the

#full screen:

flag = 0

# Default (window) mode

C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

545

if config.full_screen:

 

flag = FULLSCREEN

# Full screen mode

screen_size = config.screen_size

screen = pygame.display.set_mode(screen_size, flag)

pygame.display.set_caption('Fruit Self Defense') pygame.mouse.set_visible(False)

# The main loop: while 1:

#(1) If nextState has been changed, move to the new state, and

#display it (for the first time):

if self.state != self.nextState: self.state = self.nextState self.state.firstDisplay(screen)

#(2) Delegate the event handling to the current state: for event in pygame.event.get():

self.state.handle(event)

#(3) Update the current state:

self.state.update(self)

# (4) Display the current state: self.state.display(screen)

if __name__ == '__main__': game = Game(*sys.argv) game.run()

Further Exploration

Here are some ideas for how you can improve the game:

Add sounds to it.

Keep track of the score. Each weight dodged could be worth 16 points, for example. How about keeping a high-score file? Or even an online high-score server (using asyncore or XML-RPC)?

Make more objects fall simultaneously.

Give the player more than one “life.”

Create a stand-alone executable of your game (using py2exe, for example) and package it with an installer. (See Chapter 18 for details.)

For a much more elaborate (and extremely entertaining) example of Pygame programming, check out the SolarWolf game by Pete Shinners, the Pygame maintainer (http:// pygame.org/shredwheat/solarwolf). You can find plenty of information and several other

546

C H A P T E R 2 9 P R O J E C T 1 0 : D O - I T - Y O U R S E L F A R C A D E G A M E

games at the Pygame Web site. If playing with Pygame gets you hooked on game development, you might want to check out these Web sites:

http://www.gamedev.net

http://www.flipcode.com

A Web search should give you plenty of other similar sites.

What Now?

Well, this is it. You have finished the last project. If you take stock of what you have accomplished (assuming that you have followed all the projects), you should be rightfully impressed with yourself. The breadth of the topics presented has given you a taste of the possibilities that await you in the world of Python programming. I hope you have enjoyed the trip this far, and I wish you good luck on your continued journey as a Python programmer.

A P P E N D I X A

■ ■ ■

The Short Version

This is a minimal introduction to Python, based on my popular Web tutorial, “Instant Python” (http://hetland.org/python/instant-python). It targets programmers who already know a language or two, but who want to get up to speed with Python. For information on downloading and executing the Python interpreter, see Chapter 1.

The Basics

To get a basic feel for the Python language, think of it as pseudocode—it’s pretty close to the truth. Variables don’t have types, so you don’t have to declare them. They appear when you assign to them, and disappear when you don’t use them anymore. Assignment is done with the = operator, like this:

x = 42

Note that equality is tested by the == operator.

You can assign several variables at once, like this:

x,y,z = 1,2,3

first, second = second, first a = b = 123

Blocks are indicated through indentation, and only through indentation. (No begin/end or braces.) The following are some common control structures:

if x < 5 or (x > 10 and x < 20): print "The value is OK."

if x < 5 or 10 < x < 20: print "The value is OK."

for i in [1,2,3,4,5]:

print "This is iteration number", i

x = 10

while x >= 0:

print "x is still not negative." x = x-1

547

548

A P P E N D I X A T H E S H O R T V E R S I O N

The first two examples are equivalent.

The index variable given in the for loop iterates through the elements of a list (written with brackets, as in the example). To make an “ordinary” for loop (that is, a counting loop), use the built-in function range:

# Print out the values from 0 to 99, inclusive for value in range(100):

print value

The line beginning with # is a comment and is ignored by the interpreter.

Now you know enough (in theory) to implement any algorithm in Python. Let’s add some basic user interaction. To get input from the user (from a text prompt), use the built-in function input:

x = input("Please enter a number: ") print "The square of that number is", x*x

The input function displays the (optional) prompt given and lets the user enter any valid Python value. In this case, we were expecting a number. If something else (such as a string) is entered, the program would halt with an error message. To avoid that, you would have to add some error checking. I won’t go into that here; suffice it to say that if you want the user input returned verbatim as a string (so that anything can be entered), use the function raw_input instead. If you wanted to convert an input string s to an integer, you could then use int(s).

Note If you want to input a string with input, the user has to write the quotes explicitly. In Python, strings can be enclosed in either single or double quotes.

So, you have control structures, input, and output covered—now you need some snazzy data structures. The most important ones are lists and dictionaries. Lists are written with brackets, and can (naturally) be nested:

name = ["Cleese", "John"] x = [[1,2,3],[y,z],[[[]]]]

One of the nice things about lists is that you can access their elements separately or in groups, through indexing and slicing. Indexing is done (as in many other languages) by writing the index in brackets after the list. (Note that the first element has index 0.)

print name[1], name[0] # Prints "John Cleese" name[0] = "Smith"

Slicing is almost like indexing, except that you indicate both the start and stop index of the result, with a colon (:) separating them:

x = ["SPAM","SPAM","SPAM","SPAM","SPAM","eggs","and","SPAM"] print x[5:7] # Prints the list ["eggs","and"]

A P P E N D I X A T H E S H O R T V E R S I O N

549

Notice that the end is noninclusive. If one of the indices is dropped, it is assumed that you want everything in that direction. In other words, the slice x[:3] means “every element from the beginning of x up to element 3, noninclusive.” (It could be argued that it would actually mean element 4 because the counting starts at 0. Oh, well.) The slice x[3:] would, on the other hand, mean “every element from x, starting at element 3 (inclusive) up to, and including, the last one.” For really interesting results, you can use negative numbers, too: x[-3] is the third element from the end of the list.

Tip While on the subject of indexing, you might be interested to know that the built-in function len gives you the length of a list.

Now, then—what about dictionaries? To put it simply, they are like lists, except that their contents aren’t ordered. How do you index them then? Well, every element has a key, or a name, which is used to look up the element just as in a real dictionary. The following example demonstrates the syntax used to create dictionaries:

phone = { "Alice" : 23452532, "Boris" : 252336, "Clarice" : 2352525, "Doris" : 23624643 }

person = { 'first name': "Robin", 'last name': "Hood", 'occupation': "Scoundrel" }

Now, to get person’s occupation, you use the expression person["occupation"]. If you wanted to change his or her last name, you could write

person['last name'] = "of Locksley"

Simple, isn’t it? Like lists, dictionaries can hold other dictionaries. Or lists, for that matter. And naturally lists can hold dictionaries, too. That way, you can easily make some quite advanced data structures.

Functions

Next step: abstraction. You want to give a name to a piece of code and call it with a couple of parameters. In other words, you want to define a function (also called a procedure). That’s easy. Use the keyword def as follows:

def square(x): return x*x

print square(2) # Prints out 4

The return statement is used to return a value from the function.

When you pass a parameter to a function, you bind the parameter to the value, thus creating a new reference. This means that you can modify the original value directly inside the function,