吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1065|回复: 42
上一主题 下一主题
收起左侧

[Python 原创] 使用python和opencv写的屎山贪吃蛇游戏

  [复制链接]
跳转到指定楼层
楼主
sudezhao 发表于 2024-11-16 16:18 回帖奖励
本帖最后由 sudezhao 于 2024-11-16 16:25 编辑

使用python和opencv库写的一个贪吃蛇的游
大概是一年前写的,中间优化过好多次,仓库地址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                  // 忽略文件

运行效果:



部分代码(代码比较屎山没有全粘)
[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<=history:
        #历史得分
        history_label =tk.Label(score_window, text=f"历史最高: {history}", font=("Helvetica", 12))
        history_label.place(relx=0.5, rely=0.3, anchor="center")

        # 创建标签并显示图片
        beiyou_label = tk.Label(score_window, image=beiyou)
        beiyou_label.place(relx=0.5, rely=0.55, anchor="center")

        # 创建其他文本标签
        text_label = tk.Button(score_window, text="原神", font=("Helvetica", 12), command=reopen,
                               padx=48, pady=2)
        text_label.place(relx=0.27, rely=0.85, anchor="center")

        # 添加关闭按钮
        close_button = tk.Button(score_window, text="启动!", font=("Helvetica", 12), command=reopen,
                                 padx=47, pady=2)
        close_button.place(relx=0.73, rely=0.85, anchor="center")

    else:
        history_label = tk.Label(score_window, text=f"恭喜你!成为历史最高: {score}", font=("Helvetica", 12))
        history_label.place(relx=0.5, rely=0.45, anchor="center")

        # 添加关闭按钮
        close_button = tk.Button(score_window, text="原神怎么你了", font=("Helvetica", 12), command=close_score_window,
                                 padx=45, pady=3)
        close_button.place(relx=0.5, rely=0.85, anchor="center")

    def update_window():
        if score_window.winfo_exists():  # 检查窗口是否存在
            score_window.update()  # 更新窗口,处理事件
            score_window.after(100, update_window)  # 定时调用更新函数
        else:
            pass
    # 第一次调用更新函数
    update_window()


倒计时的屎山:
[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[i], (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:
                    break

        #cvzone.putTextRect(imgmain, "1", [630, 400], scale=7, thickness=0, offset=50)
        imgmain= cv2.putText(imgmain, "1", [580, 430], self.font,8, (0,200,200), 11)
        if self.flag1==0:
            musics.countdown.play()
            self.flag1=1
        return imgmain

    def show2(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[i], (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:
                    break

        #cvzone.putTextRect(imgmain, "2", [630, 400], scale=7, thickness=0, offset=50)
        imgmain = cv2.putText(imgmain, "2", [580, 430], self.font,8, (0, 200, 200), 11)
        if self.flag2==0:
            musics.countdown.play()
            self.flag2=1
        return imgmain

    def show3(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[i], (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:
                    break

        #cvzone.putTextRect(imgmain, "3", [630, 400], scale=7, thickness=0, offset=50)
        imgmain = cv2.putText(imgmain, "3", [580, 430], self.font ,8, (0, 200, 200), 11)
        if self.flag3==0:
            musics.countdown.play()
            self.flag3=1
        return imgmain


免费评分

参与人数 11威望 +1 吾爱币 +28 热心值 +10 收起 理由
xql123456 + 1 我很赞同!
jie5201055 + 1 + 1 谢谢@Thanks!
qqy123 + 1 我很赞同!
glass131 + 1 + 1 我很赞同!
苏紫方璇 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
jan2204 + 1 + 1 我很赞同!
ccgao + 1 + 1 我很赞同!
Qinmuyi + 1 用心讨论,共获提升!
AlbusGellert + 1 谢谢@Thanks!
netspirit + 2 + 1 感谢您的宝贵建议,我们会努力争取做得更好!
alancj + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

来自 10#
 楼主| sudezhao 发表于 2024-11-16 19:05 |楼主
代码只粘贴了一部分,完整的在github上,代码抽象和优化一点没有,希望大佬提出意见和建议
推荐
xiaoli521 发表于 2024-11-16 18:35
推荐
prettyafei 发表于 2024-11-22 16:57
sudezhao 发表于 2024-11-22 01:07
当时不会,就硬写哈哈哈

哈哈  都一样
最开始的时候  写到一起  能跑起来就感觉非常有成就感了
后面慢慢的就想写的更漂亮一点  import 就成了最常用的单词了
沙发
yixingk 发表于 2024-11-16 17:51
阿坤,跑一哈
4#
AlbusGellert 发表于 2024-11-16 18:46
我来试一试
5#
noddy 发表于 2024-11-16 18:47
这个可以,游戏看上去挺好玩的。
6#
32100004 发表于 2024-11-16 18:55
学习一下~
7#
 楼主| sudezhao 发表于 2024-11-16 18:59 |楼主

希望能运行
8#
WmeiShiLi 发表于 2024-11-16 19:01
你别说,看起来还挺有意思,代码也能错啊
9#
 楼主| sudezhao 发表于 2024-11-16 19:02 |楼主

要是有大佬能提交pr就好了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 07:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表