在这篇文章中,我们将使用Python和OpenCV创建贪吃蛇游戏。
1.贪吃蛇游戏
在贪吃蛇游戏中,我们使用箭头键控制数字蛇。
一个苹果随机出现在屏幕上,我们的目标是移动蛇,让它吃苹果。蛇在吃了苹果后会变大,我们的目标是让蛇尽可能长。
然而,如果蛇撞到墙或撞到自己,游戏就结束了。
2.用OpenCV实现贪吃蛇游戏
代码展示
import cv2
import numpy as np
from random import randint
from random import choice
class SnakePart:
def __init__(self, front, x, y):
self.front = front
self.x = x
self.y = y
def move(self):
# 跟随它前面的部分移动
self.x = self.front.x
self.y = self.front.y
class Head:
def __init__(self, direction, x, y):
self.direction = direction
self.x = x
self.y = y
def move(self):
# 检查它当前的方向,并相应地移动
if self.direction == 0:
self.x += 1
elif self.direction == 1:
self.y += 1
elif self.direction == 2:
self.x -= 1
elif self.direction == 3:
self.y -= 1
def display():
# 创建空白图像
board = np.zeros([BOARD_SIZE, BOARD_SIZE, 3])
# 把蛇涂成绿色
for part in snake:
board[part.y, part.x] = [0, 255, 0]
# 把苹果涂成红色
board[appley, applex] = [0, 0, 255]
# 显示屏
cv2.imshow("Snake Game", np.uint8(board.repeat(CELL_SIZE, 0).repeat(CELL_SIZE, 1)))
key = cv2.waitKey(int(1000/SPEED))
# 返回按下的键。如果没有按下键,则为-1。
return key
def win_focus():
# 全屏打开图像,然后返回正常窗口
cv2.namedWindow("Snake Game", cv2.WINDOW_AUTOSIZE);
board = np.zeros([BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE, 3])
cv2.imshow("Snake Game", board);
cv2.setWindowProperty("Snake Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN);
cv2.waitKey(2000)
cv2.setWindowProperty("Snake Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_AUTOSIZE)
if __name__ == '__main__' :
# 棋盘游戏中每个单元格的大小
CELL_SIZE = 20
# 游戏中沿宽度排列的单元数
BOARD_SIZE = 45
# 改变速度使游戏进行得更快
SPEED = 12
# 吃过苹果后,蛇会按生长单位生长
GROWTH = 3
# 变量跟踪苹果是否被吃
eaten = True
# 变量来检查游戏是否应该退出
quit = False
# 变量跟踪增长。
grow = 0
# 存储蛇的数组
snake = []
#蛇的头从棋盘的中心开始。
head = Head(0, int((BOARD_SIZE - 1)/2), int((BOARD_SIZE - 1)/2))
snake.append(head)
# 通过打印说明开始游戏
print('w = top, a = left, s = down, d = right')
# 让窗口聚焦的丑把戏
win_focus()
while True:
# 检查苹果是否被吃掉,并生成一个新的
if eaten:
# 列出所有可能的地点
s = list(range(0, BOARD_SIZE ** 2))
# 删除属于蛇的一部分的cell
for part in snake:
s.remove(part.x * BOARD_SIZE + part.y)
# 从剩余的cell中随机挑选一个
a = choice(s)
applex = int(a/BOARD_SIZE)
appley = a % BOARD_SIZE
eaten = False
# 制作并展示显示屏
key = display()
# 获取按下的键并相应地移动
# 8和27是删除键和退出键
# 方向键在OpenCV中很棘手。所以我们使用
#键'w', 'a','s','d'表示移动。
# w = top, a = left, s = down, d = right
if key == 8 or key == 27:
break
elif key == ord("d") :
head.direction = 0
elif key == ord("s") :
head.direction = 1
elif key == ord("a") :
head.direction = 2
elif key == ord("w") :
head.direction = 3
# 移动snake
for part in snake[::-1]:
part.move()
# 碰撞规则
if head.x < 0 or head.x > BOARD_SIZE - 1 or head.y < 0 or head.y > BOARD_SIZE - 1:
break
for part in snake[1:]:
if head.x == part.x and head.y == part.y:
quit = True
break
if quit:
break
# 蛇在多帧中逐渐生长
if grow > 0:
snake.append(SnakePart(snake[-1], subx, suby))
grow -= 1
# 当蛇吃苹果的时候,蛇就会长出来
if applex == head.x and appley == head.y:
subx = snake[-1].x
suby = snake[-1].y
eaten = True
grow += GROWTH
代码解析
首先,我们将导入一些库用于显示和生成苹果的随机位置。
import cv2
import numpy as np
from random import randint
from random import choice
让我们制作几个类来代表游戏。
- 1.
SnakePart
:这代表了蛇的身体(不包括头部)。 - 2.
Head
:这个类代表蛇头SnakePart
类有x和y坐标,以及对它前面的另一个SnakePart
或Head
的引用。
class SnakePart:
def __init__(self, front, x, y):
self.front = front
self.x = x
self.y = y
def move(self):
# Moves by following the part in front of it
self.x = self.front.x
self.y = self.front.y
move()
方法只是复制前面部件的位置。Head
类也有x和y。此外,它还有一个指定移动方向的direction
变量。
class Head:
def __init__(self, direction, x, y):
self.direction = direction
self.x = x
self.y = y
def move(self):
# 检查它当前的方向,并相应地移动
if self.direction == 0:
self.x += 1
elif self.direction == 1:
self.y += 1
elif self.direction == 2:
self.x -= 1
elif self.direction == 3:
self.y -= 1
现在,让我们转到主函数,看看游戏是如何实现的。我们首先定义几个有用的常量。CELL_SIZE
指定显示屏上每个单元格的宽度,以像素为单位。BOARD_SIZE
沿显示屏宽度的单元格数。在我们的示例中,CELL_SIZE
是20,BOARD_SIZE
是45。因此,显示器的大小是20 x 45 = 900
像素。SPEED
决定了蛇的快慢。如果游戏对你来说太快,请使用较小的数字。GROWTH
是指蛇吃了一个苹果后生长的单位数。
if __name__ == '__main__' :
# Size of each cell in the board game
CELL_SIZE = 20
# Number of cells along the width in the game
BOARD_SIZE = 45
# Change SPEED to make the game go faster
SPEED = 12
# After eating an apple the snake grows by GROWTH units
GROWTH = 3
接下来,我们定义一些以后会派上用场的变量。
# Variable to track if apple is eaten
eaten = True
# Variable to check if the game should quit
quit = False
# Variable to track growth.
grow = 0
现在,让我们创建一个空数组来存储head 和 snakeparts.。
# Array for storing snake
snake = []
#snake's head starts at the center of the board.
head = Head(0, int((BOARD_SIZE - 1)/2), int((BOARD_SIZE - 1)/2))
snake.append(head)
在进入程序的主while循环之前,让我们先看看显示当前游戏的display()函数。
在下面的代码中,我们执行以下操作。
- 我们首先创建一个显示屏,并将其填充为零(黑色),
- 我们将蛇所占据的单元格涂成绿色。
- 我们把被苹果占据的单元格涂成红色。
- 如果键盘上有一个键被按下,返回该键的id。
def display():
# Create a blank image
board = np.zeros([BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE, 3])
# Color the snake green
for part in snake:
x = part.x * CELL_SIZE
y = part.y * CELL_SIZE
board[y:y + CELL_SIZE, x:x + CELL_SIZE] = [0, 255, 0]
# Color the apple red
x = applex * CELL_SIZE
y = appley * CELL_SIZE
board[y:y + CELL_SIZE, x:x + CELL_SIZE] = [0, 0, 255]
# Display board
cv2.imshow("Snake Game", np.uint8(board))
key = cv2.waitKey(int(1000/SPEED))
# Return the key pressed. It is -1 if no key is pressed.
return key
我们几乎准备好进入游戏的主要while循环了。在此之前,我们先打印一些说明。
# Start the game by printing instructions
print('w = top, a = left, s = down, d = right')
# Ugly trick to bring the window in focus
win_focus()
您将注意到一个函数win_focus()
。取决于OpenCV在你的系统上使用的窗口方法,当你运行这个游戏时,显示窗口可能不在焦点中。为了解决这一问题,我们使用了这种丑陋的方法来聚焦游戏窗口,这样用户就不必点击窗口。
在下面的代码中,我们执行以下操作
- 我们打开一个窗口
- 显示游戏窗口
- 将窗口设置为全屏。这使窗口成为焦点。
- 我们将窗口调整回正常大小。
def win_focus():
# Ugly trick to get the window in focus.
# Opens an image in fullscreen and then back to normal window
cv2.namedWindow("Snake Game", cv2.WINDOW_AUTOSIZE);
board = np.zeros([BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE, 3])
cv2.imshow("Snake Game", board);
cv2.setWindowProperty("Snake Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN);
cv2.waitKey(2000)
cv2.setWindowProperty("Snake Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_AUTOSIZE)
如果遇到任何故障,可以在主函数中注释掉win_focus()
。
对于代码的主要部分,我们将使用一个while循环,其中每次迭代都是蛇的一次移动。在下面的循环中,我们完成两件事:
- 检查苹果是否被吃了。
- 如果是,生成一个新的苹果。在生成苹果的时候,我们确保它的位置不与蛇重叠。
while True:
# Checks if the apple is eaten and generates a new one
if eaten:
# Create a list of all possible locations
s = list(range(0, BOARD_SIZE ** 2))
# Delete cells that are part of the snake
for part in snake:
s.remove(part.x * BOARD_SIZE + part.y)
# Randomly pick from one of the remaining cells
a = choice(s)
applex = int(a/BOARD_SIZE)
appley = a % BOARD_SIZE
eaten = False
接下来,我们显示显示屏,并查找键盘输入。
if/elif
语句检查是否按下了‘w’、‘a’、’、‘d’、escape 或 delete 键
。根据键盘输入,蛇改变方向或停止程序。
# Makes and displays the board
key = display()
# Gets key presses and moves accordingly
# 8 and 27 are delete and escape keys
# Arrow keys are tricky in OpenCV. So we use
# keys 'w', 'a','s','d' for movement.
# w = top, a = left, s = down, d = right
if key == 8 or key == 27:
break
elif key == ord("d") :
head.direction = 0
elif key == ord("s") :
head.direction = 1
elif key == ord("a") :
head.direction = 2
elif key == ord("w") :
head.direction = 3
根据键盘输入,我们移动蛇。您可能还记得,SnakePart
的move
方法只是在它前面复制蛇部分的位置。
# Moving the snake
for part in snake[::-1]:
part.move()
接下来,我们实现碰撞检查。下面的代码实现了两个规则:
- 如果蛇的头移出显示屏,游戏就结束了。
- 如果蛇的头部与蛇的身体发生碰撞,游戏就结束了。
# Collision rules
if head.x < 0 or head.x > BOARD_SIZE - 1 or head.y < 0 or head.y > BOARD_SIZE - 1:
break
for part in snake[1:]:
if head.x == part.x and head.y == part.y:
quit = True
break
if quit:
break
最后,我们制定了蛇吃苹果生长的规则。当苹果被吃掉的时候,生长变量的增量GROWTH是不变的,蛇每帧生长一个单元格,直到它完全长大。
# The snake grows gradually over multiple frames
if grow > 0:
snake.append(SnakePart(snake[-1], subx, suby))
grow -= 1
# Grows the snake when it eats an apple
if applex == head.x and appley == head.y:
subx = snake[-1].x
suby = snake[-1].y
eaten = True
grow += GROWTH
参考目录
https://learnopencv.com/snake-game-with-opencv-python/