最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python利用手勢(shì)識(shí)別實(shí)現(xiàn)貪吃蛇游戲

 更新時(shí)間:2022年04月30日 09:22:28   作者:一馬歸一碼  
想必大家都玩過(guò)貪吃蛇的游戲吧:通過(guò)操縱蛇的移動(dòng)方向能夠讓蛇吃到隨機(jī)出現(xiàn)的食物,吃到的食物越多,蛇就會(huì)變得越長(zhǎng)。本文將使用手勢(shì)識(shí)別來(lái)完成貪吃蛇這個(gè)簡(jiǎn)單的游戲,感興趣的可以了解一下

一、前言

想必大家都玩過(guò)貪吃蛇的游戲吧:通過(guò)操縱蛇的移動(dòng)方向能夠讓蛇吃到隨機(jī)出現(xiàn)的食物,吃到的食物越多,蛇就會(huì)變得越長(zhǎng),但如果不小心撞到了自己,那么蛇就會(huì)死亡,game over!! 我們玩過(guò)的貪吃蛇游戲一般都是在手機(jī)或者游戲機(jī)上進(jìn)行的,通過(guò)方向鍵操縱蛇的移動(dòng),那么我們是否可以直接使用一個(gè)攝像頭捕捉我們的手勢(shì)動(dòng)作,并用手的移動(dòng)來(lái)代表貪吃蛇的移動(dòng)呢?當(dāng)然可以,今天我就和大家一起完成這個(gè)游戲的設(shè)計(jì)并愉快的玩耍。

Let's Start!

二、項(xiàng)目介紹

1、游戲的操作方式

貪吃蛇游戲人盡皆知,計(jì)算機(jī)視覺(jué)鮮為人知,計(jì)算機(jī)視覺(jué)+貪吃蛇游戲會(huì)帶給人們更多的參與感以及新鮮度,本次這個(gè)項(xiàng)目就是主要使用手勢(shì)識(shí)別來(lái)完成貪吃蛇這個(gè)簡(jiǎn)單的游戲。在這個(gè)游戲中,電腦通過(guò)攝像頭捕捉到我們的手勢(shì)并判別是否進(jìn)行移動(dòng),玩家移動(dòng)手去操縱貪吃蛇得到屏幕中隨機(jī)出現(xiàn)的食物,每得到一個(gè)食物,就會(huì)算作一分,Score 就會(huì)加1并顯示在畫(huà)面中,當(dāng)玩家在操作的過(guò)程中不小心使得蛇的頭部和身體相撞,那么就會(huì)顯示GameOver! 按下 ‘r’ 鍵可以重新開(kāi)始游戲。

2、開(kāi)發(fā)的過(guò)程中的注意事項(xiàng)

(1) 圖像的左右問(wèn)題

由于我們是使用手勢(shì)來(lái)進(jìn)行控制蛇的移動(dòng)的,但攝像頭的畫(huà)面顯示的是別人的視角,所以這和玩家的左右意識(shí)剛好是相反的,因此我們要將攝像頭讀取到的畫(huà)面進(jìn)行一個(gè)左右的翻轉(zhuǎn)。原理上說(shuō)就是將左右的像素點(diǎn)位置進(jìn)行一個(gè)調(diào)換,但在 Python 中可以使用一個(gè) cv2.flip( ) 函數(shù)就可以實(shí)現(xiàn)鏡像翻轉(zhuǎn)了。

(2) 攝像頭的畫(huà)面尺寸問(wèn)題

過(guò)攝像頭得到的圖像我們需要在上面進(jìn)行游戲,因此畫(huà)面過(guò)小會(huì)導(dǎo)致游戲空間不足,在最開(kāi)始可以對(duì)畫(huà)面的大小進(jìn)行一個(gè)預(yù)處理,設(shè)定一個(gè)較為合理的大小,最后得到的畫(huà)面玩游戲時(shí)才不會(huì)顯得局促。通過(guò)函數(shù) cap.set(3, m) cap.set(4, n) 可以實(shí)現(xiàn)對(duì)畫(huà)面的寬和高的設(shè)定。

本項(xiàng)目中還會(huì)存在一些其他的注意事項(xiàng),比如判斷碰撞,判斷獲得食物等,我會(huì)在后面的項(xiàng)目過(guò)程中再加以介紹。

三、游戲的實(shí)現(xiàn)要點(diǎn)

1、選擇第三方庫(kù)

一些使用到的第三方庫(kù):

import math
import random
import cvzone
import cv2
import numpy as np
from cvzone.HandTrackingModule import HandDetector

在本次項(xiàng)目中,我們主要使用到以上的幾個(gè)庫(kù),其中使用 random 庫(kù)來(lái)隨機(jī)選擇像素點(diǎn)來(lái)放置食物甜甜圈,使用 cvzone 中的手部識(shí)別來(lái)進(jìn)行玩家手勢(shì)的檢測(cè),使用 cv2 來(lái)進(jìn)行一些基礎(chǔ)的圖像操作,其他的一些庫(kù)也各有用處,后面一一介紹。

2、找到關(guān)鍵點(diǎn)并標(biāo)記

在本次游戲中我們是選擇了一只手作為目標(biāo)節(jié)點(diǎn),所以當(dāng)我們檢測(cè)到畫(huà)面中出現(xiàn)手部時(shí)需要對(duì)其中的關(guān)鍵點(diǎn)進(jìn)行標(biāo)記,而這個(gè)關(guān)鍵點(diǎn)恰好是我們的貪吃蛇的頭部,由于我們是調(diào)用的第三方庫(kù),而該庫(kù)可以對(duì)手部進(jìn)行3D的標(biāo)記,但我們只需要 x,y 兩個(gè)坐標(biāo)值就可以了,主要使用以下函數(shù)進(jìn)行手部關(guān)鍵節(jié)點(diǎn)的標(biāo)記:

#檢測(cè)到第一個(gè)手,并標(biāo)記手部位置
    if hands:
        lmList = hands[0]['lmList']
        pointIndex = lmList[8][0:2] #第八個(gè)坐標(biāo)點(diǎn)的 x, y值,其中 z 值不被包括在里面
        cv2.circle(img, pointIndex, 20, (200, 0, 200), cv2.FILLED) #在關(guān)鍵點(diǎn)處繪制一個(gè)圓點(diǎn)并進(jìn)行填充(此處只是示范,后面會(huì)更改)

3、創(chuàng)建一個(gè)類(lèi)來(lái)保存關(guān)于游戲的所有功能

我們需要實(shí)現(xiàn)的游戲是很多功能結(jié)合起來(lái)完成的,如果想要使用函數(shù)來(lái)實(shí)現(xiàn)這些功能,那么將會(huì)非常麻煩,當(dāng)我們使用 class 來(lái)完成時(shí),由于很多東西都保存在同一個(gè)類(lèi)中,將會(huì)降低難度。在這個(gè) class 中我們將會(huì)創(chuàng)建很多重要的列表來(lái)存儲(chǔ)我們用得到的一些關(guān)鍵點(diǎn),比如貪吃蛇的身上的所有的點(diǎn)、貪吃蛇的長(zhǎng)度、蛇的總體距離、食物的放置、得分等:

class SnakeGameClass:
    def __init__(self, pathFood):
        self.points = []  #貪吃蛇身上所有點(diǎn)
        self.lengths = []  #點(diǎn)與點(diǎn)之間的距離
        self.currentLength = 0  #當(dāng)下蛇的長(zhǎng)度
        self.allowedLength = 50  #最大允許長(zhǎng)度(閾值)
        self.previousHead = 0, 0  #手部關(guān)鍵點(diǎn)之后的第一個(gè)點(diǎn)
 
        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、定義函數(shù)進(jìn)行不斷更新 

隨著我們的手部的移動(dòng),貪吃蛇的長(zhǎng)度以及位置都會(huì)發(fā)生變化,所以我們需要?jiǎng)?chuàng)建一個(gè)函數(shù)來(lái)不斷進(jìn)行更新,滿(mǎn)足變化的需求(該部分也是在前面創(chuàng)建的大類(lèi)里面完成的):

    def update(self, imgMain, currentHead):
        #游戲結(jié)束,打印文本
        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
 
            #長(zhǎng)度縮小
            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
 
            #檢查貪吃蛇是否已經(jīng)觸碰到食物
            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)
 
            #檢測(cè)是否碰撞
            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 = []  #蛇身上所有的點(diǎn)
                self.lengths = []  #不同點(diǎn)之間的距離
                self.currentLength = 0  #當(dāng)前蛇的長(zhǎng)度
                self.allowedLength = 50  #最大允許長(zhǎng)度
                self.previousHead = 0, 0  #先前的蛇的頭部
                self.randomFoodLocation()
 
        return imgMain

在這個(gè)更新的函數(shù)中,我們需要判斷很多東西,比如貪吃蛇是否觸碰到食物(如果觸碰到食物我們就要增加蛇的長(zhǎng)度并累積得分)、當(dāng)前長(zhǎng)度是否超過(guò)所允許的最大長(zhǎng)度(當(dāng)前長(zhǎng)度小于最大長(zhǎng)度就不必要進(jìn)行更改了,但如果當(dāng)前長(zhǎng)度大于最大長(zhǎng)度,則需要進(jìn)行縮短)、貪吃蛇是否發(fā)生碰撞(通過(guò)關(guān)鍵節(jié)點(diǎn)之間的距離判斷貪吃蛇是否發(fā)生了碰撞,如果發(fā)生了碰撞,則進(jìn)入 gameover 模塊,如果沒(méi)有,繼續(xù)游戲)等,都解釋在上面的代碼中了。

主要是通過(guò)上面定義的 class 我們就能實(shí)現(xiàn)當(dāng)前的貪吃蛇游戲了。

四、總體代碼

本次小游戲我是在b站看到教程并一步步復(fù)現(xiàn)出來(lái)的,大家感興趣可以嘗試一下,當(dāng)然按照慣例整體代碼會(huì)貼在下面:

"""
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)
 
#設(shè)置畫(huà)面的尺寸大小,過(guò)小的話(huà)導(dǎo)致貪吃蛇活動(dòng)不開(kāi)
cap.set(3, 1280)
cap.set(4, 720)
 
detector = HandDetector(detectionCon=0.8, maxHands=1)
 
 
class SnakeGameClass:
    def __init__(self, pathFood):
        self.points = []  #貪吃蛇身上所有點(diǎn)
        self.lengths = []  #每一個(gè)點(diǎn)之間的距離
        self.currentLength = 0  #當(dāng)下蛇的長(zhǎng)度
        self.allowedLength = 50  #最大允許長(zhǎng)度(閾值)
        self.previousHead = 0, 0  #手部關(guān)鍵點(diǎn)之后的第一個(gè)點(diǎn)
 
        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):
        #游戲結(jié)束,打印文本
        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
 
            #長(zhǎng)度縮小
            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
 
            #檢查貪吃蛇是否已經(jīng)觸碰到食物
            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)
 
            #檢測(cè)是否碰撞
            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 = []  #蛇身上所有的點(diǎn)
                self.lengths = []  #不同點(diǎn)之間的距離
                self.currentLength = 0  #當(dāng)前蛇的長(zhǎng)度
                self.allowedLength = 50  #最大允許長(zhǎng)度
                self.previousHead = 0, 0  #先前的蛇的頭部
                self.randomFoodLocation()
 
        return imgMain
 
 
game = SnakeGameClass("Donut.png")
 
while True:
    success, img = cap.read()
    img = cv2.flip(img, 1) #鏡像翻轉(zhuǎn)
    hands, img = detector.findHands(img, flipType=False)
    #檢測(cè)到第一個(gè)手,并標(biāo)記手部位置
    if hands:
        lmList = hands[0]['lmList']
        pointIndex = lmList[8][0:2] #第八個(gè)坐標(biāo)點(diǎn)的 x, y值,其中 z 值不被包括在里面
        #cv2.circle(img, pointIndex, 20, (200, 0, 200), cv2.FILLED) #在關(guān)鍵點(diǎn)處繪制一個(gè)圓點(diǎn)并進(jìn)行填充(此處只是示范,后面會(huì)更改)
        img = game.update(img, pointIndex)
 
    cv2.imshow("Image", img)
    key = cv2.waitKey(1)
    #按下‘r'重新開(kāi)始游戲
    if key == ord('r'):
        game.gameOver = False

至于需要使用到的甜甜圈的圖案,可以網(wǎng)上找一個(gè)合適的大小的圖案進(jìn)行替代即可。

到此這篇關(guān)于Python利用手勢(shì)識(shí)別實(shí)現(xiàn)貪吃蛇游戲的文章就介紹到這了,更多相關(guān)Python貪吃蛇游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 跟老齊學(xué)Python之再深點(diǎn),更懂list

    跟老齊學(xué)Python之再深點(diǎn),更懂list

    對(duì)于list,由于她的確非常非常龐雜,在python中應(yīng)用非常廣泛,所以,雖然已經(jīng)介紹完畢了基礎(chǔ)內(nèi)容,這里還要用一講深入一點(diǎn)點(diǎn),往往越深入越...
    2014-09-09
  • Django使用模板后無(wú)法找到靜態(tài)資源文件問(wèn)題解決

    Django使用模板后無(wú)法找到靜態(tài)資源文件問(wèn)題解決

    這篇文章主要介紹了Django使用模板后無(wú)法找到靜態(tài)資源文件問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Pytorch在NLP中的簡(jiǎn)單應(yīng)用詳解

    Pytorch在NLP中的簡(jiǎn)單應(yīng)用詳解

    今天小編就為大家分享一篇Pytorch在NLP中的簡(jiǎn)單應(yīng)用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-01-01
  • python super的使用方法及實(shí)例詳解

    python super的使用方法及實(shí)例詳解

    這篇文章主要介紹了python super的使用方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 使用Python實(shí)現(xiàn)MongoDB數(shù)據(jù)轉(zhuǎn)表格文件CSV

    使用Python實(shí)現(xiàn)MongoDB數(shù)據(jù)轉(zhuǎn)表格文件CSV

    這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)將MongoDB中的數(shù)據(jù)轉(zhuǎn)換為表格文件(如CSV)以便于數(shù)據(jù)交換、共享或?qū)氲狡渌到y(tǒng)進(jìn)行分析,需要的可以參考下
    2024-04-04
  • Python操作word文檔的示例詳解

    Python操作word文檔的示例詳解

    本文為大家介紹了Python操作docx文檔相關(guān)知識(shí)點(diǎn)。主要涉及的內(nèi)容為python-docx?,一款可以操作Word文檔(僅支持docx)的第三方庫(kù)??旄S小編一起學(xué)習(xí)一下吧
    2022-01-01
  • python?包中的sched?事件調(diào)度器的操作方法

    python?包中的sched?事件調(diào)度器的操作方法

    sched模塊內(nèi)容很簡(jiǎn)單,只定義了一個(gè)類(lèi)。它用來(lái)最為一個(gè)通用的事件調(diào)度模塊,接下來(lái)通過(guò)本文給大家介紹python?包之?sched?事件調(diào)度器教程,需要的朋友可以參考下
    2022-04-04
  • python中使用np.delete()的實(shí)例方法

    python中使用np.delete()的實(shí)例方法

    在本篇文章里小編給大家整理的是一篇關(guān)于python中使用np.delete()的實(shí)例方法,對(duì)此有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-02-02
  • python爬蟲(chóng)調(diào)度器用法及實(shí)例代碼

    python爬蟲(chóng)調(diào)度器用法及實(shí)例代碼

    在本篇文章里小編給各位整理了關(guān)于python爬蟲(chóng)調(diào)度器用法及實(shí)例代碼,需要的朋友們可以參考學(xué)習(xí)下。
    2020-11-11
  • python字典取值的幾種方法總結(jié)

    python字典取值的幾種方法總結(jié)

    這篇文章主要介紹了python字典取值的幾種方法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評(píng)論

元阳县| 德惠市| 万山特区| 崇州市| 五家渠市| 龙江县| 晋城| 乌审旗| 巴林左旗| 景宁| 阳原县| 久治县| 靖州| 怀化市| 阿拉善右旗| 淮安市| 绩溪县| 丹阳市| 罗城| 闽侯县| 萨嘎县| 瑞安市| 克拉玛依市| 西贡区| 讷河市| 汶川县| 新安县| 茌平县| 巴林右旗| 茶陵县| 廊坊市| 龙门县| 格尔木市| 安庆市| 灵石县| 屏山县| 斗六市| 奉贤区| 揭东县| 三亚市| 磴口县|