第29章 项目10:DIY街机游戏

1.问题

“Self-Defense Against Fresh Fruit”:军士长指挥自己的士兵使用自我防御战术对抗以石榴、芒果、青梅和香蕉等新鲜水果入侵者。防御战术包括使用枪、释放老虎以及从敌人头顶扔下16吨重的秤砣。游戏改变后,需要控制香蕉在自我防御过程中尽力视图存活下来。

游戏的运行效果应该和设计的一样,此外,代码应该模块化并且容易扩展。游戏状态可以作为另外一个有用的游戏需求,此外,添加新的状态也应能轻松地实现。

2.工具 —— Pygame

32位下载地址:http://www.pygame.org/download.shtml

第29章 项目10:DIY街机游戏

下载完成后双击安装即可。

64位下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame

第29章 项目10:DIY街机游戏

使用pip install D:\python64\pygame-1.9.2b1-cp27-cp27m-win_amd64.whl安装。

第29章 项目10:DIY街机游戏

未报错,安装成功。

(1) Pygame

Pygame模块会自动导入其他的Pygame模块。

Pygame模块包括surface函数,可以返回一个新的surface对象。

Init函数是Pygame游戏的核心,必须在进入游戏的主事件循环之前调用。会自动初始化其他所有模块(比如font 和image)。

(2) Pygame.locals

包括在你自己的模块作用域内使用的名字(变量)。包括事件类型、键和视频模式等的名字。

(3) Pygame.display

包括处理pygame显示方式的函数。包括普通窗口和全屏模式。

Flip:更新显示

Update:更新一部分时候使用update.

Set_mode:设定显示的类型和尺寸。

Set_caption:设定pygame程序的标题

Get_surface:调用flip和blit前返回一个可用于发图的surface对象。

(4) Pygame.font

包括font函数,用于表现不同的字体。

(5) Pygame.sprite

游戏精灵,Group用做sprite对象的容器。调用group对象的update对象,会自动调用所有sprite对象的update方法。

(6) Pygame.mouse

隐藏鼠标光标,获取鼠标位置。

(7) Pygame.event

追踪鼠标单击、按键按下和释放等事件。

(8) Pygame.image

用于处理保存在GIF、PNG或者JPEG文件内的图像。

3.初次实现

图片文件:weight.png和banana.jpg

第29章 项目10:DIY街机游戏第29章 项目10:DIY街机游戏

这两个图片太大,使用时截成小图放入目录中。

29-1  weights.py ——简单的“天上掉秤砣动画”

# coding=utf-8

import sys,
pygame

from pygame.locals
import *

from random
import randrange

pygame.init()

class Weight(pygame.sprite.Sprite):

    def __init__(self):

        pygame.sprite.Sprite.__init__(self)

        # 在画sprite时使用的图像和矩形

       
self.image = weight_image

        self.rect
= self.image.get_rect()

        self.reset()



    def reset(self):

        """

       
将秤砣移动到屏幕顶端的随机位置。

        """

       
self.rect.top = -self.rect.height

        self.rect.centerx
= randrange(screen_size[0])



    def update(self):

        """

       
更新秤砣,显示下一帧

       """

       
self.rect.top += 1

        if self.rect.top > screen_size[1]:

            self.reset()



# 初始化

pygame.init()

screen_size = 800, 600

pygame.display.set_mode(screen_size,
FULLSCREEN)

pygame.mouse.set_visible(0)



# 载入秤砣的图像

weight_image =
pygame.image.load('weight.png')

weight_image = weight_image.convert()  #
...to match the display

# weight_image = pygame.image.load('weight.png').convert_alpha()#
转化



# 创建一个子图片组(sprite group),增加Weight

sprites =
pygame.sprite.RenderUpdates()

sprites.add(Weight())



# 获取屏幕并填充

screen = pygame.display.get_surface()

bg = (255, 255, 255)  # 白色

screen.fill(bg)

pygame.display.flip() # 全屏更新



# 用于清除子图形

def clear_callback(surf,
rect):

    surf.fill(bg, rect)



while True:

    # 检查退出事件:

   
for
event in
pygame.event.get():

        if event.type == QUIT:

            sys.exit()

        if event.type == KEYDOWN and
event.key == K_ESCAPE:

            sys.exit()

    # 清除前面的位置

   
sprites.clear(screen, clear_callback)

    # 更新所有子图形:

   
sprites.update()

    # 绘制所有子图形:

   
updates = sprites.draw(screen)

    # 更新所需的显示部分:

   
pygame.display.update(updates)

运行后截图:

第29章 项目10:DIY街机游戏

4.再次实现

29-2  config.py
—— Squish
的配置文件

#
coding=utf-8

# Squish
的配置文件

# ------------------------------

# 请放心地按照自己的喜好修改下面的配置文件



# 如果游戏太快或太慢,请修改速度变量

# 改变这些设置,一遍在游戏中使用其他图像:



banana_image = 'banana.png'

weight_image = 'weight.png'

splash_image = 'weight.png'



# 改变这些设置以影响一般的外观:

screen_size = 800, 600

background_color = 255, 255, 255

margin = 30

full_screen = 1

font_size = 48



# 这些设置会影响游戏的表现行为:

drop_speed = 1

banana_speed = 10

speed_increase = 1

weights_per_level = 10

banana_pad_top = 40

banana_pad_side = 20

29-3  objects.py
—— Squish
Game对象

#
coding=utf-8

import pygame,
config, os

from random
import randrange



# 这个模块包括Squish的游戏对象。



class SquishSprite(pygame.sprite.Sprite):

    """

    Squish
中所有子图形的范型超类。

    """

   
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):

    """

   
落下的秤砣。它使用了SquishSprite构造函数设置它的秤砣图像,并且会以给定的速度作为

    构造函数的参数来设置下落的速度。

    """

   
def
__init__(self,
speed):

        SquishSprite.__init__(self, config.weight_image)

        self.speed
= speed

        self.reset()



    def reset(self):

        """

      
将秤砣移动到屏幕顶端(视线外),放置到任意水平位置上。

        """

        # random between (30,770)

       
x = randrange(self.area.left,
self.area.right)

        self.rect.midbottom
= x, 0



    def update(self):

        """

       
根据它的速度将秤砣垂直移动(下落)一段距离。并且根据它是否触及屏幕底端来设置landed属性。

        """

       
self.rect.top += self.speed

        self.landed
= self.rect.top >= self.area.bottom



class Banana(SquishSprite):

    """

   
绝望的香蕉。它使用SquishSprite构造函数设置香蕉的图像,并且会停留在屏幕底端。

    它的水平位置由当前的鼠标位置(有一定限制)决定

    """

   
def
__init__(self):

        SquishSprite.__init__(self, config.banana_image)

        self.rect.bottom
= self.area.bottom

        # 在没有香蕉的部分进行填充。如果秤砣移动到这些区域,它不会被判定为碰撞(或者说是将香蕉压扁):

       
self.pad_top = config.banana_pad_top

        self.pad_side
= config.banana_pad_side



    def update(self):

        """

       
将Banana中心点的横坐标设定为当前鼠标指针的横坐标,并且使用rec的clamp方法确保Banana停留在所允许的范围内。

        """

       
self.rect.centerx = pygame.mouse.get_pos()[0]

        self.rect
= self.rect.clamp(self.area)



    def touches(self,
other):

        """

       
确定香蕉是否触碰到了另外的子图形(比如秤砣)。除了使用rec的colliderect方法外,

        首先要计算一个不包括香蕉图像顶端和侧边“空区域”的新矩形(使用rect的inflate方法对顶端和侧边进行填充)。

        """

        # 使用适当的填充缩小边界:

       
bounds = self.rect.inflate(-self.pad_side, -self.pad_top)

        # 移动边界,将它们放置到banana的底部。

       
bounds.bottom = self.rect.bottom

        # 检查边界是否和其他对象的rect交叉。

       
return
bounds.colliderect(other.rect)

29-4  squish.py
——
Game模块

#
coding=utf-8

import os,
sys, pygame

from pygame.locals
import *

import objects,
config



# 这个模块包括Squish游戏的主要游戏逻辑。

class State:

    """

   
范型游戏状态类,可以处理时间并在给定的表面上显示自身。

    """

   
def
handle(self, event):

        """

       
只处理退出时间的默认事件处理。

        """

       
if
event.type == QUIT:

            sys.exit()

        if event.type == KEYDOWN and
event.key == K_ESCAPE:

            sys.exit()



    def firstDisplay(self,
screen):

        """

       
用于第一次显示状态。使用背景色填充屏幕。

        """

       
screen.fill(config.background_color)

        # 记得要使用flip,让更改可见

       
pygame.display.flip()



    def display(self,
screen):

        """

       
用于在已经显示过一次状态后再次显示。默认的行为是什么都不做。

        """

       
pass



class
Level(State):

    """

   
游戏等级。用于计算已经落下了多少秤砣,移动子图形以及其他和游戏逻辑相关的任务。

    """

   
def
__init__(self,
number=1):

        self.number
= number

        # 本关内还要落下多少秤砣?

       
self.remaining = config.weights_per_level



        speed = config.drop_speed

        # 为每个大于1 的等级都增加一个speed_increase

       
speed += (self.number
- 1) * config.speed_increase

        # 创建秤砣和香蕉:

       
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):

        "从前一帧更新游戏状态"

        # 更新所有子图形:

       
self.sprites.update()

        # 如果香蕉碰到了秤砣,那么告诉游戏切换到GameOver状态::

       
if self.banana.touches(self.weight):

            game.nextState = GameOver()

        # 否则在秤砣落地时将其复位。

        # 如果在本馆内所有秤砣都落下了,则让游戏切换到LevelCleared 状态::

       
elif
self.weight.landed:

            self.weight.reset()

            self.remaining
-= 1

            if self.remaining == 0:

                game.nextState =
LevelCleared(self.number)



    def display(self,
screen):

        """

       
在第一次显示(只清空屏幕)后显示状态。与firstDisplay不同,这个方法使用pygame.display.update对

        self.sprites.draw提供的、需要更新的矩形列表进行更新。

        """

       
screen.fill(config.background_color)

        updates = self.sprites.draw(screen)

        pygame.display.update(updates)



class Paused(State):

    """

   
简单的暂停游戏状态,按下键盘上任意键或者点击鼠标都会结束这个状态。

    """

   
finished = 0  # 用户结束暂停了吗?

   
image = None  # 如果需要图片的话,将这个变量设置为文件名

   
text = '' 
# 将它设定为一些提示性文本



   
def
handle(self, event):

        """

       
通过对State进行委托(一般处理退出事件)以及对按键和鼠标点击最、作为反应来处理事件。

        如果键被按下或者鼠标被点击,将self.finished设定为真。

        """

       
State.handle(self,
event)

        if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:

            self.finished
= 1



    def update(self,
game):

        """

       
更新等级。如果按键被按下或者鼠标被点击(比如self.finished为真),那么告诉我们切换到下一个

        由self.nextState()表示的状态(应该由子类实现)

        """

       
if
self.finished:

            game.nextState = self.nextState()



    def firstDisplay(self,
screen):

        """

       
暂停状态第一次出现时,绘制图像(如果有的话)并且生成文本。

        """

        #首先,使用填充背景色的方式清空屏幕:

       
screen.fill(config.background_color)



        # 使用默认的外观和指定的大小创建Font对象

       
font = pygame.font.Font(None, config.font_size)



        # 获取self.text中的文本行,忽略开头和结尾的空行:

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



        # 计算文本的高度(使用 font.get_linesize())以获取每行文本的高度:

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



        # 计算文本的放置位置(屏幕中心):

       
center, top =
screen.get_rect().center  #
为400,300

       
top -= height // 2  #
260

        #
如果有图片要显示的话...

       
if
self.image:

            # 载入图片:

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

            # 获取它的rect:

           
r = image.get_rect()  # 为rect(0,0,166,132)

            # 将图像向下移动其高度的一半的距离:

           
top += r.height // 2  #
326

            #
将图片放置在文本上方20像素处:

           
r.midbottom = center, top - 20  #
400,306

            #
将图像移动到屏幕上:

           
screen.blit(image, r)



        antialias = 1  #
Smooth the text

       
black = 0, 0, 0  #
Render it as black



        #
生成所有行,从计算过的top开始,并且对于每一行向下移动font.get_linesize()像素:

       
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()



            # 显示所有更改:

       
pygame.display.flip()



class Info(Paused):

    """

   
简单的暂停状态,显示有关游戏的信息。在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):

    """

   
显示展示图片和欢迎信息的暂停状态。在Info状态后显示。

    """

   
nextState = Info

    image = config.splash_image

    text = '''''

    Welcome to Squish,

    the game of Fruit Self-Defense'''



class LevelCleared(Paused):

    """

    
提示用户过关的暂停状态。在next
level状态后显示。

    """

   
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):

    """

   
提示用户输掉游戏的状态。在first
level后显示。

    """

   
nextState = Level

    text = '''''

    Game Over

    Click to Restart, Esc to Quit'''



class Game:

    """

  
负责主事件循环的游戏对象,任务不包括在不同状态间切换。

    """

   
def
__init__(self,
*args):

       
# 获取游戏和图像放置的目录::

       
path = os.path.abspath(args[0])  #
当前代码文件路径

       
dir = os.path.split(path)[0]  #
代码目录

        # 移动那个目录(这样图片文件可以在随后打开):

       
os.chdir(dir)  # cd 到代码目录

        # 无状态方式启动:

       
self.state = None

        # 在第一个事件循环迭代中移动到StartUp

       
self.nextState = StartUp()



    def run(self):

        """

       
这个方法动态设定变量。进行一些重要的初始化工作,并且进入主事件循环。

        """

       
pygame.init()  # 初始化所有 pygame 模块。



        # 决定以窗口模式还是全屏模式显示游戏

       
flag = 0                  #
默认窗口



       
if
config.full_screen:

            flag = FULLSCREEN  # 全屏模式

       
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)



        # 主循环:

       
while
True:

            # (1) 如果nextState被修改了,那么移动到新状态,并且显示它(第一次)

           
if
self.state != self.nextState:

                self.state
= self.nextState

                self.state.firstDisplay(screen)

            # (2)代理当前状态的事件处理:

           
for
event in
pygame.event.get():

                self.state.handle(event)

            # (3) 更新当前状态:

           
self.state.update(self)

            # (4) 显示当前状态:

            # time.sleep( 0.5 )

           
self.state.display(screen)



if __name__
== '__main__':

    # print(sys.argv)

   
game = Game(*sys.argv)

    game.run()

运行后截图:

第29章 项目10:DIY街机游戏

4.再次实现

29-2  config.py
—— Squish
的配置文件

#
coding=utf-8

# Squish
的配置文件

# ------------------------------

# 请放心地按照自己的喜好修改下面的配置文件



# 如果游戏太快或太慢,请修改速度变量

# 改变这些设置,一遍在游戏中使用其他图像:



banana_image = 'banana.png'

weight_image = 'weight.png'

splash_image = 'weight.png'



# 改变这些设置以影响一般的外观:

screen_size = 800, 600

background_color = 255, 255, 255

margin = 30

full_screen = 1

font_size = 48



# 这些设置会影响游戏的表现行为:

drop_speed = 1

banana_speed = 10

speed_increase = 1

weights_per_level = 10

banana_pad_top = 40

banana_pad_side = 20

29-3  objects.py
—— Squish
Game对象

#
coding=utf-8

import pygame,
config, os

from random
import randrange



# 这个模块包括Squish的游戏对象。



class SquishSprite(pygame.sprite.Sprite):

    """

    Squish
中所有子图形的范型超类。

    """

   
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):

    """

   
落下的秤砣。它使用了SquishSprite构造函数设置它的秤砣图像,并且会以给定的速度作为

    构造函数的参数来设置下落的速度。

    """

   
def
__init__(self,
speed):

        SquishSprite.__init__(self, config.weight_image)

        self.speed
= speed

        self.reset()



    def reset(self):

        """

      
将秤砣移动到屏幕顶端(视线外),放置到任意水平位置上。

        """

        # random between (30,770)

       
x = randrange(self.area.left,
self.area.right)

        self.rect.midbottom
= x, 0



    def update(self):

        """

       
根据它的速度将秤砣垂直移动(下落)一段距离。并且根据它是否触及屏幕底端来设置landed属性。

        """

       
self.rect.top += self.speed

        self.landed
= self.rect.top >= self.area.bottom



class Banana(SquishSprite):

    """

   
绝望的香蕉。它使用SquishSprite构造函数设置香蕉的图像,并且会停留在屏幕底端。

    它的水平位置由当前的鼠标位置(有一定限制)决定

    """

   
def
__init__(self):

        SquishSprite.__init__(self, config.banana_image)

        self.rect.bottom
= self.area.bottom

        # 在没有香蕉的部分进行填充。如果秤砣移动到这些区域,它不会被判定为碰撞(或者说是将香蕉压扁):

       
self.pad_top = config.banana_pad_top

        self.pad_side
= config.banana_pad_side



    def update(self):

        """

       
将Banana中心点的横坐标设定为当前鼠标指针的横坐标,并且使用rec的clamp方法确保Banana停留在所允许的范围内。

        """

       
self.rect.centerx = pygame.mouse.get_pos()[0]

        self.rect
= self.rect.clamp(self.area)



    def touches(self,
other):

        """

       
确定香蕉是否触碰到了另外的子图形(比如秤砣)。除了使用rec的colliderect方法外,

        首先要计算一个不包括香蕉图像顶端和侧边“空区域”的新矩形(使用rect的inflate方法对顶端和侧边进行填充)。

        """

        # 使用适当的填充缩小边界:

       
bounds = self.rect.inflate(-self.pad_side, -self.pad_top)

        # 移动边界,将它们放置到banana的底部。

       
bounds.bottom = self.rect.bottom

        # 检查边界是否和其他对象的rect交叉。

       
return
bounds.colliderect(other.rect)

29-4  squish.py
——
Game模块

#
coding=utf-8

import os,
sys, pygame

from pygame.locals
import *

import objects,
config



# 这个模块包括Squish游戏的主要游戏逻辑。

class State:

    """

   
范型游戏状态类,可以处理时间并在给定的表面上显示自身。

    """

   
def
handle(self, event):

        """

       
只处理退出时间的默认事件处理。

        """

       
if
event.type == QUIT:

            sys.exit()

        if event.type == KEYDOWN and
event.key == K_ESCAPE:

            sys.exit()



    def firstDisplay(self,
screen):

        """

       
用于第一次显示状态。使用背景色填充屏幕。

        """

       
screen.fill(config.background_color)

        # 记得要使用flip,让更改可见

       
pygame.display.flip()



    def display(self,
screen):

        """

       
用于在已经显示过一次状态后再次显示。默认的行为是什么都不做。

        """

       
pass



class
Level(State):

    """

   
游戏等级。用于计算已经落下了多少秤砣,移动子图形以及其他和游戏逻辑相关的任务。

    """

   
def
__init__(self,
number=1):

        self.number
= number

        # 本关内还要落下多少秤砣?

       
self.remaining = config.weights_per_level



        speed = config.drop_speed

        # 为每个大于1 的等级都增加一个speed_increase

       
speed += (self.number
- 1) * config.speed_increase

        # 创建秤砣和香蕉:

       
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):

        "从前一帧更新游戏状态"

        # 更新所有子图形:

       
self.sprites.update()

        # 如果香蕉碰到了秤砣,那么告诉游戏切换到GameOver状态::

       
if self.banana.touches(self.weight):

            game.nextState = GameOver()

        # 否则在秤砣落地时将其复位。

        # 如果在本馆内所有秤砣都落下了,则让游戏切换到LevelCleared 状态::

       
elif
self.weight.landed:

            self.weight.reset()

            self.remaining
-= 1

            if self.remaining == 0:

                game.nextState =
LevelCleared(self.number)



    def display(self,
screen):

        """

       
在第一次显示(只清空屏幕)后显示状态。与firstDisplay不同,这个方法使用pygame.display.update对

        self.sprites.draw提供的、需要更新的矩形列表进行更新。

        """

       
screen.fill(config.background_color)

        updates = self.sprites.draw(screen)

        pygame.display.update(updates)



class Paused(State):

    """

   
简单的暂停游戏状态,按下键盘上任意键或者点击鼠标都会结束这个状态。

    """

   
finished = 0  # 用户结束暂停了吗?

   
image = None  # 如果需要图片的话,将这个变量设置为文件名

   
text = '' 
# 将它设定为一些提示性文本



   
def
handle(self, event):

        """

       
通过对State进行委托(一般处理退出事件)以及对按键和鼠标点击最、作为反应来处理事件。

        如果键被按下或者鼠标被点击,将self.finished设定为真。

        """

       
State.handle(self,
event)

        if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:

            self.finished
= 1



    def update(self,
game):

        """

       
更新等级。如果按键被按下或者鼠标被点击(比如self.finished为真),那么告诉我们切换到下一个

        由self.nextState()表示的状态(应该由子类实现)

        """

       
if
self.finished:

            game.nextState = self.nextState()



    def firstDisplay(self,
screen):

        """

       
暂停状态第一次出现时,绘制图像(如果有的话)并且生成文本。

        """

        #首先,使用填充背景色的方式清空屏幕:

       
screen.fill(config.background_color)



        # 使用默认的外观和指定的大小创建Font对象

       
font = pygame.font.Font(None, config.font_size)



        # 获取self.text中的文本行,忽略开头和结尾的空行:

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



        # 计算文本的高度(使用 font.get_linesize())以获取每行文本的高度:

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



        # 计算文本的放置位置(屏幕中心):

       
center, top =
screen.get_rect().center  #
为400,300

       
top -= height // 2  #
260

        #
如果有图片要显示的话...

       
if
self.image:

            # 载入图片:

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

            # 获取它的rect:

           
r = image.get_rect()  # 为rect(0,0,166,132)

            # 将图像向下移动其高度的一半的距离:

           
top += r.height // 2  #
326

            #
将图片放置在文本上方20像素处:

           
r.midbottom = center, top - 20  #
400,306

            #
将图像移动到屏幕上:

           
screen.blit(image, r)



        antialias = 1  #
Smooth the text

       
black = 0, 0, 0  #
Render it as black



        #
生成所有行,从计算过的top开始,并且对于每一行向下移动font.get_linesize()像素:

       
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()



            # 显示所有更改:

       
pygame.display.flip()



class Info(Paused):

    """

   
简单的暂停状态,显示有关游戏的信息。在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):

    """

   
显示展示图片和欢迎信息的暂停状态。在Info状态后显示。

    """

   
nextState = Info

    image = config.splash_image

    text = '''''

    Welcome to Squish,

    the game of Fruit Self-Defense'''



class LevelCleared(Paused):

    """

    
提示用户过关的暂停状态。在next
level状态后显示。

    """

   
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):

    """

   
提示用户输掉游戏的状态。在first
level后显示。

    """

   
nextState = Level

    text = '''''

    Game Over

    Click to Restart, Esc to Quit'''



class Game:

    """

  
负责主事件循环的游戏对象,任务不包括在不同状态间切换。

    """

   
def
__init__(self,
*args):

       
# 获取游戏和图像放置的目录::

       
path = os.path.abspath(args[0])  #
当前代码文件路径

       
dir = os.path.split(path)[0]  #
代码目录

        # 移动那个目录(这样图片文件可以在随后打开):

       
os.chdir(dir)  # cd 到代码目录

        # 无状态方式启动:

       
self.state = None

        # 在第一个事件循环迭代中移动到StartUp

       
self.nextState = StartUp()



    def run(self):

        """

       
这个方法动态设定变量。进行一些重要的初始化工作,并且进入主事件循环。

        """

       
pygame.init()  # 初始化所有 pygame 模块。



        # 决定以窗口模式还是全屏模式显示游戏

       
flag = 0                  #
默认窗口



       
if
config.full_screen:

            flag = FULLSCREEN  # 全屏模式

       
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)



        # 主循环:

       
while
True:

            # (1) 如果nextState被修改了,那么移动到新状态,并且显示它(第一次)

           
if
self.state != self.nextState:

                self.state
= self.nextState

                self.state.firstDisplay(screen)

            # (2)代理当前状态的事件处理:

           
for
event in
pygame.event.get():

                self.state.handle(event)

            # (3) 更新当前状态:

           
self.state.update(self)

            # (4) 显示当前状态:

            # time.sleep( 0.5 )

           
self.state.display(screen)



if __name__
== '__main__':

    # print(sys.argv)

   
game = Game(*sys.argv)

    game.run()

运行后截图:game.MP4

第29章 项目10:DIY街机游戏

附:摘自:http://eyehere.net/2011/python-pygame-novice-professional-1/

test.py

# coding=utf-8

background_image_filename
= 'sushiplate.jpg'

mouse_image_filename = 'fugu.png'

# 指定图像文件名称



import pygame

# 导入pygame库

from pygame.locals
import *

# 导入一些常用的函数和常量

from sys
import exit

# 向sys模块借一个exit函数用来退出程序



pygame.init()

# 初始化pygame,为使用硬件做准备



screen =
pygame.display.set_mode((640, 480), 0, 32)

# 创建了一个窗口

pygame.display.set_caption("Hello,
World!"
)

# 设置窗口标题



background =
pygame.image.load(background_image_filename).convert()

mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()

# 加载并转换图像



while True:

    # 游戏主循环



   
for
event in
pygame.event.get():

        if event.type == QUIT:

            # 接收到退出事件后退出程序

           
exit()



    screen.blit(background, (0, 0))

    # 将背景图画上去



   
x, y = pygame.mouse.get_pos()

    # 获得鼠标位置

   
x -= mouse_cursor.get_width() / 2

    y -= mouse_cursor.get_height() / 2

    # 计算光标的左上角位置

   
screen.blit(mouse_cursor, (x, y))

    # 把光标画上去



   
pygame.display.update()

    # 刷新一下画面

运行后截图:

第29章 项目10:DIY街机游戏

fugu.png和sushiplate.jpg

第29章 项目10:DIY街机游戏

第29章 项目10:DIY街机游戏

上一篇:ActiveMQ结合Spring开发


下一篇:为Kindeditor控件添加图片自动上传功能