使用python和opencv写的屎山贪吃蛇游戏

查看 62|回复 10
作者:sudezhao   
使用python和opencv库写的一个
[color=]贪吃蛇
的游戏

大概是一年前写的,中间优化过好多次,仓库地址https://github.com/buptsdz/gluttonous-snake-cv写了完整的readme(个人感觉)
代码还是比较屎山的(难以想象的屎),比如三秒倒计时那里感觉很冗余(在最底下)
但是运行效果还行,能跑(

运行方式
可以从源码运行也可以从exe文件运行,使用的pyinstaller进行的打包(这个耗费了好久,今年十月份更新,之前都失败了,然后顺手记了个文档)
多文件打包的文档https://www.yuque.com/u39067637/maezfz/qqm6xavvkp00blyb
打包后的文件我放网盘了,可以直接运行:
百度网盘:https://pan.baidu.com/s/1rw7uLH-ReM5BjVyY-mQTzw?pwd=1565
夸克网盘:https://pan.quark.cn/s/e64625f6e4d4
演示视频:https://www.bilibili.com/video/BV1UP411C7YJ/?share_source=copy_web&vd_source=15650a9af77f8d76a170b281763a3039

文件目录结构
├── main.py                     // 主循环函数  
├── music.py                    // 音乐触发函数  
├── sg.py                       // 逻辑处理函数
├── requirements.txt            // 依赖库文件
├── history.txt                 // 存储最高记录
├── README.md                   // 说明文档
├── musicpackegs                // 音乐包  
│   ├── xxx.mp3                 // 各类音效  
│   └── ·······                 
├── photo                       // 图片素材
│   ├── xxx.jpg
│   ├── xxx.png  
│   └── ·······
└── .gitignore                  // 忽略文件
运行效果:


83061ce4e112f31f777bedbda0bb76c9.png (556.98 KB, 下载次数: 1)
下载附件
2024-11-16 15:48 上传



0616f17431daa953476055bb4cf2ca75.png (1.32 MB, 下载次数: 0)
下载附件
2024-11-16 15:52 上传



4f4bd31c817bcaa9ad07c04e57a0f163.png (40.2 KB, 下载次数: 0)
下载附件
2024-11-16 16:11 上传

部分代码(代码比较屎山没有全粘)
[Python] 纯文本查看 复制代码import cv2
from cvzone.HandTrackingModule import HandDetector
import tkinter as tk
from PIL import Image, ImageTk,ImageSequence
import time
from sg import SnakeGameClass
from music import musics
import os
import random
script_dir = os.path.dirname(os.path.abspath(__file__))
donut_path = os.path.join(script_dir, "photos", "Donut.jpg")
chang_path = os.path.join(script_dir, "photos", "chang.png")
tiao_path = os.path.join(script_dir, "photos", "tiao.png")
rap_path = os.path.join(script_dir, "photos", "rap.png")
lanqiu_path = os.path.join(script_dir, "photos", "lanqiu.png")
music_path = os.path.join(script_dir, "photos", "music.png")
head_path = os.path.join(script_dir, "photos", "zhongfen.png")
history_path = os.path.join(script_dir, "history.txt")
beiyou_path=os.path.join(script_dir, "photos", "beiyou.png")
cap = cv2.VideoCapture(0)
cap.set(3, 1280)  # width
cap.set(4, 800)  # height
prev_frame_time = 0
detector = HandDetector(detectionCon=0.7, maxHands=1)
def close_window():
    window.destroy()
def minimize_window():
    window.iconify()
# 创建窗口
window = tk.Tk()
window.title("Snake Game")
window.attributes("-fullscreen", False)  # 全屏显示
# 创建游戏对象
food_path=[chang_path,tiao_path,rap_path,lanqiu_path,music_path]
game = SnakeGameClass(food_path,head_path)
#图片路径
yanfei_path = os.path.join(script_dir, "photos", "yanfei.png")
shenlilinghua_path =os.path.join(script_dir, "photos", "shenlilinghua.png")
xiaogong_path =os.path.join(script_dir, "photos", "xiaogong.png")
ganyu_path =os.path.join(script_dir, "photos", "ganyu.png")
# 打开原始图片
yanfei = Image.open(yanfei_path)
shenlilinghua = Image.open(shenlilinghua_path)
xiaogong = Image.open(xiaogong_path)
ganyu = Image.open(ganyu_path)
# 缩放图片
yanfei= yanfei.resize((300, 300))
shenlilinghua = shenlilinghua.resize((300, 300))
xiaogong = xiaogong.resize((300, 300))
ganyu = ganyu.resize((300, 300))
# 将缩放后的图片转换为tkinter.PhotoImage对象
yanfei = ImageTk.PhotoImage(yanfei)
shenlilinghua = ImageTk.PhotoImage(shenlilinghua)
xiaogong = ImageTk.PhotoImage(xiaogong)
ganyu = ImageTk.PhotoImage(ganyu)
#转为tk的laber对象
label_wife1= tk.Label(window, image=yanfei)
label_wife2= tk.Label(window, image=shenlilinghua)
label_wife3= tk.Label(window, image=xiaogong)
label_wife4= tk.Label(window, image=ganyu)
canvas = None  # 画布对象
start_button = None  # 开始按钮
quit_button=None
restart_button = None  # 重新开始按钮
reset_flag = 0
update_flag = 0
score_label=None
gameover_label=None
image=None
countdown_flag=0
start_time=0
flag_random=0
score_window=None
history=0
#获取文件
history_file = open(history_path, 'r')
try:
    history = int(history_file.read())
except ValueError:
    history = 0
# 关闭文件
history_file.close()
# 读取 GIF 图像
gif_path=os.path.join(script_dir, "photos", "snake.gif")
gif = Image.open(gif_path)
# 获取 GIF 图像的每一帧
frames = []
for frame in ImageSequence.Iterator(gif):
    frames.append(ImageTk.PhotoImage(frame))
# 创建显示 GIF 图像的 Label
gif_label = tk.Label(window)
gif_label.pack()
# 更新 GIF 图像的显示
def update_frame(index):
    gif_label.config(image=frames[index])
    window.after(100, update_frame, (index + 1) % len(frames))
# 设置 GIF 图像的位置
gif_label.place(relx=0.5, rely=0.4, anchor=tk.CENTER)
# 开始播放 GIF 图像
update_frame(0)
# 创建beiyou的 PhotoImage 对象
beiyou = Image.open(beiyou_path)
beiyou = ImageTk.PhotoImage(beiyou)
def update_high_score(path):#更新文件中最高分
    # 读取txt文件中的数值
    with open(path, 'r') as file:
        content = file.read()
        max_value = int(content) if content else 0
    if game.score >= max_value:
        with open(path, 'w') as file:
            file.write(str(game.score))
def clear_history_callback():#清空历史记录
    global history
    history = 0
    with open(history_path, 'w') as file:
        file.write('0')
    history_label.config(text=f"历史最高: {history}")
def update_history():#清空历史记录
    global history
    with open(history_path, 'r') as file:
        history=int(file.read())
def close_score_window():
    global score_window
    score_window.destroy()
def show_score(score):
    global flag_random,history_path,history,score_window,history
    # 创建一个新窗口
    score_window = tk.Toplevel()
    score_window.title("玩原神导致的")
    score_window.protocol("WM_DELETE_WINDOW", close_score_window)
    # 设置窗口尺寸
    width = 350
    height = 250
    # 获取屏幕尺寸
    screen_width = score_window.winfo_screenwidth()
    screen_height = score_window.winfo_screenheight()
    if flag_random==0:
        x = (screen_width // 2) - (width // 2)
        y = (screen_height // 2) - (height // 2)
        score_window.geometry(f"{width}x{height}+{x}+{y}")
        score_window.resizable(False, False)  # 禁用窗口的调整大小功能
    else:
        # 计算窗口位置的随机范围
        x_range = screen_width - width
        y_range = screen_height - height
        # 生成随机位置坐标
        xran = random.randint(0, x_range)
        yran = random.randint(0, y_range)
        # 设置窗口位置
        score_window.geometry(f"{width}x{height}+{xran}+{yran}")
        score_window.resizable(False, False)  # 禁用窗口的调整大小功能
    def reopen():
        global flag_random
        if score_window.winfo_exists():  # 检查窗口是否存在
            score_window.destroy()  # 删除当前窗口
        if flag_random==0:
            flag_random=1
        show_score(game.score)  # 重新打开当前窗口
    # 创建显示得分的标签
    score_label = tk.Label(score_window, text=f"你的得分: {score}", font=("Helvetica", 14))
    score_label.place(relx=0.5, rely=0.2, anchor="center")
    if score
倒计时的屎山:
[Python] 纯文本查看 复制代码
#采用了最笨的方法展示倒计时
    def show1(self,imgmain,currenthead):
        px, py = self.previoushead  # previous xy坐标
        cx, cy = currenthead  # current xy坐标
        self.points.append([cx, cy])  # 为蛇添加当前点
        distance = math.hypot(cx - px, cy - py)  # 计算两个点之间距离
        self.lengths.append(distance)  # 将距离加到总长度上
        self.currentlength += distance  # 计算当前长度
        self.previoushead = cx, cy  # 更新蛇头位置
        #draw snake
        if self.points:
            for i, point in enumerate(self.points):
                if i != 0:  # 如果当前点不是第一个点
                    cv2.line(imgmain, self.points[i - 1], self.points, (0, 0, 255), 20)  # 画线
            cv2.circle(imgmain, self.points[-1], 20, (200, 0, 200), cv2.FILLED)
        #length reduction
        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:
            for i, length in enumerate(self.lengths):
                self.currentlength -= length
                self.lengths.pop(i)
                self.points.pop(i)
                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

窗口, 文件

sudezhao
OP
  

代码只粘贴了一部分,完整的在github上,代码抽象和优化一点没有,希望大佬提出意见和建议
xiaoli521   

来看看哈哈哈哈哈哈哈哈哈哈
prettyafei   


sudezhao 发表于 2024-11-22 01:07
当时不会,就硬写哈哈哈

哈哈  都一样
最开始的时候  写到一起  能跑起来就感觉非常有成就感了
后面慢慢的就想写的更漂亮一点  import 就成了最常用的单词了
yixingk   

阿坤,跑一哈
AlbusGellert   

我来试一试
noddy   

这个可以,游戏看上去挺好玩的。
32100004   

学习一下~
sudezhao
OP
  


AlbusGellert 发表于 2024-11-16 18:46
我来试一试

希望能运行
WmeiShiLi   

你别说,看起来还挺有意思,代码也能错啊
您需要登录后才可以回帖 登录 | 立即注册