SnakeGame (jeu de serpent)

Table des matières

Introduction

2. Présentation du projet

1. Comment utiliser le jeu

2. Questions nécessitant une attention particulière au cours du processus de développement

(1) La gauche et la droite de l'image

(2) La taille de l'écran de la caméra

3. Points clés de la réalisation du jeu

1. Sélectionnez une bibliothèque tierce

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

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

4. Définir les fonctions de mise à jour continue 

Quatrièmement, le code global

5. Conclusion


Introduction

Vraisemblablement, tout le monde a joué au jeu des serpents gourmands : en manipulant la direction de déplacement du serpent, le serpent peut manger de la nourriture au hasard. Plus vous mangez de nourriture, plus le serpent deviendra long, mais si vous vous frappez accidentellement, alors le serpent meurs, game over !!  Les jeux de serpent auxquels nous avons joué se jouent généralement sur des téléphones portables ou des consoles de jeu. Le mouvement du serpent est contrôlé par les touches fléchées, nous pouvons donc directement utiliser une caméra pour capturer nos gestes et utiliser le mouvement de la main pour représenter le serpent Qu'en est-il des mobiles ? Bien sûr, aujourd'hui, je vais terminer la conception de ce jeu avec vous et jouer joyeusement.

Commençons!

2. Présentation du projet

1. Comment utiliser le jeu

Le jeu du serpent est bien connu, mais la vision par ordinateur est peu connue. La vision par ordinateur + le jeu du serpent apporteront aux gens plus de sens de la participation et de la fraîcheur. Ce projet utilise principalement la reconnaissance des gestes pour compléter le jeu du serpent. Jeu simple. Dans ce jeu, l'ordinateur capture nos gestes à travers la caméra et juge s'il faut bouger. Le joueur bouge sa main pour manipuler le serpent afin d'obtenir de la nourriture qui apparaît au hasard sur l'écran. Chaque fois qu'une nourriture est obtenue, elle sera comptée comme une point, et le score sera Add 1 et l'affichera à l'écran. Lorsque le joueur fait accidentellement entrer en collision la tête du serpent avec le corps pendant l'opération, GameOver s'affichera ! Appuyez sur la touche ' r ' pour redémarrer le jeu.

2. Questions nécessitant une attention particulière au cours du processus de développement

(1) La gauche et la droite de l'image

Puisque nous utilisons des gestes pour contrôler le mouvement du serpent, mais que l'image de la caméra montre la perspective de quelqu'un d'autre, c'est donc exactement l'opposé de la conscience gauche et droite du joueur, nous devons donc faire une gauche et une droite de l'image lue par la caméra Flip . En principe, il s'agit d'échanger les positions des pixels gauche et droit, mais en Python, une  fonction cv2.flip( )  peut être utilisée pour réaliser le retournement de l'image miroir.

(2) La taille de l'écran de la caméra

Nous devons jouer à des jeux sur l'image obtenue par la caméra, de sorte que le petit écran entraînera un espace de jeu insuffisant. Au début, nous pouvons prétraiter la taille de l'écran et définir une taille plus raisonnable. L'écran final obtenu lors de la lecture de jeux Pour ne pas paraître à l'étroit. La largeur et la hauteur de l'écran peuvent être définies via la fonction  cap.set(3, m) cap.set(4, n) .

Il y aura d'autres précautions dans ce projet, telles que juger les collisions, juger pour obtenir de la nourriture, etc., que je présenterai plus tard dans le processus du projet.

3. Points clés de la réalisation du jeu

1. Sélectionnez une bibliothèque tierce

Certaines bibliothèques tierces utilisées :

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

Dans ce projet, nous utilisons principalement les bibliothèques ci-dessus, parmi lesquelles la bibliothèque aléatoire est utilisée pour sélectionner au hasard des pixels pour placer des beignets alimentaires, la reconnaissance de la main dans cvzone est utilisée pour détecter les gestes du joueur, et cv2 est utilisé pour certaines opérations d'image de base, et certaines autres bibliothèques sont également utiles, qui seront introduites 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 la présence d'une main à l'écran, nous devons marquer les points clés, et ce point clé se trouve être la tête de notre serpent gourmand, car nous appelons une bibliothèque tierce, et cette bibliothèque peut marquer la main en 3D, mais nous n'avons besoin que de deux valeurs de coordonnées de x et y. Les fonctions suivantes sont principalement utilisées 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) #在关键点处绘制一个圆点并进行填充(此处只是示范,后面会更改)

3. Créez une classe pour contenir 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 implémenter ces fonctions, ce sera très gênant. Lorsque nous utilisons la classe pour terminer, puisque de nombreuses choses sont stockées dans la même classe, il sera Réduire la difficulté. Dans cette classe, nous allons créer de nombreuses listes importantes pour stocker certains points clés que nous utilisons, tels que tous les points sur le serpent, la longueur du serpent, la distance totale du serpent, le placement de la nourriture, le score, 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

4. Définir les fonctions de mise à jour continue 

Au fur et à mesure que nos mains bougent, la longueur et la position du serpent changent, nous devons donc créer une fonction à mettre à jour en permanence pour répondre aux besoins changeants (cette partie est également complétée dans la classe créée précédemment) :

    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

 Dans cette fonction mise à jour, nous devons juger de nombreuses choses, telles que si le serpent gourmand touche la nourriture (s'il touche la nourriture, nous devons augmenter la longueur du serpent et accumuler des points), si la longueur actuelle dépasse la longueur maximale autorisé (si la longueur actuelle est inférieure à la longueur maximale, il n'est pas nécessaire de la modifier, mais si la longueur actuelle est supérieure à la longueur maximale, elle doit être raccourcie), si le serpent entre en collision (en jugeant si le serpent entre en collision par la distance entre les nœuds clés, s'il y a une collision, entrez dans le module gameover, sinon, continuez le jeu), etc., sont expliqués dans le code ci-dessus.

Principalement à travers la classe définie ci-dessus, nous pouvons réaliser le jeu Snake actuel.

Quatrièmement, le code global

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

"""
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

En ce qui concerne le modèle de beignet à utiliser, vous pouvez trouver en ligne un modèle de taille appropriée pour le remplacer.

5. Conclusion

Comparé à la reconnaissance gestuelle, ce jeu semble se concentrer davantage sur la capacité de programmation Python. Avant de commencer à écrire ce billet de blog, je sentais que je n'avais aucun moyen d'écrire, mais après avoir commencé à écrire, j'ai découvert qu'il n'y avait vraiment rien à écrire. C'était juste un exercice.

Je suppose que tu aimes

Origine blog.csdn.net/qq_52309640/article/details/124076230
conseillé
Classement