Opencv项目实战:17 贪吃蛇游戏

news/2024/11/29 13:30:14/

目录

0、项目介绍

1、效果展示

2、项目搭建

3、项目代码展示与讲解

4、项目资源

5、项目总结


0、项目介绍

这次是一个有意思的计算机视觉游戏——贪吃蛇,我们以食指为蛇头,不断的移动我们的手指,当吃到甜甜圈的时候,蛇身增长,当食指停止或触碰蛇身时,游戏结束。点击'r'将重新开始游戏。

1、效果展示

制作成GIF图片的效果有点卡,实际的效果还是很好的。

2、项目搭建

————17 Gluttonous snake

        ——Donut.png

        ——snake.py

3、项目代码展示与讲解

#snake.pyimport math
import random
import cvzone
import cv2
import numpy as np
from cvzone.HandTrackingModule import HandDetectorcap = 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 = 150  # 总共允许长度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 # gameoverdef 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.previousHeadcx, cy = currentHeadself.points.append([cx, cy])distance = math.hypot(cx - px, cy - py)self.lengths.append(distance)self.currentLength += distanceself.previousHead = cx, cy# Length Reductionif self.currentLength > self.allowedLength:for i, length in enumerate(self.lengths):self.currentLength -= lengthself.lengths.pop(i)self.points.pop(i)if self.currentLength < self.allowedLength:break# Check if snake ate the Foodrx, ry = self.foodPointif rx - self.wFood // 2 < cx < rx + self.wFood // 2 and \ry - self.hFood // 2 < cy < ry + self.hFood // 2:self.randomFoodLocation()self.allowedLength += 50self.score += 1print(self.score)# Draw Snakeif 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)# Add FoodimgMain = 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)# Check for Collisionpts = 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 = Trueself.points = []  # all points of the snakeself.lengths = []  # distance between each pointself.currentLength = 0  # total length of the snakeself.allowedLength = 150  # total allowed Lengthself.previousHead = 0, 0  # previous head pointself.randomFoodLocation()return imgMaingame = 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]img = game.update(img, pointIndex)cv2.imshow("Image", img)key = cv2.waitKey(1)if key == ord('r'):game.gameOver = Falseelif key==27:break

首先,打开摄像头的窗口得比较的大,不仅为了方便测试,而且窗口小了也会影响我们的手势识别,然后建立SnakeGameClass类,其下的变量名用途请查看注释。

其中,先看while True里面,cv2.flip函数是用窗口画面翻转的,输入一个正值,便会绕y轴翻转,这样能够保证窗口画面中的动作与我们的想法相同,接下来获得我们的手指landmark,即是获得目前蛇的头部landmark,之后完成我们的游戏更新部分。

其次,来看randomFoodLocation()函数,可以用于随机生成甜甜圈所在位置,我们为边缘留了些位置,避免手势识别失误和容易出界的问题。此时它并不具备更新位置的能力(只是生成了一个随机数位置),我们用update函数来完成主要的功能。我们想要实现的一个效果是游戏结束时,我需要得到一个提示,你不能再继续下去了,就如上面的GIF图片中,当我手势被识别时,会有一个提示(用putTextRcet完成),否则就会进行正常的游戏操作。

正常的游戏操作,我希望有这几个方面,甜甜圈覆盖窗口画面实现更新、蛇头与蛇身相撞就会gameover、手势一旦识别必须不停移动、吃掉一个甜甜圈增长一节蛇身等。

  • Length Reduction:

        一旦蛇的总长度超过了我们允许的长度,我们就需要对其进行缩减,这里使用了enumerate函数对列表进行了迭代,返回了一个索引和值,方便减去对应的距离。

  • Check if snake ate the Food:

        只要蛇头进入了甜甜圈的范围内,即可认为是吃到了甜甜圈,接下来就可以完成食物位置的更新,蛇身的加长,score得分。

  • Draw Snake:

        画蛇的初始态。

  • Add Food:

        在窗口画面中覆盖上甜甜圈,可以使用cvzone.overlayPNG函数。

  • Check for Collision:

        [pts]为多边形曲线阵列,在这里我们将会绘制一个多边形,并用cv2.pointPolygonTest来返回除最后两个点外与蛇头最近的距离,因为有tickness的原因,所以我们无需纠结于minDist一定小于等于0。

4、项目资源

GitHub:17 Gluttonous snake

5、项目总结

最近本来在准备期末考试的,所以很久没写博客了,但由于疫情原因提前返乡回家了,这学期的考试要得等到明年啦。想要添加按钮、声音、毒药这些的,没有精力搞了,有兴趣的大家可以试试。


http://www.ppmy.cn/news/3312.html

相关文章

Mongodb数据库的安装部署及基本使用

Mongodb数据库的安装部署及基本使用一、Mongodb数据库介绍1.Mongodb简介2.Mongodb适用场景3.MongoDB特性二、检查本地系统环境1.检查系统版本2.检查yum仓库三、Mongodb的安装1.配置Mongodb的yum源2.安装Mongodb3.修改绑定IP4.启动Mongodb服务四、检查Mongodb状态1.查看Mongodb服…

Python中ArcPy基于矢量范围批量裁剪大量栅格遥感影像

本文介绍基于Python中ArcPy模块&#xff0c;基于矢量数据范围&#xff0c;对大量栅格遥感影像加以批量裁剪掩膜的方法。 本文所需要的代码如下所示。 # -*- coding: utf-8 -*- """ Created on Tue Dec 13 20:07:48 2022author: fkxxgis """impo…

IT Resilience vs. Disaster Recovery 谈谈对弹性与灾备的理解

背景 在大型企业中&#xff0c;可能会花很多时间考虑灾难恢复以及如何避免灾难恢复。毕竟&#xff0c;这是一场噩梦——一场自然灾难或人为破坏的灾难&#xff0c;会摧毁你的IT基础设施&#xff0c;让企业团队陷入困境。 灾难恢复&#xff08;DR&#xff09;和IT Resilience …

Spring Boot使用EasyExcel导入导出Excel

一、导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.6</version></dependency> 二、实现导出excel操作 1、对我们需要导出的实体类上加上注解&#xff0c;如下&#…

一个关于React数据不可变的无聊问题

对于一个React的开发者来说不知道你有没有想过为什么React追求数据不可变这个范式&#xff1b; 一个月前我想过一个问题如果我在使用useState这个hooks的时候传入的是一个改变后的引用类型对象会发生什么&#xff1f; 例如&#xff1a; import {useState} from "react&…

Python3.1 使用卡通头像网络模型生成卡通头像(基于GAN)

使用GAN网络生成卡通头像 提示&#xff1a;当前生成方法仅为实验场景 参考地址 使用GAN网络模型生成卡通头像使用GAN网络生成卡通头像前言一、下载模型二、编写生成卡通头像Python代码1.引入库2.加载模型3.生成卡通头像方法总结前言 随着人工智能的不断发展&#xff0c;机器学…

大数据面试之Spark基础常见题目

大数据面试之Spark基础常见题目 1.1 Spark解决什么问题 ​ Hadoop主要解决海量数据的存储和海量数据的计算。 ​ Spark主要解决海量数据的计算。存储还是交给HDFS。 1.2 Spark为什么会有自己的资源调度器 ​ Hadoop的YARN框架比Spark框架诞生的晚&#xff0c;因为YARN是在…

简洁而优美的结构 - 并查集 | 一文吃透 “带权并查集” 不同应用场景 | “手撕” 蓝桥杯A组J题 - 推导部分和

&#x1f49b;前情提要&#x1f49b; 本章节是每日一算法的并查集&带权并查集的相关知识~ 接下来我们即将进入一个全新的空间&#xff0c;对代码有一个全新的视角~ 以下的内容一定会让你对数据结构与算法有一个颠覆性的认识哦&#xff01;&#xff01;&#xff01; ❗以…