面向对象编程进阶
在上个周的学习中,我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息。为了能够更好的使用面向对象编程思想,我们还需要对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()