• 第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:python64pygame-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

     

  • 相关阅读:
    倍数求和
    最大(小)值
    数组扁平化:(多维数组 -> 一维数组)
    任意数求和
    找到重复最多项
    Linux 标准 I/O 库
    Linux文件操作函数
    Linux文件的IO操作 一
    Linux文件系统概述二
    Linux文件系统概述
  • 原文地址:https://www.cnblogs.com/Sumomo0516/p/6131803.html
Copyright © 2020-2023  润新知