Home > Article > Backend Development > How Python uses gesture recognition to implement the Snake game
Everyone knows about the Snake game, but little is known about computer vision. The computer vision Snake game will bring people more participation and freshness. This project mainly uses gesture recognition to complete the simple Snake game. In this game, the computer captures our gestures through the camera and determines whether to move. The player moves his hand to control the snake to get food that appears randomly on the screen. Each time a food is obtained, it will be counted as one point, and the Score will be Add 1 and display it on the screen. When the player accidentally collides with the snake's head and body during operation, GameOver! will be displayed. Press the ‘r’ button to restart the game.
(1) The left and right issue of the image
Because we use gestures to do it It controls the movement of the snake, but the camera picture shows someone else's perspective, so this is exactly opposite to the player's left and right awareness, so we need to flip the picture read by the camera left and right. In principle, the position of the left and right pixels is exchanged, but in Python you can use a cv2.flip() function to achieve mirror flipping.
(2) The problem of camera screen size
We need to play games on the images obtained through the camera, so if the screen is too small, it will lead to insufficient game space. At the beginning, you can Perform a pre-processing on the size of the screen and set a more reasonable size, so that the final screen will not look cramped when playing games. The width and height of the screen can be set through the functions cap.set(3, m) cap.set(4, n).
There will also be some other matters needing attention in this project, such as determining collision, determining obtaining food, etc. I will introduce them later in the project process.
3. Game implementation points
import math import random import cvzone import cv2 import numpy as np from cvzone.HandTrackingModule import HandDetector
In this project , we mainly use the above libraries, among which we use the random library to randomly select pixels to place food donuts, use hand recognition in cvzone to detect player gestures, and use cv2 to perform some basic image operations. , some other libraries also have their own uses, which will be introduced one by one later.
2. Find the key points and mark them
#检测到第一个手,并标记手部位置 if hands: lmList = hands[0]['lmList'] pointIndex = lmList[8][0:2] #第八个坐标点的 x, y值,其中 z 值不被包括在里面 cv2.circle(img, pointIndex, 20, (200, 0, 200), cv2.FILLED) #在关键点处绘制一个圆点并进行填充(此处只是示范,后面会更改)
3. Create a class to save all the functions of the game
class SnakeGameClass: def __init__(self, pathFood): self.points = [] #贪吃蛇身上所有点 self.lengths = [] #点与点之间的距离 self.currentLength = 0 #当下蛇的长度 self.allowedLength = 50 #最大允许长度(阈值) 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
4. Define a function to continuously update
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.previousHead cx, cy = currentHead self.points.append([cx, cy]) distance = math.hypot(cx - px, cy - py) self.lengths.append(distance) self.currentLength += distance self.previousHead = cx, cy #长度缩小 if self.currentLength > self.allowedLength: for i, length in enumerate(self.lengths): self.currentLength -= length self.lengths.pop(i) self.points.pop(i) if self.currentLength < self.allowedLength: break #检查贪吃蛇是否已经触碰到食物 rx, ry = self.foodPoint if rx - self.wFood // 2 < cx < rx + self.wFood // 2 and \ ry - self.hFood // 2 < cy < ry + self.hFood // 2: self.randomFoodLocation() self.allowedLength += 50 self.score += 1 print(self.score) #使用线条绘制贪吃蛇 if 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) #显示食物 imgMain = 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) #检测是否碰撞 pts = 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 = True self.points = [] #蛇身上所有的点 self.lengths = [] #不同点之间的距离 self.currentLength = 0 #当前蛇的长度 self.allowedLength = 50 #最大允许长度 self.previousHead = 0, 0 #先前的蛇的头部 self.randomFoodLocation() return imgMain
In this updated function, we need to determine many things, such as whether the greedy snake touches the food (If we touch the food, we will increase the length of the snake and accumulate points), whether the current length exceeds the maximum length allowed (if the current length is less than the maximum length, there is no need to make changes, but if the current length is greater than the maximum length, it is necessary shorten), whether the snake collides (determine whether the snake collides by the distance between key nodes, if a collision occurs, enter the gameover module, if not, continue the game), etc., are all explained above The code is broken.
Mainly through the class defined above, we can realize the current snake game.
4. Overall code
""" Author:XiaoMa CSDN Address:一马归一码 """ import math import random import cvzone import cv2 import numpy as np from cvzone.HandTrackingModule import HandDetector cap = 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 = 50 #最大允许长度(阈值) 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 def 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.previousHead cx, cy = currentHead self.points.append([cx, cy]) distance = math.hypot(cx - px, cy - py) self.lengths.append(distance) self.currentLength += distance self.previousHead = cx, cy #长度缩小 if self.currentLength > self.allowedLength: for i, length in enumerate(self.lengths): self.currentLength -= length self.lengths.pop(i) self.points.pop(i) if self.currentLength < self.allowedLength: break #检查贪吃蛇是否已经触碰到食物 rx, ry = self.foodPoint if rx - self.wFood // 2 < cx < rx + self.wFood // 2 and \ ry - self.hFood // 2 < cy < ry + self.hFood // 2: self.randomFoodLocation() self.allowedLength += 50 self.score += 1 print(self.score) #使用线条绘制贪吃蛇 if 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) #显示食物 imgMain = 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) #检测是否碰撞 pts = 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 = True self.points = [] #蛇身上所有的点 self.lengths = [] #不同点之间的距离 self.currentLength = 0 #当前蛇的长度 self.allowedLength = 50 #最大允许长度 self.previousHead = 0, 0 #先前的蛇的头部 self.randomFoodLocation() return imgMain game = 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] #第八个坐标点的 x, y值,其中 z 值不被包括在里面 #cv2.circle(img, pointIndex, 20, (200, 0, 200), cv2.FILLED) #在关键点处绘制一个圆点并进行填充(此处只是示范,后面会更改) img = game.update(img, pointIndex) cv2.imshow("Image", img) key = cv2.waitKey(1) #按下‘r'重新开始游戏 if key == ord('r'): game.gameOver = False
As for the donut pattern you need to use, you can find a suitable size pattern online to replace it.
The above is the detailed content of How Python uses gesture recognition to implement the Snake game. For more information, please follow other related articles on the PHP Chinese website!