您的位置:首页 > 编程语言 > Python开发

python项目练习十:DIY街机游戏-(香蕉,快走)

2015-03-19 14:59 393 查看
这个练习是一个小游戏程序,如果要是给它起个名字的话,应该叫:快躲,香蕉。主要的游戏内容就是,游戏开始会从屏幕上方不断随便的掉一些铁块,在屏幕下方有一个小香蕉是受你控制的,你需要不断的左右移动来躲避铁块。在你躲避完一定数量的铁块之后,就会进入下一关。下一关依然是让你躲铁块,不过铁块下降的速度就快了很多。在游戏中你可以按下任意键暂停,再次按则继续,按下ESC键退出。这就是全部的功能了,下面我们来看游戏的实现。

无论是在实现功能时还是在代码分析的时候,分类归纳总是一个好习惯,这里自然也不例外。

首先对所有代码分类, 1、整体上代码有一个配置模块,来对游戏的速度、屏幕的宽度、香蕉移动速度、字体大小、各个物体的图片等进行配置。 2、然后是有一个元素模块,即游戏中的两个元素落下来的铁块以及被砸的香蕉,其中还要包含他们具有的行为。 3、然后还有游戏中的各种状态模块,状态模块中的类继承关系稍微多一些,处于家谱最上方的就是state类,由它来衍生其他的所有状态,它的直接子类是Level和Pause,其中Pause有衍生出子类Info、levelCleared、GameOver、StartUp。
4、最后就是游戏的主模块,用来让其他模块协调工作的。

然后再来看一个整体图:



上代码:

config.py:

<span style="font-size:14px;"># Configuration file for Squish
# -----------------------------

# Feel free to modify the configuration variables below to taste.
# If the game is too fast or too slow, try to modify the speed
# variables.

# Change these to use other images in the game:
banana_image = 'banana.png'
weight_image = 'weight.png'
splash_image = 'weight.png'

# Change these to affect the general appearance:
screen_size = 800, 600
background_color = 255, 255, 255
margin = 30
full_screen = 0
font_size = 48

# These affect the behavior of the game:
drop_speed = 1
banana_speed = 1
speed_increase = 1
weights_per_level = 10
banana_pad_top = 40
banana_pad_side = 20</span>

objects.py:

<span style="font-size:14px;">
import pygame, config, os
from random import randrange

"This module contains the game objects of the Squish game."

class SquishSprite(pygame.sprite.Sprite):

"""
Generic superclass for all sprites in Squish. The constructor
takes care of loading an image, setting up the sprite rect, and
the area within which it is allowed to move. That area is governed
by the screen size and the margin.
"""

def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image).convert()
self.rect = self.image.get_rect()
screen = pygame.display.get_surface()
shrink = -config.margin * 2 # -60
size = screen.get_rect(); #(0,0,800,600)
self.area = screen.get_rect().inflate(shrink, shrink) #(30,30,740,540)
print(self.area)

class Weight(SquishSprite):

"""
A falling weight. It uses the SquishSprite constructor to set up
its weight image, and will fall with a speed given as a parameter
to its constructor.
"""

def __init__(self, speed):
SquishSprite.__init__(self, config.weight_image)
self.speed = speed
self.reset()

def reset(self):
"""
Move the weight to the top of the screen (just out of sight)
and place it at a random horizontal position.
"""
# random between (30,770)
x = randrange(self.area.left, self.area.right)
self.rect.midbottom = x, 0

def update(self):
"""
Move the weight vertically (downwards) a distance
corresponding to its speed. Also set the landed attribute
according to whether it has reached the bottom of the screen.
"""
self.rect.top += self.speed
self.landed = self.rect.top >= self.area.bottom

class Banana(SquishSprite):

"""
A desperate banana. It uses the SquishSprite constructor to set up
its banana image, and will stay near the bottom of the screen,
with its horizontal position governed by the current mouse
position (within certain limits).
"""

def __init__(self):
SquishSprite.__init__(self, config.banana_image)
self.rect.bottom = self.area.bottom

# These paddings represent parts of the image where there is
# no banana. If a weight moves into these areas, it doesn't
# constitute a hit (or, rather, a squish):
self.pad_top = config.banana_pad_top
self.pad_side = config.banana_pad_side

def update(self):
"""
Set the Banana's center x-coordinate to the current mouse
x-coordinate, and then use the rect method clamp to ensure
that the Banana stays within its allowed range of motion.
"""
self.rect.centerx = pygame.mouse.get_pos()[0]
self.rect = self.rect.clamp(self.area)

def touches(self, other):
"""
Determines whether the banana touches another sprite (e.g., a
Weight). Instead of just using the rect method colliderect, a
new rectangle is first calculated (using the rect method
inflate with the side and top paddings) that does not include
the 'empty' areas on the top and sides of the banana.
"""
# Deflate the bounds with the proper padding:
bounds = self.rect.inflate(-self.pad_side, -self.pad_top)
# Move the bounds so they are placed at the bottom of the Banana:
bounds.bottom = self.rect.bottom
# Check whether the bounds intersect with the other object's rect:
return bounds.colliderect(other.rect)</span>

game.py:
<span style="font-size:14px;">import os, sys, pygame
from pygame.locals import *
import objects, config
import time

"This module contains the main game logic of the Squish game."

class State:

"""
A generic game state class that can handle events and display
itself on a given surface.
"""

def handle(self, event):
"""
Default event handling only deals with quitting.
"""
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()

def firstDisplay(self, screen):
"""
Used to display the State for the first time. Fills the screen
with the background color.
"""
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 = objects.Weight(speed)
self.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()
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()

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 # 为400,300
top -= height // 2 # 260

# 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() #为rect(0,0,166,132)
# move the text down by half the image height:
top += r.height // 2 #326
# place the image 20 pixels above the text:
r.midbottom = center, top - 20 # 400,306
# 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() # 0,0,312,37
r.midtop = center, top # 400,326
screen.blit(text, r)
top += font.get_linesize()

# 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

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) #cd到代码目录
# 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

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 True:
# (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:
# time.sleep( 0.5 )
self.state.display(screen)

if __name__ == '__main__':
#print(sys.argv)
game = Game(*sys.argv)
game.run()
</span>


ref: http://blog.csdn.net/tommyjsj/article/details/16330073
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: