Comment Python utilise la reconnaissance gestuelle pour implémenter le jeu Snake

WBOY
Libérer: 2023-05-11 20:19:04
avant
1379 Les gens l'ont consulté

    Introduction au projet

    1. Comment faire fonctionner le jeu

    Tout le monde connaît le jeu Snake, mais la vision par ordinateur peu connue + le jeu Snake apportera aux gens plus de participation et de fraîcheur. Ce projet utilise principalement la reconnaissance gestuelle pour. terminez le jeu simple de Snake. Dans ce jeu, l'ordinateur capture nos gestes à travers la caméra et détermine s'il doit bouger. Le joueur bouge sa main pour contrôler le serpent afin d'obtenir de la nourriture qui apparaît aléatoirement sur l'écran. Chaque fois qu'un aliment est obtenu, il compte pour un. point, et le score sera Add 1 et l'affichera à l'écran. Lorsque le joueur entre accidentellement en collision avec la tête et le corps du serpent pendant le fonctionnement, GameOver!

    Comment Python utilise la reconnaissance gestuelle pour implémenter le jeu Snake

    2. Choses à noter pendant le processus de développement

    (1) Le problème gauche et droite de l'image

    Parce que nous utilisons des gestes pour contrôler le mouvement du serpent, mais l'image de la caméra montre le point de vue des autres. , donc c'est exactement l'opposé de la conscience gauche et droite du joueur, nous devons donc retourner l'image lue par la caméra vers la gauche et la droite. En principe, la position des pixels gauche et droit est échangée, mais en Python, vous pouvez utiliser une fonction cv2.flip() pour réaliser un retournement de miroir.

    (2) Le problème de la taille de l'écran de la caméra
    Nous devons jouer à des jeux sur les images obtenues via la caméra. Par conséquent, si l'écran est trop petit, cela entraînera un espace de jeu insuffisant au début, nous pouvons prétraiter le. taille de l'écran et définissez-la sur une taille plus raisonnable, et l'image finale n'aura pas l'air à l'étroit lorsque vous jouerez à des jeux. La largeur et la hauteur de l'écran peuvent être définies via les fonctions cap.set(3, m) cap.set(4, n).

    Il y aura également d'autres précautions dans ce projet, telles que la détermination des collisions, la détermination de l'acquisition de nourriture, etc. Je les présenterai plus tard dans le processus du projet.

    3. Points clés de la mise en œuvre du jeu

    1. Sélectionnez les bibliothèques tierces

    Quelques bibliothèques tierces utilisées :

    import math
    import random
    import cvzone
    import cv2
    import numpy as np
    from cvzone.HandTrackingModule import HandDetector
    Copier après la connexion

    Dans ce projet, nous utilisons principalement les bibliothèques ci-dessus, parmi lesquelles la bibliothèque aléatoire est utilisée. sélectionnez des pixels pour placer des beignets de nourriture, utilisez la reconnaissance des mains dans cvzone pour détecter les gestes des joueurs, utilisez cv2 pour effectuer certaines opérations de base sur les images, et d'autres bibliothèques ont également leurs propres utilisations, qui seront présentées une par une plus tard.

    2. Trouvez les points clés et marquez-les

    Dans ce jeu, nous avons choisi une main comme nœud cible, donc lorsque nous détectons une main apparaissant à l'écran, nous devons marquer les points clés, et cette clé Le point arrive pour être la tête de notre serpent gourmand. Puisque nous appelons une bibliothèque tierce et que cette bibliothèque peut marquer la main en 3D, nous n'avons besoin que des deux valeurs de coordonnées de x et y. Nous utilisons principalement ce qui suit. Fonction pour marquer les nœuds clés de la main :

    #检测到第一个手,并标记手部位置
        if hands:
            lmList = hands[0]['lmList']
            pointIndex = lmList[8][0:2] #第八个坐标点的 x, y值,其中 z 值不被包括在里面
            cv2.circle(img, pointIndex, 20, (200, 0, 200), cv2.FILLED) #在关键点处绘制一个圆点并进行填充(此处只是示范,后面会更改)
    Copier après la connexion

    3. Créez une classe pour sauvegarder toutes les fonctions du jeu

    Le jeu que nous devons implémenter est une combinaison de nombreuses fonctions Si vous souhaitez utiliser des fonctions pour les implémenter. fonctions, alors ce sera très gênant lorsque nous utiliserons des classes pour le compléter, puisque beaucoup de choses sont enregistrées dans la même classe, la difficulté sera réduite. Dans ce cours, nous créerons de nombreuses listes importantes pour stocker certains des points clés que nous utilisons, tels que tous les points du corps du serpent, la longueur du serpent, la distance totale du serpent, le placement de la nourriture, les scores, etc. :

    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
    Copier après la connexion

    4. Définir une fonction pour mettre à jour en permanence

    À mesure que nos mains bougent, la longueur et la position du serpent changeront, nous devons donc créer une fonction pour mettre à jour en permanence pour répondre aux besoins changeants (la partie est également effectuée dans la grande classe créée plus tôt) :

        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&#39;Score: {self.score}&#39;, [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
    Copier après la connexion

    Dans cette fonction mise à jour, nous devons juger beaucoup de choses, comme par exemple si le serpent gourmand touche la nourriture (s'il touche la nourriture, nous devons augmenter la longueur du serpent et accumuler des scores), si la longueur actuelle dépasse la longueur maximale autorisée (il n'est pas nécessaire de modifier la longueur actuelle si elle est inférieure à la longueur maximale, mais si la longueur actuelle est supérieure à la longueur maximale, elle doit être raccourcie), si le serpent collisions (par La distance entre les nœuds clés détermine si le serpent est entré en collision. Si une collision se produit, entrez dans le module gameover. Sinon, continuez le jeu), etc., sont tous expliqués dans le code ci-dessus.

    Principalement grâce à la classe définie ci-dessus, on peut réaliser le jeu du serpent actuel.

    4. Code global

    J'ai vu le tutoriel sur la station b pour ce mini jeu et je l'ai reproduit étape par étape. Si vous êtes intéressé, vous pouvez l'essayer. Bien sûr, comme d'habitude, le code global sera posté ci-dessous :

    """
    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&#39;Your Score: {self.score}&#39;, [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&#39;Score: {self.score}&#39;, [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
    Copier après la connexion

    Quant aux besoins Le patron donut utilisé peut être remplacé par un patron de taille adaptée en ligne.

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Étiquettes associées:
    source:yisu.com
    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
    Tutoriels populaires
    Plus>
    Derniers téléchargements
    Plus>
    effets Web
    Code source du site Web
    Matériel du site Web
    Modèle frontal
    À propos de nous Clause de non-responsabilité Sitemap
    Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!