目录
0、项目介绍
1、效果展示
2、项目搭建
3、项目代码展示与讲解
4、项目资源
5、项目总结
0、项目介绍
这次是一个有意思的计算机视觉游戏——贪吃蛇,我们以食指为蛇头,不断的移动我们的手指,当吃到甜甜圈的时候,蛇身增长,当食指停止或触碰蛇身时,游戏结束。点击'r'将重新开始游戏。
1、效果展示
制作成GIF图片的效果有点卡,实际的效果还是很好的。
2、项目搭建
————17 Gluttonous snake
——Donut.png
——snake.py
3、项目代码展示与讲解
#snake.pyimport math
import random
import cvzone
import cv2
import numpy as np
from cvzone.HandTrackingModule import HandDetectorcap = cv2.VideoCapture(0)
cap.set(3, 1280)
cap.set(4, 720)detector = HandDetector(detectionCon=0.8, maxHands=1)class SnakeGameClass:def __init__(self, pathFood):self.points = [] # 蛇的所有点self.lengths = [] # 每个点之间的距离self.currentLength = 0 # 蛇的总长度self.allowedLength = 150 # 总共允许长度self.previousHead = 0, 0 # 起点self.imgFood = cv2.imread(pathFood, cv2.IMREAD_UNCHANGED) # 读取甜甜圈的位置路径self.hFood, self.wFood, _ = self.imgFood.shape # 得到甜甜圈图像的长、宽self.foodPoint = 0, 0 # 初始化甜甜圈的位置self.randomFoodLocation() # 随机分布甜甜圈的位置 self.score = 0 # 吃得甜甜圈的分数self.gameOver = False # gameoverdef randomFoodLocation(self):self.foodPoint = random.randint(100, 1000), random.randint(100, 600)def update(self, imgMain, currentHead):if self.gameOver:cvzone.putTextRect(imgMain, "Game Over", [300, 400],scale=7, thickness=5, offset=20)cvzone.putTextRect(imgMain, f'Your Score: {self.score}', [300, 550],scale=7, thickness=5, offset=20)else:px, py = self.previousHeadcx, cy = currentHeadself.points.append([cx, cy])distance = math.hypot(cx - px, cy - py)self.lengths.append(distance)self.currentLength += distanceself.previousHead = cx, cy# Length Reductionif self.currentLength > self.allowedLength:for i, length in enumerate(self.lengths):self.currentLength -= lengthself.lengths.pop(i)self.points.pop(i)if self.currentLength < self.allowedLength:break# Check if snake ate the Foodrx, ry = self.foodPointif rx - self.wFood // 2 < cx < rx + self.wFood // 2 and \ry - self.hFood // 2 < cy < ry + self.hFood // 2:self.randomFoodLocation()self.allowedLength += 50self.score += 1print(self.score)# Draw Snakeif self.points:for i, point in enumerate(self.points):if i != 0:cv2.line(imgMain, self.points[i - 1], self.points[i], (0, 0, 255), 20)cv2.circle(imgMain, self.points[-1], 20, (0, 255, 0), cv2.FILLED)# Add FoodimgMain = cvzone.overlayPNG(imgMain, self.imgFood,(rx - self.wFood // 2, ry - self.hFood // 2))cvzone.putTextRect(imgMain, f'Score: {self.score}', [50, 80],scale=3, thickness=3, offset=10)# Check for Collisionpts = np.array(self.points[:-2], np.int32)pts = pts.reshape((-1, 1, 2))cv2.polylines(imgMain, [pts], False, (0, 255, 0), 3)minDist = cv2.pointPolygonTest(pts, (cx, cy), True)if -1 <= minDist <= 1:print("Hit")self.gameOver = Trueself.points = [] # all points of the snakeself.lengths = [] # distance between each pointself.currentLength = 0 # total length of the snakeself.allowedLength = 150 # total allowed Lengthself.previousHead = 0, 0 # previous head pointself.randomFoodLocation()return imgMaingame = SnakeGameClass("Donut.png")while True:success, img = cap.read()img = cv2.flip(img, 1)hands, img = detector.findHands(img, flipType=False)if hands:lmList = hands[0]['lmList']pointIndex = lmList[8][0:2]img = game.update(img, pointIndex)cv2.imshow("Image", img)key = cv2.waitKey(1)if key == ord('r'):game.gameOver = Falseelif key==27:break
首先,打开摄像头的窗口得比较的大,不仅为了方便测试,而且窗口小了也会影响我们的手势识别,然后建立SnakeGameClass类,其下的变量名用途请查看注释。
其中,先看while True里面,cv2.flip函数是用窗口画面翻转的,输入一个正值,便会绕y轴翻转,这样能够保证窗口画面中的动作与我们的想法相同,接下来获得我们的手指landmark,即是获得目前蛇的头部landmark,之后完成我们的游戏更新部分。
其次,来看randomFoodLocation()函数,可以用于随机生成甜甜圈所在位置,我们为边缘留了些位置,避免手势识别失误和容易出界的问题。此时它并不具备更新位置的能力(只是生成了一个随机数位置),我们用update函数来完成主要的功能。我们想要实现的一个效果是游戏结束时,我需要得到一个提示,你不能再继续下去了,就如上面的GIF图片中,当我手势被识别时,会有一个提示(用putTextRcet完成),否则就会进行正常的游戏操作。
正常的游戏操作,我希望有这几个方面,甜甜圈覆盖窗口画面实现更新、蛇头与蛇身相撞就会gameover、手势一旦识别必须不停移动、吃掉一个甜甜圈增长一节蛇身等。
- Length Reduction:
一旦蛇的总长度超过了我们允许的长度,我们就需要对其进行缩减,这里使用了enumerate函数对列表进行了迭代,返回了一个索引和值,方便减去对应的距离。
- Check if snake ate the Food:
只要蛇头进入了甜甜圈的范围内,即可认为是吃到了甜甜圈,接下来就可以完成食物位置的更新,蛇身的加长,score得分。
- Draw Snake:
画蛇的初始态。
- Add Food:
在窗口画面中覆盖上甜甜圈,可以使用cvzone.overlayPNG函数。
- Check for Collision:
[pts]为多边形曲线阵列,在这里我们将会绘制一个多边形,并用cv2.pointPolygonTest来返回除最后两个点外与蛇头最近的距离,因为有tickness的原因,所以我们无需纠结于minDist一定小于等于0。
4、项目资源
GitHub:17 Gluttonous snake
5、项目总结
最近本来在准备期末考试的,所以很久没写博客了,但由于疫情原因提前返乡回家了,这学期的考试要得等到明年啦。想要添加按钮、声音、毒药这些的,没有精力搞了,有兴趣的大家可以试试。