首页 > 编程语言 >基于Python的手势控制贪吃蛇游戏

基于Python的手势控制贪吃蛇游戏

时间:2024-09-07 17:22:49浏览次数:10  
标签:Python self SNAKE 贪吃蛇 pygame gesture 手势 def SIZE

文章目录


前言

   随着计算机视觉技术的不断发展,手势识别成为了一种新兴的人机交互方式。通过Python编程语言结合OpenCV库,我们可以开发出基于手势识别的游戏应用。本文将详细介绍如何使用Python实现一个简单的手势控制版贪吃蛇游戏,让玩家可以通过手势来控制游戏中蛇的移动方向。


一、pygame和mediapipe是什么?

尽管本文的重点在于手势识别与贪吃蛇游戏的结合,但为了保持结构的完整性,这里简要说明pygamemediapipe的作用:

  • pygame是一个用于制作视频游戏的Python库,它提供了访问计算机上的音频、图像等功能的接口,并简化了许多常见游戏功能的实现。
  • mediapipe是一个跨平台的框架,用于构建多模态应用级机器感知流水线,特别适用于实时处理人体姿态、面部表情等数据。

二、使用步骤

1.引入库

在开始之前,我们需要导入一些必要的库,包括pygame用于游戏逻辑和界面显示,cv2(OpenCV)、mediapipe用于手势识别,以及numpy进行数值计算。

import pygame
import sys
import random
import cv2
import mediapipe as mp
import numpy as np

2.实现游戏逻辑

首先,初始化pygame并设置游戏的基本参数,如屏幕大小、蛇的大小等。

pygame.init()

# 游戏常量
SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480
SNAKE_SIZE = 20
FOOD_SIZE = 20

# 方向定义
DIRECTIONS = {'UP': 0, 'RIGHT': 1, 'DOWN': 2, 'LEFT': 3}

# 颜色定义
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()

 定义SnakeFood类来管理游戏中的蛇和食物:

class Snake:
    def __init__(self):
        self.body = [(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)]
        self.direction = DIRECTIONS['RIGHT']

    def move(self):
        head_x, head_y = self.body[0]
        if self.direction == DIRECTIONS['RIGHT']:
            head_x += SNAKE_SIZE
        elif self.direction == DIRECTIONS['LEFT']:
            head_x -= SNAKE_SIZE
        elif self.direction == DIRECTIONS['UP']:
            head_y -= SNAKE_SIZE
        elif self.direction == DIRECTIONS['DOWN']:
            head_y += SNAKE_SIZE

        self.body.insert(0, (head_x, head_y))
        if len(self.body) > len(self.body) - 1:
            self.body.pop()

    def draw(self, surface):
        for segment in self.body:
            pygame.draw.rect(surface, GREEN, pygame.Rect(segment[0], segment[1], SNAKE_SIZE, SNAKE_SIZE))

class Food:
    def __init__(self):
        self.position = (random.randint(0, (SCREEN_WIDTH - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE,
                         random.randint(0, (SCREEN_HEIGHT - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE)

    def draw(self, surface):
        pygame.draw.rect(surface, RED, pygame.Rect(self.position[0], self.position[1], FOOD_SIZE, FOOD_SIZE))

接着,实现手势识别功能:

class HandDetector:
    # 手势识别类
    # ...(省略部分代码,见原代码段)

    def findHands(self, img, draw=True):
        # ...(省略部分代码,见原代码段)
        return img

    def findPosition(self, img, handNo=0, draw=True):
        # ...(省略部分代码,见原代码段)
        return self.lmList, bboxInfo

    def fingcurved(self):
        # ...(省略部分代码,见原代码段)
        return finger

    def okgesture(self):
        # ...(省略部分代码,见原代码段)
        return True

    def handType(self):
        # ...(省略部分代码,见原代码段)
        return "Right"

class Main:
    # 主控类
    # ...(省略部分代码,见原代码段)

    def Gesture_recognition(self):
        # ...(省略部分代码,见原代码段)
        return gesture

 最后,编写游戏主循环,将手势识别与游戏逻辑结合起来:

def get_new_direction():
    MAIN = Main()
    gesture = MAIN.Gesture_recognition()
    if gesture == "UP":
        return DIRECTIONS['UP']
    elif gesture == "RIGHT":
        return DIRECTIONS['RIGHT']
    elif gesture == "DOWN":
        return DIRECTIONS['DOWN']
    elif gesture == "LEFT":
        return DIRECTIONS['LEFT']
    return None

def main():
    snake = Snake()
    food = Food()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        new_direction = get_new_direction()
        if new_direction is not None and new_direction != (snake.direction + 2) % 4:
            snake.direction = new_direction

        snake.move()
        if snake.body[0] == food.position:
            food.position = (random.randint(0, (SCREEN_WIDTH - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE,
                             random.randint(0, (SCREEN_HEIGHT - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE)
            snake.body.append(snake.body[-1])

        screen.fill(WHITE)
        snake.draw(screen)
        food.draw(screen)
        pygame.display.flip()
        clock.tick(10)

if __name__ == "__main__":
    main()

 完整代码如下:

# 比心       前进
# 张开手掌    后退
# ok手势     右转
# 剪刀手势    左转
import pygame
import sys
import random
import cv2
import mediapipe as mp
import numpy as np

pygame.init()

# 游戏常量
SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480
SNAKE_SIZE = 20
FOOD_SIZE = 20

# 方向定义
DIRECTIONS = {'UP': 0, 'RIGHT': 1, 'DOWN': 2, 'LEFT': 3}

# 颜色定义
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()


class Snake:
    def __init__(self):
        self.body = [(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)]
        self.direction = DIRECTIONS['RIGHT']

    def move(self):
        head_x, head_y = self.body[0]
        if self.direction == DIRECTIONS['RIGHT']:
            head_x += SNAKE_SIZE
        elif self.direction == DIRECTIONS['LEFT']:
            head_x -= SNAKE_SIZE
        elif self.direction == DIRECTIONS['UP']:
            head_y -= SNAKE_SIZE
        elif self.direction == DIRECTIONS['DOWN']:
            head_y += SNAKE_SIZE

        self.body.insert(0, (head_x, head_y))
        if len(self.body) > len(self.body) - 1:
            self.body.pop()

    def draw(self, surface):
        for segment in self.body:
            pygame.draw.rect(surface, GREEN, pygame.Rect(segment[0], segment[1], SNAKE_SIZE, SNAKE_SIZE))


class Food:
    def __init__(self):
        self.position = (random.randint(0, (SCREEN_WIDTH - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE,
                         random.randint(0, (SCREEN_HEIGHT - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE)

    def draw(self, surface):
        pygame.draw.rect(surface, RED, pygame.Rect(self.position[0], self.position[1], FOOD_SIZE, FOOD_SIZE))


class HandDetector:
    """
    使用mediapipe库查找手。导出地标像素格式。添加了额外的功能。
    如查找方式,许多手指向上或两个手指之间的距离。而且提供找到的手的边界框信息。
    """

    def __init__(self, mode=False, maxHands=2, detectionCon=0.5, minTrackCon=0.5):
        """
        :param mode: 在静态模式下,对每个图像进行检测
        :param maxHands: 要检测的最大手数
        :param detectionCon: 最小检测置信度
        :param minTrackCon: 最小跟踪置信度
        """
        self.results = None
        self.mode = mode
        self.maxHands = maxHands
        self.modelComplex = False
        self.detectionCon = detectionCon
        self.minTrackCon = minTrackCon

        # 初始化手部识别模型
        self.mpHands = mp.solutions.hands
        self.hands = self.mpHands.Hands(self.mode, self.maxHands, self.modelComplex,
                                        self.detectionCon, self.minTrackCon)
        self.mpDraw = mp.solutions.drawing_utils  # 初始化绘图器
        self.tipIds = [4, 8, 12, 16, 20]  # 指尖列表
        self.fingers = []  # 存储手的状态
        self.lmList = []  # 储检测到的手部的每个关键点的坐标

    def findHands(self, img, draw=True):
        """
        从图像(BRG)中找到手部。
        :param img: 用于查找手的图像。
        :param draw: 在图像上绘制输出的标志。
        :return: 带或不带图形的图像
        """
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 将传入的图像由BGR模式转标准的Opencv模式——RGB模式,
        self.results = self.hands.process(imgRGB)  # 处理图像,返回包含检测到的手部信息的结果。这个结果通常包含了手部的关键点坐标

        # 画出手的关键点和线条
        if self.results.multi_hand_landmarks:
            for handLms in self.results.multi_hand_landmarks:
                if draw:
                    self.mpDraw.draw_landmarks(img, handLms,
                                               self.mpHands.HAND_CONNECTIONS)
        return img

    def findPosition(self, img, handNo=0, draw=True):
        """
        查找单手的地标并将其放入列表中像素格式。还可以返回手部周围的边界框。
        :param img: 要查找的主图像
        :param handNo: 如果检测到多只手,则为手部id
        :param draw: 在图像上绘制输出的标志。(默认绘制矩形框)
        :return: 像素格式的手部关节位置列表;手部边界框
        """
        # 保存关键点的像素坐标
        xList = []
        yList = []
        bbox = []
        bboxInfo = []  # 保存首部检测框信息
        self.lmList = []
        if self.results.multi_hand_landmarks:
            myHand = self.results.multi_hand_landmarks[handNo]
            for id, lm in enumerate(myHand.landmark):  # 遍历手部关键点,id表示关键点下标,lm表示关键点对象
                h, w, c = img.shape
                #  lm是存储的是关键点归一化(0~1)的相对位置,
                px, py = int(lm.x * w), int(lm.y * h)  # 转换为图像中的像素坐标
                xList.append(px)
                yList.append(py)
                self.lmList.append([px, py])
                if draw:
                    cv2.circle(img, (px, py), 5, (255, 0, 255), cv2.FILLED)  # 用红点标记关键点
            # 获取手关键点的左上点和右下点
            xmin, xmax = min(xList), max(xList)
            ymin, ymax = min(yList), max(yList)
            # 边界框信息存储
            boxW, boxH = xmax - xmin, ymax - ymin
            bbox = xmin, ymin, boxW, boxH
            # 边界框中心坐标
            cx, cy = bbox[0] + (bbox[2] // 2), \
                     bbox[1] + (bbox[3] // 2)
            # id含义是指的手部最后一个关键点的下标
            bboxInfo = {"id": id, "bbox": bbox, "center": (cx, cy)}

            if draw:
                cv2.rectangle(img, (bbox[0] - 20, bbox[1] - 20),
                              (bbox[0] + bbox[2] + 20, bbox[1] + bbox[3] + 20),
                              (0, 255, 0), 2)

        return self.lmList, bboxInfo

    def fingcurved(self):
        """
        查找除拇指外的四个手指弯曲状态
        计算方式:
            取出除了大拇指以外的四个手指指尖坐标a1、a2、a3、a4(对应地标8,12,16,20),
            然后取出地标为6,10,14,18的坐标b1、b2、b3、b4(即每个指尖以下第二个关节),
            通过比较指尖(a1、a2、a3、a4)到手腕地标(0)和指关节(b1、b2、b3、b4)到地标0的欧几里得距离,
            即可区分手指是否弯曲
        :return: 弯曲手指的列表
        """
        finger = []
        for id in range(1, 5):
            point1 = np.array((self.lmList[self.tipIds[id]][0], self.lmList[self.tipIds[id]][1]))
            point2 = np.array((self.lmList[self.tipIds[id] - 2][0], self.lmList[self.tipIds[id] - 2][1]))
            point3 = np.array((self.lmList[0][0], self.lmList[0][1]))
            if np.linalg.norm(point1 - point3) < np.linalg.norm(point2 - point3):  # 计算两点之间的距离
                finger.append(1)
            else:
                finger.append(0)

        return finger

    def okgesture(self):
        """
        特殊手势处理:判断是否手势为ok
        判断方式:
            ok手势,其拇指指尖地标a0和食指指尖地标a1十分接近,于是我们这样处理:如果中指、无名
            指、小拇指伸直并且食指指尖到大拇指指尖的距离小于食指指尖到中指指尖距离则断定为ok手
            势。
        """
        f1, f2, f3, f4 = self.fingcurved()
        if (f2 == 0 and f3 == 0 and f4 == 0):
            point1 = np.array((self.lmList[8][0], self.lmList[8][1]))
            point2 = np.array((self.lmList[4][0], self.lmList[4][1]))
            point3 = np.array((self.lmList[12][0], self.lmList[12][1]))
            if np.linalg.norm(point1 - point2) < np.linalg.norm(point1 - point3):
                return True

    def handType(self):
        """
        检查传入的手部是左还是右
        :return: "Right" 或 "Left"
        """
        if self.results.multi_hand_landmarks:
            if self.lmList[17][0] < self.lmList[5][0]:
                return "Right"
            else:
                return "Left"


class Main:

    def __init__(self):
        self.detector = None
        self.camera = cv2.VideoCapture(0, cv2.CAP_DSHOW)  # 以视频流传入
        self.camera.set(3, 1280)  # 设置分辨率
        self.camera.set(4, 720)

    def Gesture_recognition(self):
        self.detector = HandDetector()
        while True:
            ret, img = self.camera.read()
            if ret:
                img = self.detector.findHands(img)  # 获取你的手部的关键点信息
                cv2.imshow('hand', img)
                lmList, bbox = self.detector.findPosition(img)  # 获取你手部的关键点的像素坐标和边界框
                if lmList:
                    x_1, y_1 = bbox["bbox"][0], bbox["bbox"][1]
                    f1, f2, f3, f4 = self.detector.fingcurved()
                    # 根据手指弯曲状态识别手势并在图像上显示相应文本
                    if f1 == 0 and f2 == 1 and f3 == 1 and f4 == 1:
                        gesture = "UP"
                        print("手势为:", gesture)
                    elif self.detector.okgesture():
                        gesture = "RIGHT"
                        print("手势为:", gesture)
                    elif f1 == 0 and f2 == 0 and f3 == 0 and f4 == 0:
                        gesture = "DOWN"
                        print("手势为:", gesture)
                    elif f1 == 0 and f2 == 0 and f3 == 1 and f4 == 1:
                        gesture = "LEFT"
                        print("手势为:", gesture)
                    else:
                        gesture = None
                        print("手势为:", gesture)
                    return gesture
                    if gesture:
                        cv2.putText(img, gesture, (x_1, y_1), cv2.FONT_HERSHEY_PLAIN, 3, (0, 0, 255), 3)
                        gesture_buffer.insert(0, gesture)
                        gesture_buffer.pop()
                    return gesture
                cv2.imshow("camera", img)
            if cv2.getWindowProperty('camera', cv2.WND_PROP_VISIBLE) < 1:
                break
            # 通过关闭按钮退出程序
            cv2.waitKey(1)
            # if cv2.waitKey(1) & 0xFF == ord("q"):
            #     break # 按下q退出


def main():
    snake = Snake()
    food = Food()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        # 更新方向(这里应由手势控制)
        new_direction = get_new_direction()
        if new_direction is not None and new_direction != (snake.direction + 2) % 4:
            snake.direction = new_direction

        snake.move()
        if snake.body[0] == food.position:
            food.position = (random.randint(0, (SCREEN_WIDTH - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE,
                             random.randint(0, (SCREEN_HEIGHT - FOOD_SIZE) // SNAKE_SIZE) * SNAKE_SIZE)
            snake.body.append(snake.body[-1])

        screen.fill(WHITE)
        snake.draw(screen)
        food.draw(screen)
        pygame.display.flip()
        clock.tick(10)


def get_new_direction():
    MAIN = Main()
    MAIN.Gesture_recognition()
    gesture = MAIN.Gesture_recognition()
    if gesture == "UP":
        return DIRECTIONS['UP']
    elif gesture == "DOWN":
        return DIRECTIONS['DOWN']
    elif gesture == "LEFT":
        return DIRECTIONS['LEFT']
    elif gesture == "RIGHT":
        return DIRECTIONS['RIGHT']
    else:
        return None


if __name__ == "__main__":
    main()

总结

以上就是今天我开发的手势大逃“蛇”游戏。本文通过详细的示例介绍了如何使用Python结合OpenCVmediapipe库来开发一个基于手势控制的贪吃蛇游戏。虽然这里提供的只是一个基础版本,但是通过进一步的开发,您可以逐步完善这个项目,实现更复杂的功能。

赋几张手势图和游戏画面

标签:Python,self,SNAKE,贪吃蛇,pygame,gesture,手势,def,SIZE
From: https://blog.csdn.net/sun_boy98/article/details/141965389

相关文章

  • 计算机毕业设计推荐-基于python医院门诊预约挂号系统
    精彩专栏推荐订阅:在下方主页......
  • python学习总结之内存处理
    1.引用计数法注:类似于java,这个系统自动回收垃圾对象,明显有循环引用的弊端。代码例子importsysimportpsutilimportosimportgcprint(gc.get_threshold())defshowMemSize(tag):pid=os.getpid()p=psutil.Process(pid)info=p.memory_full_info()memory=i......
  • Python数学建模算法与应用例题
    2.21.aggcacggaaaaacgggaataacggaggaggacttggcacggcattacacggagg2.cggaggacaaacgggatggcggtattggaggtggcggactgttcgggga3.gggacggatacggattctggccacggacggaaaggaggacacggcggacataca4.atggataacggaaacaaaccagacaaacttcggtagaaatacagaagctta5.cggctggcggacaacggactggcggatt......
  • Python中差分进化differential_evolution的调用及参数说明
    在场景应用中,要求我们的函数计算结果尽可能的逼近实际测量结果,可转化计算结果与测量结果的残差,通过最小化残差,便可求出最优的结果。但使用最小二乘等方法来计算时,常常会使迭代的结果显然局部最优点而导致结算错误。差分进化原理差分进化(DifferentialEvolution,DE)是一种基......
  • 分享10个免费的Python代码仓库,轻松实现办公自动化!
    为了帮助大家更好地利用Python实现自动化办公,我们精心挑选了10个免费的Python代码仓库。这些仓库不仅包含了实用的脚本和示例,还涵盖了从基础到进阶的各种自动化任务解决方案。无论你是Python编程的初学者,还是希望提升工作效率的职场人士,都能在这些仓库中找到适合自己的资......
  • Python毕业设计基于Django的动漫漫画手办周边商城
    文末获取资源,收藏关注不迷路文章目录一、项目介绍二、主要使用技术三、研究内容四、核心代码五、文章目录一、项目介绍动漫周边商城分为二个模块,分别是管理员功能模块和用户功能模块。管理员功能模块包括:文章资讯、文章类型、动漫活动、动漫商品功能,用户功能模块......
  • 【Python 千题 —— 基础篇】敏感词处理
    Python千题持续更新中……脑图地址......
  • Python爬取下载m3u8加密视频,原来这么简单!
    本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。爬取视频的时候发现,现在的视频都是经过加密(m3u8),不再是mp4或者avi链接直接在网页显示,都是经过加密形成ts文件分段进行播放。今天就教大家如何通过python爬取下载m3u8加密......
  • [Python之代码爬虫] -爬取披头士乐队历年专辑封面-网易云音乐
    一、前言前文说过我的设计师小伙伴的设计需求,他想做一个披头士乐队历年专辑的瀑布图。通过搜索,发现网易云音乐上有比较全的历年专辑信息加配图,图片质量还可以,虽然有大有小。我的例子怎么都是爬取图片?(谁让你总是跟设计师小伙伴一起玩耍。。。)看来图片对于设计师来说还是有着......
  • Python windows虚拟环境离线安装依赖
    前提需要一台有网的机器下载whl包能够拷贝到离线机器创建虚拟环境安装虚拟环境之前,需要你本地有一套python环境。比如我本地python环境:创建虚拟环境命令:python-mvenvmyvenv创建成功后会生成myvenv目录,并且有Scripts文件夹(这里存放的就是虚拟环境的python解释器,pychr......