第三周总结和复习

面向对象编程进阶

在上个周的学习中,我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息。为了能够更好的使用面向对象编程思想,我们还需要对Python中的面向对象编程进行更为深入的了解。

@property装饰器的初步了解

Python中有属性和方法访问权限的问题,我们将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装的方法,使得对属性的访问既安全又方便,代码如下所示。

"""
class Person(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 访问器 - getter方法
    @property
    def name(self):
        return self._name

    # 访问器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在做游戏.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 12)
    person.play()
    person.age = 22
    person.play()

if __name__ == '__main__':
    main()
"""

类之间的关系

简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。
is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。

"""
第一个Python程序:扑克游戏
Version:0.1
Author:贺松
Date:2018-3-19
"""
from random import randrange


class Card(object):
    """一张牌"""

    def __init__(self, suite, face):
        self._suite = suite
        self._face = face

    @property
    def face(self):
        return self._face

    @property
    def suite(self):
        return self._suite

    def __str__(self):
        all_suites = ('♠', '♥', '♣', '♦')
        if self._face == 1:
            face_str = 'A'
        elif self._face == 11:
            face_str = 'J'
        elif self._face == 12:
            face_str = 'Q'
        elif self._face == 13:
            face_str = 'K'
        else:
            face_str = str(self._face)
        return '%s%s' % (all_suites[self._suite], face_str)


class Poker(object):
    """一副牌"""

    def __init__(self):
        self._cards = []
        self._current = 0
        for suite in range(4):
            for face in range(1, 14):
                card = Card(suite, face)
                self._cards.append(card)

    @property
    def cards(self):
        return self._cards

    def shuffle(self):
        """洗牌"""
        self._current = 0
        cards_len = len(self._cards)
        for index in range(cards_len):
            pos = randrange(cards_len)
            self._cards[index], self._cards[pos] = \
                self._cards[pos], self._cards[index]

    @property
    def next(self):
        """发牌"""
        card = self._cards[self._current]
        self._current += 1
        return card

    @property
    def has_next(self):
        """还有没有牌"""
        return self._current < len(self._cards)


class Player(object):
    """玩家"""

    def __init__(self, name):
        self._name = name
        self._cards_on_hand = []

    @property
    def name(self):
        return self._name

    @property
    def cards_on_hand(self):
        return self._cards_on_hand

    def get(self, card):
        """摸牌"""
        self._cards_on_hand.append(card)

    def arrange(self, card_key):
        """玩家整理手上的牌"""
        self._cards_on_hand.sort(key=card_key)


# 排序规则-先根据花色再根据点数排序
def get_key(card):
    return (card.suite, card.face)


def main():
    p = Poker()
    p.shuffle()
    players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
    for _ in range(13):
        for player in players:
            player.get(p.next)
    for player in players:
        print(player.name + ':', end=' ')
        player.arrange(get_key)
        for card in player.cards_on_hand:
            print(card, end=' ')
        print()


if __name__ == '__main__':
    main()

图形用户界面和游戏开发

使用Pygame进行游戏开发

Pygame是一个开源的Python模块,用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在SDL的基础上,SDL是一套跨平台的多媒体开发库,用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。而Pygame让游戏开发者不再被底层语言束缚,可以更多的关注游戏的功能和逻辑。
下面我们来完成一个简单的小游戏,游戏的名字叫“大球吃小球”,体会如何使用前面学习的面向对象程序设计,学会用这种编程思想去解决现实中的问题。

制作游戏窗口

import pygame


def main():
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800, 600))
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


if __name__ == '__main__':
    main()

在窗口中绘图

import pygame


def main():
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800, 600))
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组)
    screen.fill((242, 242, 242))
    # 绘制一个圆(参数分别是: 屏幕, 颜色, 圆心位置, 半径, 0表示填充圆)
    pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0)
    # 刷新当前窗口(渲染窗口将绘制的图像呈现出来)
    pygame.display.flip()
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


if __name__ == '__main__':
    main()

加载图像

import pygame


def main():
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800, 600))
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组)
    screen.fill((255, 255, 255))
    # 通过指定的文件名加载图像
    ball_image = pygame.image.load('./res/ball.png')
    # 在窗口上渲染图像
    screen.blit(ball_image, (50, 50))
    # 刷新当前窗口(渲染窗口将绘制的图像呈现出来)
    pygame.display.flip()
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


if __name__ == '__main__':
    main()

动画效果

import pygame


def main():
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800, 600))
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    # 定义变量来表示小球在屏幕上的位置
    x, y = 50, 50
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        screen.fill((255, 255, 255))
        pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0)
        pygame.display.flip()
        # 每隔50毫秒就改变小球的位置再刷新窗口
        pygame.time.delay(50)
        x, y = x + 5, y + 5


if __name__ == '__main__':
    main()

碰撞效果(大球吃小球)

from enum import Enum, unique
from math import sqrt
from random import randint

import pygame


@unique
class Color(Enum):
    """颜色"""

    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GRAY = (242, 242, 242)

    @staticmethod
    def random_color():
        """获得随机颜色"""
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        return (r, g, b)


class Ball(object):
    """球"""

    def __init__(self, x, y, radius, sx, sy, color=Color.RED):
        """初始化方法"""
        self.x = x
        self.y = y
        self.radius = radius
        self.sx = sx
        self.sy = sy
        self.color = color
        self.alive = True

    def move(self, screen):
        """移动"""
        self.x += self.sx
        self.y += self.sy
        if self.x - self.radius <= 0 or \
                self.x + self.radius >= screen.get_width():
            self.sx = -self.sx
        if self.y - self.radius <= 0 or \
                self.y + self.radius >= screen.get_height():
            self.sy = -self.sy

    def eat(self, other):
        """吃其他球"""
        if self.alive and other.alive and self != other:
            dx, dy = self.x - other.x, self.y - other.y
            distance = sqrt(dx ** 2 + dy ** 2)
            if distance < self.radius + other.radius \
                    and self.radius > other.radius:
                other.alive = False
               a self.radius = self.radius + int(other.radius * 0.146)

    def draw(self, screen):
        """在窗口上绘制球"""
        pygame.draw.circle(screen, self.color,
                           (self.x, self.y), self.radius, 0)

事件处理

def main():
    # 定义用来装所有球的容器
    balls = []
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800, 600))
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            # 处理鼠标事件的代码
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                # 获得点击鼠标的位置
                x, y = event.pos
                radius = randint(10, 100)
                sx, sy = randint(-10, 10), randint(-10, 10)
                color = Color.random_color()
                # 在点击鼠标的位置创建一个球(大小、速度和颜色随机)
                ball = Ball(x, y, radius, sx, sy, color)
                # 将球添加到列表容器中
                balls.append(ball)
        screen.fill((255, 255, 255))
        # 取出容器中的球 如果没被吃掉就绘制 被吃掉了就移除
        for ball in balls:
            if ball.alive:
                ball.draw(screen)
            else:
                balls.remove(ball)
        pygame.display.flip()
        # 每隔50毫秒就改变球的位置再刷新窗口
        pygame.time.delay(50)
        for ball in balls:
            ball.move(screen)
            # 检查球有没有吃到其他的球
            for other in balls:
                ball.eat(other)


if __name__ == '__main__':
    main()

我们可以用Pygame再做一些其它的游戏,例如五子棋游戏,贪吃蛇游戏

"""
第二个Python程序:五子棋
Version:0.1
Author:贺松
Date:2018-3-19
"""
import pygame

EMPTY = 0
BLACK = 1
WHITE = 2

black_color = [0, 0, 0]
white_color = [255, 255, 255]


class RenjuBoard(object):

    def __init__(self):
        self._board = [[]] * 15
        self.reset()

    def reset(self):
        for row in range(len(self._board)):
            self._board[row] = [EMPTY] * 15

    def move(self, row, col, is_black):
        if self._board[row][col] == EMPTY:
            self._board[row][col] = BLACK if is_black else WHITE
            return True
        return False

    def draw(self, screen):
        for index in range(1, 16):
            pygame.draw.line(screen, black_color,
                             [40, 40 * index], [600, 40 * index], 1)
            pygame.draw.line(screen, black_color,
                             [40 * index, 40], [40 * index, 600], 1)
        pygame.draw.rect(screen, black_color, [36, 36, 568, 568], 4)
        pygame.draw.circle(screen, black_color, [320, 320], 5, 0)
        pygame.draw.circle(screen, black_color, [160, 160], 5, 0)
        pygame.draw.circle(screen, black_color, [480, 480], 5, 0)
        pygame.draw.circle(screen, black_color, [480, 160], 5, 0)
        pygame.draw.circle(screen, black_color, [160, 480], 5, 0)
        for row in range(len(self._board)):
            for col in range(len(self._board[row])):
                if self._board[row][col] != EMPTY:
                    ccolor = black_color \
                        if self._board[row][col] == BLACK else white_color
                    pos = [40 * (col + 1), 40 * (row + 1)]
                    pygame.draw.circle(screen, ccolor, pos, 20, 0)


def main():
    board = RenjuBoard()
    is_black = True
    pygame.init()
    pygame.display.set_caption('五子棋')
    screen = pygame.display.set_mode([640, 640])
    screen.fill([255, 255, 0])
    board.draw(screen)
    pygame.display.flip()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYUP:
                pass
            elif event.type == pygame.MOUSEBUTTONDOWN\
                    and event.button == 1:
                x, y = event.pos
                row = round((y - 40) / 40)
                col = round((x - 40) / 40)
                if board.move(row, col, is_black):
                    is_black = not is_black
                    screen.fill([255, 255, 0])
                    board.draw(screen)
                    pygame.display.flip()
    pygame.quit()


if __name__ == '__main__':
    main()
"""
第三个Python程序:贪吃蛇
Version:0.1
Author:贺松
Date:2018-3-19
"""
import pygame
from random import randint
from abc import ABCMeta, abstractmethod

BLACK_COLOR = (0, 0, 0)  # 全局常量,黑色
RED_COLOR = (255, 0, 0)  # 全局常量,红色
FOOD_COLOR = (230, 185, 185)  # 全局常量,食物的颜色
GREEN_COLOR = (0, 255, 0)  # 绿色

UP = 0  # 向上
RIGHT = 1  # 向右
DOWN = 2  # 向下
LEFT = 3  # 向左


class GameObject(object, metaclass=ABCMeta):
    """游戏物品抽象父类"""

    def __init__(self, pos=(0, 0), color=BLACK_COLOR):
        """初始化方法"""
        self._pos = pos  # 在界面中的位置
        self._color = color  # 在界面中的颜色

    @abstractmethod
    def draw(self, screen):
        """
        抽象方法,绘制对象

        :param screen: 在哪个图像表面层绘画
        :return: None
        """
        pass

    @property
    def pos(self):
        """
        装饰器修饰,直接获取对象在界面中的位置

        :return: 元组,(x,y)
        """
        return self._pos


class Wall(GameObject):
    """四周墙的类"""

    def __init__(self, pos, width, height, tick, color=BLACK_COLOR):
        """
        初始化、构造方法

        :param pos: 墙开始画的坐标点
        :param width: 墙的宽度
        :param height: 墙的高度
        :param tick: 墙的厚度
        :param color: 墙的颜色
        """
        super().__init__(pos, color)
        self._width = width
        self._height = height
        self._tick = tick

    @property
    def width(self):
        return self._width

    @property
    def height(self):
        return self._height

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._pos[0], self._pos[1], self._width, self._height), self._tick)


class Food(GameObject):
    """食物类"""

    def __init__(self, pos, size=10, color=RED_COLOR):
        """
        初始化方法

        :param pos: 外切圆坐标
        :param color:
        :param size:
        """
        super().__init__(pos, color)
        self._is_hide = 0  # 是否隐藏的计数器,为了食物点不频繁的闪烁
        self._size = size  # 食物点的大小,直径

    def draw(self, screen):
        """
        绘制食物的方法

        :param screen: 在哪个表面绘制
        :return: None
        """
        # 如果不是5的倍数,就消失一次,减少Boolean值的频繁闪烁,数值越大闪烁越快
        if self._is_hide % 5 != 0:
            # 开始在界面进行绘制食物
            pygame.draw.circle(screen, self._color,
                               (self._pos[0] + self._size // 2, self._pos[1] + self._size // 2),
                               self._size // 2, 0)
        self._is_hide += 1  # 计数点自加


class SnakeNode(GameObject):
    """蛇的结点类"""

    def __init__(self, pos, size, color=GREEN_COLOR):
        """
        初始化构造方法

        :param pos: 结点的坐标
        :param size: 结点的大小
        :param color: 结点的颜色
        """
        super().__init__(pos, color)
        self._size = size

    def draw(self, screen):
        """
        绘制结点

        :param screen: 在哪个表面进行绘制
        :return: None
        """
        # 绘制内部绿色的结点
        pygame.draw.rect(screen, self._color,
                         (self._pos[0], self._pos[1], self._size, self._size), 0)
        # 绘制结点的外部边框
        pygame.draw.rect(screen, BLACK_COLOR,
                         (self._pos[0], self._pos[1], self._size, self._size), 1)

    @property
    def size(self):
        """
        装饰器,修饰get_size方法

        :return: 结点的大小
        """
        return self._size


class Snake(GameObject):
    """蛇的类"""

    def __init__(self):
        """
        初始化构造方法

        """
        super().__init__()
        self._snake_dir = LEFT  # 默认的蛇爬行的方向为左
        self._nodes = []  # 蛇的结点容器
        self._aline = True  # 蛇是否存活
        self._eat_food = False  # 是否吃到食物
        for index in range(5):  # 初始化5个结点在列表容器中
            node = SnakeNode((290 + index * 20, 250), 20)
            self._nodes.append(node)

    def is_in_nodes(self, x, y):
        """
        判断刷新出来的苹果是否在蛇的结点上

        :param x:  食物的x坐标
        :param y:  食物的y坐标
        :return: boolean 值,在为True,不在为False
        """
        for node in self._nodes:
            if node.pos[0] == x and node.pos[1] == y:
                return True
        return False

    @property
    def aline(self):
        """
        装饰器,获取对象属性

        :return: aline属性
        """
        return self._aline

    @property
    def snake_dir(self):
        """
        装饰器,获取蛇移动的方向

        :return: snake_dir 属性
        """
        return self._snake_dir

    @property
    def head(self):
        """
        装饰器,获取容器中的第一个结点

        :return: nodes[0] 第一个元素
        """
        return self._nodes[0]

    def change_dir(self, new_dir):
        """
        改变蛇前进的方向

        :param new_dir:  前进的新方向
        :return: None
        """
        # 如果方向和原来方向不同,而且相反,就可以改变,蛇的前进方向
        if new_dir != self._snake_dir and (self._snake_dir + new_dir) % 2 != 0:
            self._snake_dir = new_dir

    def draw(self, screen):
        """
        画出蛇

        :param screen: 在哪个表面画蛇
        :return: None
        """
        # 遍历结点,同时画出结点
        for node in self._nodes:
            node.draw(screen)

    def move(self):
        """
        蛇移动的方法

        :return: None
        """
        # if self._aline:
        head = self.head  # 获取蛇头
        snake_dir = self._snake_dir  # 获取蛇前进的方向
        # 获取蛇头想x,y坐标和头的大小
        x, y, size = head.pos[0], head.pos[1], head.size
        if snake_dir == UP:  # 向上移动
            y -= size  # y坐标减少
        elif snake_dir == RIGHT:  # 向右移动
            x += size  # x坐标增加
        elif snake_dir == DOWN:  # 向下移动
            y += size  # y坐标增加
        else:  # 向左移动
            x -= size  # x坐标减少
        new_head = SnakeNode((x, y), size)  # 新建一个结点对象
        self._nodes.insert(0, new_head)  # 添加到蛇头
        if self._eat_food:  # 如果吃到了食物
            self._eat_food = False  # 重设参数为没吃到,同时不移除最后的一个结点
        else:  # 没吃到食物
            self._nodes.pop()  # 移除最后的一个结点元素

    def collide(self, wall):
        """
        判断是否撞墙

        :param wall: 墙的对象
        :return: None
        """
        x, y = wall.pos  # 获取墙的坐标
        width = wall.width  # 获取墙的宽
        height = wall.height  # 获取墙的高
        node_one = self._nodes[0]  # 获取蛇头对象
        # 如果蛇头的x坐标小于了墙的最左边,或加上自身宽度大于了墙的初始位置加宽度,
        # 或蛇头的y坐标小于了墙最上面,或加上自身宽度大于了墙初始xy位置加高度
        if x > node_one.pos[0] or node_one.pos[0] + node_one.size > x + width \
                or y > node_one.pos[1] or node_one.pos[1] + node_one.size > y + height:
            self._aline = False  # 蛇撞到墙,死了
        else:
            self._aline = True  # 蛇没撞到墙,还或者

    def eat_food(self, food):
        """
        蛇吃食物的行为

        :param food: food对象
        :return: Boolean 是否吃到
        """
        head = self.head  # 获取蛇头结点
        # 如果蛇头的x,y坐标与食物的x,y坐标重合,那么就吃到了食物
        if head.pos[0] == food.pos[0] and head.pos[1] == food.pos[1]:
            self._eat_food = True  # 吃到食物的状态为True
            return True  # 返回True
        return False  # 没吃到食物返回False

    def eat_me(self):
        """
        吃自己判断

        :return: None
        """
        head = self.head  # 获取蛇头结点对象
        # 从蛇的第4个结点开始遍历,因为,蛇只能吃到自己的第四个结点
        for index in range(3, len(self._nodes)):
            # 如果蛇头坐标和蛇身坐标重合,就吃到了自己
            if head.pos[0] == self._nodes[index].pos[0] and \
                    head.pos[1] == self._nodes[index].pos[1]:
                self._aline = False  # 修改当前蛇的存活状态

    @property
    def snake_len(self):
        """
        获取蛇的结点长度

        :return: 蛇的长度
        """
        return len(self._nodes)


def main():
    """游戏主函数"""

    def refresh():
        """刷新游戏窗口"""
        screen.fill((242, 242, 242))
        snake.draw(screen)
        food.draw(screen)
        wall.draw(screen)
        pygame.display.flip()

    def handle_key_event(key_event):
        """游戏按键事件监听"""
        key = key_event.key  # 拿取到所按的按键的值
        if key == pygame.K_F2:  # 如果为F2则重新开始游戏
            reset_game()
        # 如果key为w,a,s,d则为方向控制
        elif key in (pygame.K_w, pygame.K_d, pygame.K_s, pygame.K_a):
            if snake.aline:  # 如果蛇还活着则进行操作
                if key == pygame.K_w:  # 按下了W
                    new_dir = UP  # 设置新的方向向上
                elif key == pygame.K_d:  # 按下了D
                    new_dir = RIGHT  # 设置新的方向向右
                elif key == pygame.K_s:  # 按下了S
                    new_dir = DOWN  # 设置新的方向向下
                else:  # 按下了A
                    new_dir = LEFT  # 设置新的方向向左
                snake.change_dir(new_dir)  # 调用蛇的方法,发送消息改变方向
        pygame.event.clear()  # 清除按键事件

    def reset_game():
        """重设游戏的食物和蛇的对象,游戏重开"""
        nonlocal food, snake, game_over  # 设置为嵌套值域,而非当前函数
        food = create_apple(snake)  # 创建新的食物对象
        snake = Snake()  # 创建新的蛇对象
        game_over = False  # 游戏继续

    scores = []  # 分数容器
    pygame.init()  # 初始化pygame
    wall = Wall((10, 10), 600, 600, 3)  # 创建墙的对象
    screen = pygame.display.set_mode([620, 620])  # 设置窗口大小
    screen.fill((242, 242, 242))  # 设置填充的背景
    pygame.display.set_caption('贪吃蛇')  # 设置窗口标题
    pygame.display.flip()  # 刷新界面
    clock = pygame.time.Clock()  # 设置时钟
    snake = Snake()  # 初始化一个蛇对象
    food = create_apple(snake)  # 初始化一个食物对象
    reset_game()  # 重开游戏,刷新界面
    is_running = True  # 游戏运行状态
    game_over = False  # 游戏当前回合状态
    while is_running:
        for event in pygame.event.get():  # 获取事件
            if event.type == pygame.QUIT:  # 判断事件类型是否为退出
                is_running = False
            elif event.type == pygame.KEYDOWN:
                handle_key_event(event)  # 传递按钮点击事件
        if not game_over:  # 本回合是否继续
            if snake.aline:  # 当前蛇是否还存活
                refresh()  # 刷新游戏窗口
                snake.move()  # 蛇开始移动
                snake.collide(wall)  # 判断蛇是否撞墙
                snake.eat_me()  # 判断蛇是否吃到自己
                if snake.eat_food(food):  # 判断蛇是否吃到食物
                    food = create_apple(snake)  # 吃到食物,创建一个新的食物对象
            else:  # 本回合结束
                game_over = True  # 设置本回合结束
                scores.append(snake.snake_len - 5)  # 将分数添加到容器中
                show_info(screen, scores)  # 显示分数
        clock.tick(5)  # 帧数
    pygame.quit()  # 销毁pygame
    pass


def show_info(screen, scores):
    """
    在界面中绘制分数的函数

    :param screen:  绘制的表面
    :param scores:  绘制的分数列表
    :return:  None
    """
    scores.sort(reverse=True)  # 对分数进行排序
    my_font = pygame.font.SysFont('宋体', 60)  # 设置字体格式
    game_over = my_font.render('GAME OVER', False, [0, 0, 0])  # 字体的文本和颜色
    screen.blit(game_over, (180, 260))  # 开始绘制的坐标
    list_len = len(scores) if len(scores) < 5 else 5  # 获取分数容器的大小,最多展示前五
    for index in range(list_len):  # 遍历欠前五的分数
        my_font = pygame.font.SysFont('宋体', 40)  # 设置字体
        # 绘制的文字内容和分数
        score = my_font.render('Number ' + str(index + 1) + ':' + str(scores[index]), False, [255, 0, 0])
        screen.blit(score, (200, 300 + index * 40))  # 绘制的位置
    pygame.display.flip()  # 刷新界面


def create_apple(snake):
    """
    创建苹果在蛇的身体之外

    :param snake:  蛇的对象
    :return: 在蛇身之外的Food坐标
    """
    row = randint(0, 29)  # 随机行号
    col = randint(0, 29)  # 随机列号
    x, y = 10 + 20 * col, 10 + 20 * row  # 换算为对应的坐标点
    if not snake.is_in_nodes(x, y):  # 如果不在蛇身上
        return Food((10 + 20 * col, 10 + 20 * row), 20)  # 返回一个食物对象
    else:
        create_apple(snake)  # 递归调用函数直到有返回值


if __name__ == '__main__':
    main()

猜你喜欢

转载自blog.csdn.net/hs_blog/article/details/79606552