使用Opencv和Python构建贪吃蛇小游戏

基础工作
导入基础程序库
首先需要导入我们所需要的各种程序库。
import cv2
import numpy as np
form random import randint
form random import choice
定义小蛇的基础构成
在这个程序中,我们的小蛇由头部和身体组成。

头部:使用Head类来表示蛇头,即蛇头是整条蛇的最开始部分。身体:对于一条小蛇,除了蛇头外,都是身体。

具体如下图所示。
接下来,我们分别定义小蛇的头部类和身体类。
头部类
【使用Opencv和Python构建贪吃蛇小游戏】因为整个小蛇是二维平面的小蛇,因此,确定一个蛇头的位置需要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

身体类
身体类也需要使用x和y来确定位置,但由于每个身体但小块的运动均依赖于前一个小块的运动,因此,身体类没有direction变量,有的只是front变量,用来让身体知道自己的上一块的位置。具体代码如下。
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

定义游戏变量
CELL_SIZE:每个单元格的宽度。BOARD_SIZE:每一行有多少个单元格。SPEED:小蛇运动的速度。GROWTH:小蛇每吃掉一个苹果后,苹果出现的速度。等等。。。

# 每个单元格的宽度
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)
编写游戏
初始化opencv窗口
除了初始化窗口外,这个函数还可以让用户的焦点自动到该贪吃蛇游戏中。
构建窗口并获取焦点 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)
展现游戏
这个函数需要完成的功能如下。
创建一个用于给小蛇活动的黑色背景板。把蛇的身体(包括头部)设置为绿色。把苹果设置成红色。监听键盘按键信息。

具体代码如下。
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
游戏主循环
在游戏主循环中,主要完成两件事
检查地图中的苹果是否被吃了。如果被吃,需要增长小蛇长度,并重新生成一个新的不被小蛇覆盖的苹果。

判断苹果是否被吃 if eaten:
# 将二维地图转换为一维序列
s = list(range(0, BOARD_SIZE ** 2))
# 将序列中被小蛇占有的值给剔除
for part in snake:
s.remove(part.x * BOARD_SIZE + part.y)

# 随机生成一个不被小蛇覆盖的新苹果
a = choice(s)
applex = int(a/BOARD_SIZE)
appley = a % BOARD_SIZE
eaten = False
刷新屏幕并更新移动方向
刷新屏幕 key = display()
根据按键的信息来控制小蛇的运动方向 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
移动小蛇 从后往前移动,因为后一个小块使用的是前一个小块的位置 for part in snake[::-1]:
part.move()
小蛇吃到苹果后长大
当小蛇吃到苹果后的生长代码 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
游戏失败的判断
碰撞墙壁规则 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

    推荐阅读