스네이크 게임은 누구나 알고 있지만 컴퓨터 비전은 잘 알려지지 않았습니다. 컴퓨터 비전 + 스네이크 게임은 사람들의 참여도와 신선도를 높여줄 것입니다. 간단한 뱀 게임을 완성해보세요. 이 게임에서는 컴퓨터가 카메라를 통해 우리의 동작을 포착하고 움직일지 여부를 결정합니다. 플레이어는 손을 움직여 뱀을 제어하여 화면에 무작위로 나타나는 음식을 얻을 때마다 음식을 1개로 계산합니다. 점수는 1이 추가되어 화면에 표시됩니다. 플레이어가 작동 중에 실수로 뱀의 머리와 몸에 충돌하면 게임오버(GameOver) 버튼을 눌러 게임을 다시 시작하세요.
(1) 이미지의 좌우 문제
뱀의 움직임을 제어하기 위해 제스처를 사용하지만 카메라 사진은 타인의 관점을 보여주기 때문입니다. , 그래서 이것은 플레이어의 왼쪽 및 오른쪽 인식과 정확히 반대이므로 카메라가 읽은 이미지를 왼쪽 및 오른쪽으로 뒤집어야 합니다. 원칙적으로 왼쪽과 오른쪽 픽셀의 위치는 교환되지만 Python에서는 cv2.flip() 함수를 사용하여 거울 뒤집기를 수행할 수 있습니다.
(2) 카메라 화면 크기 문제
카메라를 통해 얻은 이미지로 게임을 해야 하기 때문에 화면이 너무 작으면 처음에는 게임 공간이 부족해집니다. 화면 크기를 좀 더 적당하게 설정해 놓으면 게임을 할 때 최종 화면이 좁아 보이지 않습니다. 화면의 너비와 높이는 cap.set(3, m) cap.set(4, n) 함수를 통해 설정할 수 있습니다.
이 프로젝트에는 충돌 판단, 식량 획득 판단 등 다른 예방 조치도 있을 예정입니다. 이에 대해서는 나중에 프로젝트 과정에서 소개하겠습니다.
3. 게임 구현의 핵심 포인트
import math import random import cvzone import cv2 import numpy as np from cvzone.HandTrackingModule import HandDetector
이 프로젝트에서는 주로 위의 라이브러리를 사용하며 그중 무작위 라이브러리가 사용됩니다. 음식 도넛을 배치하기 위해 픽셀을 선택하고, cvzone에서 손 인식을 사용하여 플레이어 제스처를 감지하고, cv2를 사용하여 일부 기본 이미지 작업을 수행하며, 다른 라이브러리에도 자체 용도가 있습니다. 이에 대해서는 나중에 하나씩 소개하겠습니다.
2 핵심 포인트를 찾아 표시하세요
#检测到第一个手,并标记手部位置 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. 게임의 모든 기능을 저장하는 클래스를 만듭니다.
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. 지속적으로 업데이트하는 기능 정의
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
이 업데이트된 함수에서는 욕심 많은 뱀이 음식에 닿았는지 여부(음식에 닿으면 뱀의 길이를 늘려 점수를 축적해야 함), 현재 길이가 허용된 최대 길이를 초과하는지 여부(최대 길이보다 작으면 현재 길이를 변경할 필요가 없지만, 현재 길이가 최대 길이보다 크면 줄여야 함), 뱀이 있는지 여부 충돌(키 노드 사이의 거리에 따라 뱀이 충돌했는지 여부가 결정됩니다. 충돌이 발생하면 게임 오버 모듈로 들어가고 충돌이 없으면 게임을 계속합니다) 등은 위 코드에서 모두 설명됩니다.
주로 위에서 정의한 클래스를 통해서 현재의 Snake 게임을 구현할 수 있습니다.
4. 전체 코드
""" 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
필요한 사항은 사용된 도넛 패턴은 온라인에서 적당한 사이즈 패턴을 찾아 교체하시면 됩니다.
위 내용은 Python이 제스처 인식을 사용하여 Snake 게임을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!