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

1.问题

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

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

2.工具 —— Pygame

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

下载完成后双击安装即可。
64位下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame

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

未报错,安装成功。

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

运行后截图:

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 —— SquishGame对象

# 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 *
# -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中心点的横坐标设定为当前鼠标指针的横坐标,并且使用recclamp方法确保Banana停留在所允许的范围内。
        """
       
self.rect.centerx = pygame.mouse.get_pos()[0]
       
self.rect = self.rect.clamp(self.area)

   
def touches(self, other):
       
"""
       
确定香蕉是否触碰到了另外的子图形(比如秤砣)。除了使用reccolliderect方法外,
       
首先要计算一个不包括香蕉图像顶端和侧边“空区域”的新矩形(使用rectinflate方法对顶端和侧边进行填充)。
        """
        #
使用适当的填充缩小边界:
       
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 = # 用户结束暂停了吗?
   
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 // # 260
        #
如果有图片要显示的话...
       
if self.image:
           
# 载入图片:
           
image = pygame.image.load(self.image).convert()
           
# 获取它的rect:
           
r = image.get_rect()  # rect(0,0,166,132)
            #
将图像向下移动其高度的一半的距离:
           
top += r.height // # 326
            #
将图片放置在文本上方20像素处:
           
r.midbottom = center, top - 20  # 400,306
            #
将图像移动到屏幕上:
           
screen.blit(image, r)

        antialias =
# Smooth the text
       
black = 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()

 

运行后截图:

 

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 —— SquishGame对象

# 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 *
# -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中心点的横坐标设定为当前鼠标指针的横坐标,并且使用recclamp方法确保Banana停留在所允许的范围内。
        """
       
self.rect.centerx = pygame.mouse.get_pos()[0]
       
self.rect = self.rect.clamp(self.area)

   
def touches(self, other):
       
"""
       
确定香蕉是否触碰到了另外的子图形(比如秤砣)。除了使用reccolliderect方法外,
       
首先要计算一个不包括香蕉图像顶端和侧边“空区域”的新矩形(使用rectinflate方法对顶端和侧边进行填充)。
        """
        #
使用适当的填充缩小边界:
       
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 = # 用户结束暂停了吗?
   
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 // # 260
        #
如果有图片要显示的话...
       
if self.image:
           
# 载入图片:
           
image = pygame.image.load(self.image).convert()
           
# 获取它的rect:
           
r = image.get_rect()  # rect(0,0,166,132)
            #
将图像向下移动其高度的一半的距离:
           
top += r.height // # 326
            #
将图片放置在文本上方20像素处:
           
r.midbottom = center, top - 20  # 400,306
            #
将图像移动到屏幕上:
           
screen.blit(image, r)

        antialias =
# Smooth the text
       
black = 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

 

 

附:摘自: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()
    # 刷新一下画面

 

运行后截图:

fugu.png和sushiplate.jpg

 

 

 

posted @ 2016-12-04 20:45  Sumomo  阅读(1371)  评论(0编辑  收藏  举报