用Python Tkinter打造的精彩连连看小游戏【附源码】

文章目录

  • 连连看小游戏:用Python Tkinter打造的精彩游戏体验
    • 游戏简介
    • 技术背景
    • MainWindow类:
      • 职责:
      • 方法:
      • Point类:
    • 主执行部分:
    • 完整代码:
    • 总结:

连连看小游戏:用Python Tkinter打造的精彩游戏体验

在丰富多彩的游戏世界中,连连看作为一种经典的解谜游戏一直备受欢迎。连连看的玩法简单却富有挑战性,通过消除相同图标的配对,玩家可以不断地挑战自己的观察力和反应速度。
在这里插入图片描述

游戏简介

连连看是一种考验玩家眼力和逻辑思维的益智游戏,也是一种常见的休闲娱乐方式。游戏的基本规则是通过连接两个相同图标的路径,消除所有的图标。路径连接可以是直线、一次拐弯或两次拐弯。只有在路径不被其他图标遮挡的情况下,连接才会有效。

技术背景

本次实现采用了Python编程语言,并利用Tkinter库作为图形用户界面的构建工具。Tkinter是Python内建的GUI工具包,它提供了创建窗口、按钮、菜单等常见GUI元素的功能,极大地简化了图形界面的开发过程。通过结合Python的易读性和Tkinter的灵活性,我们可以迅速实现一个简单而有趣的连连看小游戏。

MainWindow类:

职责:

  • 代表游戏的主窗口。
  • 使用Tkinter处理GUI元素。
  • 管理游戏的初始化、地图绘制、处理用户点击以及检查游戏结束条件。

代码示例:

class MainWindow():# ... (其他代码)def __init__(self):self.root = tk.Tk()# ... (其他初始化代码)def __addComponets(self):self.menubar = tk.Menu(self.root, bg="lightgrey", fg="black")# ... (其他菜单初始化代码)self.canvas = tk.Canvas(self.root, bg='white', width=450, height=450)self.canvas.pack(side=tk.TOP, pady=5)self.canvas.bind('<Button-1>', self.clickCanvas)# ... (其他画布初始化代码)def centerWindow(self, width, height):# ... (屏幕居中代码)def file_new(self, event=None):# ... (新游戏初始化代码)def clickCanvas(self, event):# ... (处理画布点击事件的代码)# ... (其他方法)

方法:

  • __init__: 初始化主窗口并设置组件。
  • __addComponets: 添加组件,如菜单和画布到窗口。
  • centerWindow: 将窗口居中于屏幕。
  • file_new: 初始化并开始新游戏。
  • clickCanvas: 处理游戏画布上的鼠标点击。
  • isGameEnd: 检查游戏是否结束。
  • extractSmallIconList: 从较大的图像中加载并提取小图标。
  • iniMap: 使用随机图标初始化游戏地图。
  • drawMap: 在画布上绘制游戏地图。

包含用于坐标计算、绘制选定区域、清除连接块以及检查地图中空白区域的各种辅助方法。

Point类:

  • 表示游戏网格上的二维点。
  • 提供用于检查有用性、相等性、克隆和更改坐标的方法。
class Point():def __init__(self, x, y):self.x = xself.y = ydef isUserful(self):return self.x >= 0 and self.y >= 0def isEqual(self, point):return self.x == point.x and self.y == point.ydef clone(self):return Point(self.x, self.y)def changeTo(self, point):self.x = point.xself.y = point.y

方法:

  • __init__: 使用x和y坐标初始化点。
  • isUserful: 检查点的坐标是否为非负数。
  • isEqual: 检查两个点是否相等。
  • clone: 创建点的克隆。
  • changeTo: 更改点的坐标以匹配另一个点。

主执行部分:

创建MainWindow类的实例以启动游戏。

MainWindow()

完整代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2017-10-02 15:19:24
# @Author  : Salamander	(1906747819@qq.com)
# @Link    : http://blog.51lucy.comimport os, random
import tkinter as tk
import tkinter.messagebox
from PIL import Image, ImageTkclass MainWindow():__gameTitle = "连连看游戏"__windowWidth = 700__windowHeigth = 500__icons = []__gameSize = 10 # 游戏尺寸__iconKind = __gameSize * __gameSize / 4 # 小图片种类数量__iconWidth = 40__iconHeight = 40__map = [] # 游戏地图__delta = 25__isFirst = True__isGameStart = False__formerPoint = NoneEMPTY = -1NONE_LINK = 0STRAIGHT_LINK = 1ONE_CORNER_LINK = 2TWO_CORNER_LINK = 3def __init__(self):self.root = tk.Tk()self.root.title(self.__gameTitle)self.centerWindow(self.__windowWidth, self.__windowHeigth)self.root.minsize(460, 460)self.__addComponets()self.extractSmallIconList()self.root.mainloop()def __addComponets(self):self.menubar = tk.Menu(self.root, bg="lightgrey", fg="black")self.file_menu = tk.Menu(self.menubar, tearoff=0, bg="lightgrey", fg="black")self.file_menu.add_command(label="新游戏", command=self.file_new, accelerator="Ctrl+N")self.menubar.add_cascade(label="游戏", menu=self.file_menu)self.root.configure(menu=self.menubar)self.canvas = tk.Canvas(self.root, bg = 'white', width = 450, height = 450)self.canvas.pack(side=tk.TOP, pady = 5)self.canvas.bind('<Button-1>', self.clickCanvas)def centerWindow(self, width, height):screenwidth = self.root.winfo_screenwidth()screenheight = self.root.winfo_screenheight()size = '%dx%d+%d+%d' % (width, height, (screenwidth - width)/2, (screenheight - height)/2)self.root.geometry(size)def file_new(self, event=None):self.iniMap()self.drawMap()self.__isGameStart = Truedef clickCanvas(self, event):if self.__isGameStart:point = self.getInnerPoint(Point(event.x, event.y))# 有效点击坐标if point.isUserful() and not self.isEmptyInMap(point):if self.__isFirst:self.drawSelectedArea(point)self.__isFirst= Falseself.__formerPoint = pointelse:if self.__formerPoint.isEqual(point):self.__isFirst = Trueself.canvas.delete("rectRedOne")else:linkType = self.getLinkType(self.__formerPoint, point)if linkType['type'] != self.NONE_LINK:# TODO Animationself.ClearLinkedBlocks(self.__formerPoint, point)self.canvas.delete("rectRedOne")self.__isFirst = Trueif self.isGameEnd():tk.messagebox.showinfo("You Win!", "Tip")self.__isGameStart = Falseelse:self.__formerPoint = pointself.canvas.delete("rectRedOne")self.drawSelectedArea(point)# 判断游戏是否结束def isGameEnd(self):for y in range(0, self.__gameSize):for x in range(0, self.__gameSize):if self.__map[y][x] != self.EMPTY:return Falsereturn True'''提取小头像数组'''def extractSmallIconList(self):root_dir = os.getcwd()imagePath = os.path.join(root_dir, 'images', 'NARUTO.png')imageSouce = Image.open(imagePath)for index in range(0, int(self.__iconKind)):region = imageSouce.crop((self.__iconWidth * index, 0, self.__iconWidth * index + self.__iconWidth - 1, self.__iconHeight - 1))self.__icons.append(ImageTk.PhotoImage(region))'''初始化地图 存值为0-24'''def iniMap(self):self.__map = [] # 重置地图tmpRecords = []records = []for i in range(0, int(self.__iconKind)):for j in range(0, 4):tmpRecords.append(i)total = self.__gameSize * self.__gameSizefor x in range(0, total):index = random.randint(0, total - x - 1)records.append(tmpRecords[index])del tmpRecords[index]# 一维数组转为二维,y为高维度for y in range(0, self.__gameSize):for x in range(0, self.__gameSize):if x == 0:self.__map.append([])self.__map[y].append(records[x + y * self.__gameSize])'''根据地图绘制图像'''def drawMap(self):self.canvas.delete("all")for y in range(0, self.__gameSize):for x in range(0, self.__gameSize):point = self.getOuterLeftTopPoint(Point(x, y))im = self.canvas.create_image((point.x, point.y), image=self.__icons[self.__map[y][x]], anchor='nw', tags = 'im%d%d' % (x, y))'''获取内部坐标对应矩形左上角顶点坐标'''def getOuterLeftTopPoint(self, point):return Point(self.getX(point.x), self.getY(point.y))'''获取内部坐标对应矩形中心坐标'''def getOuterCenterPoint(self, point):return Point(self.getX(point.x) + int(self.__iconWidth / 2), self.getY(point.y) + int(self.__iconHeight / 2))def getX(self, x):return x * self.__iconWidth + self.__deltadef getY(self, y):return y * self.__iconHeight + self.__delta'''获取内部坐标'''def getInnerPoint(self, point):x = -1y = -1for i in range(0, self.__gameSize):x1 = self.getX(i)x2 = self.getX(i + 1)if point.x >= x1 and point.x < x2:x = ifor j in range(0, self.__gameSize):j1 = self.getY(j)j2 = self.getY(j + 1)if point.y >= j1 and point.y < j2:y = jreturn Point(x, y)'''选择的区域变红,point为内部坐标'''def drawSelectedArea(self, point):pointLT = self.getOuterLeftTopPoint(point)pointRB = self.getOuterLeftTopPoint(Point(point.x + 1, point.y + 1))self.canvas.create_rectangle(pointLT.x, pointLT.y, pointRB.x - 1, pointRB.y - 1, outline = 'red', tags = "rectRedOne")'''消除连通的两个块'''def ClearLinkedBlocks(self, p1, p2):self.__map[p1.y][p1.x] = self.EMPTYself.__map[p2.y][p2.x] = self.EMPTYself.canvas.delete('im%d%d' % (p1.x, p1.y))self.canvas.delete('im%d%d' % (p2.x, p2.y))'''地图上该点是否为空'''def isEmptyInMap(self, point):if self.__map[point.y][point.x] == self.EMPTY:return Trueelse:return False'''获取两个点连通类型'''def getLinkType(self, p1, p2):# 首先判断两个方块中图片是否相同if self.__map[p1.y][p1.x] != self.__map[p2.y][p2.x]:return { 'type': self.NONE_LINK }if self.isStraightLink(p1, p2):return {'type': self.STRAIGHT_LINK}res = self.isOneCornerLink(p1, p2)if res:return {'type': self.ONE_CORNER_LINK,'p1': res}res = self.isTwoCornerLink(p1, p2)if res:return {'type': self.TWO_CORNER_LINK,'p1': res['p1'],'p2': res['p2']}return {'type': self.NONE_LINK}'''直连'''def isStraightLink(self, p1, p2):start = -1end = -1# 水平if p1.y == p2.y:# 大小判断if p2.x < p1.x:start = p2.xend = p1.xelse:start = p1.xend = p2.xfor x in range(start + 1, end):if self.__map[p1.y][x] != self.EMPTY:return Falsereturn Trueelif p1.x == p2.x:if p1.y > p2.y:start = p2.yend = p1.yelse:start = p1.yend = p2.yfor y in range(start + 1, end):if self.__map[y][p1.x] != self.EMPTY:return Falsereturn Truereturn Falsedef isOneCornerLink(self, p1, p2):pointCorner = Point(p1.x, p2.y)if self.isStraightLink(p1, pointCorner) and self.isStraightLink(pointCorner, p2) and self.isEmptyInMap(pointCorner):return pointCornerpointCorner = Point(p2.x, p1.y)if self.isStraightLink(p1, pointCorner) and self.isStraightLink(pointCorner, p2) and self.isEmptyInMap(pointCorner):return pointCornerdef isTwoCornerLink(self, p1, p2):for y in range(-1, self.__gameSize + 1):pointCorner1 = Point(p1.x, y)pointCorner2 = Point(p2.x, y)if y == p1.y or y == p2.y:continueif y == -1 or y == self.__gameSize:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner2, p2):return {'p1': pointCorner1, 'p2': pointCorner2}else:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner1, pointCorner2) and self.isStraightLink(pointCorner2, p2) and self.isEmptyInMap(pointCorner1) and self.isEmptyInMap(pointCorner2):return {'p1': pointCorner1, 'p2': pointCorner2}# 横向判断for x in range(-1, self.__gameSize + 1):pointCorner1 = Point(x, p1.y)pointCorner2 = Point(x, p2.y)if x == p1.x or x == p2.x:continueif x == -1 or x == self.__gameSize:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner2, p2):return {'p1': pointCorner1, 'p2': pointCorner2}else:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner1, pointCorner2) and self.isStraightLink(pointCorner2, p2) and self.isEmptyInMap(pointCorner1) and self.isEmptyInMap(pointCorner2):return {'p1': pointCorner1, 'p2': pointCorner2}class Point():def __init__(self, x, y):self.x = xself.y = ydef isUserful(self):if self.x >= 0 and self.y >= 0:return Trueelse:return False'''判断两个点是否相同'''def isEqual(self, point):if self.x == point.x and self.y == point.y:return Trueelse:return False'''克隆一份对象'''def clone(self):return Point(self.x, self.y)'''改为另一个对象'''def changeTo(self, point):self.x = point.xself.y = point.yMainWindow()

总结:

在这个充满创造力和乐趣的项目中,我们不仅实现了一个简单的连连看小游戏,更体验了Python和Tkinter在游戏开发领域的强大潜力。可以作为初学者学习Python GUI编程和游戏开发的绝佳实例。希望这个项目能够激发更多人对编程和游戏开发的兴趣,开启一个全新的学习之旅。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/251404.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海外云手机开辟企业跨境电商新道路

近几年&#xff0c;海外云手机为跨境电商、海外媒体引流、游戏行业等互联网领域注入了蓬勃活力。对于国内跨境电商而言&#xff0c;在亚马逊及其他平台上&#xff0c;短视频引流和社交电商营销成为最为有效的流量来源。如何通过海外云手机的助力&#xff0c;在新兴社交平台为企…

有趣的CSS - 鼠标悬浮线条动态变化

鼠标悬浮线条动态变化 整体效果核心代码html 代码&#xff1a;css 部分代码&#xff1a; 完整代码如下html 页面&#xff1a;css 样式&#xff1a;页面渲染效果&#xff1a; 整体效果 这个链接悬浮效果主要用 css3 的 animation 属性配合 :hover 伪选择器来实现的。 此效果可以…

【华为】GRE Over IPsec 实验配置

【思科】GRE Over IPsec 实验配置 前言报文格式 实验需求配置拓扑GRE配置步骤IPsec 配置步骤R1基础配置GRE 配置IPsec 配置 ISP_R2基础配置 R3基础配置GRE 配置IPsec 配置 PCPC1PC2 抓包检查OSPF建立GRE隧道建立IPsec 隧道建立Ping 配置文档 前言 GRE over IPSec可利用GRE和IP…

[python]基于LSTR车道线实时检测onnx部署

【框架地址】 https://github.com/liuruijin17/LSTR 【LSTR算法介绍】 LSTR车道线检测算法是一种用于识别和定位车道线的计算机视觉算法。它基于图像处理和机器学习的技术&#xff0c;通过对道路图像进行分析和处理&#xff0c;提取出车道线的位置和方向等信息。 LSTR车道线…

MySQL库表操作 作业

题目&#xff1a; 1. sql语句分为几类?2. 表的约束有哪些,分别是什么,设置的语法分别是什么?3. 做出班级表,学生表的E-R图,数据库模型图,以及核心的sql语句. 1. MySQL致力于支持全套ANSI/ISO SQL标准。在MySQL数据库中&#xff0c;SQL语句主要可以划分为以下几类: > DD…

计算机网络-调度算法-2(时间片轮转 优先级调度算法 多级反馈队列调度算法 多级队列调度算法)

文章目录 总览时间片轮转时间片大小为2时间片大小为5若按照先来先服务算法 优先级调度算法例题&#xff08; 非抢占式优先级调度算法&#xff09;例题&#xff08; 抢占式优先级调度算法&#xff09;补充 思考多级反馈队列调度算法例题 小结多级队列调度算法 总览 时间片轮转 …

ElementUI Form:Form表单

ElementUI安装与使用指南 Form表单 点击下载learnelementuispringboot项目源码 效果图 el-form.vue&#xff08;Form表单&#xff09;页面效果图 项目里 el-form.vue代码 <script> export default {name: el_form,data() {var checkAge (rule, value, callback…

LabVIEW核能设施监测

LabVIEW核能设施监测 在核能领域&#xff0c;确保设施运行的安全性和效率至关重要。LabVIEW通过与硬件的紧密集成&#xff0c;为高温气冷堆燃料装卸计数系统以及脉冲堆辐射剂量监测与数据管理系统提供了解决方案。这些系统不仅提高了监测和管理的精确度&#xff0c;也保证了核…

JVM之Java内存区域

JVM-Java内存区域 Java内存区域是Java虚拟机&#xff08;JVM&#xff09;管理的内存资源的逻辑划分&#xff0c;用于存储程序运行时所需的数据。Java内存区域的合理划分和管理对于程序的性能和稳定性具有重要影响。本文将深入探讨Java内存区域的各个部分&#xff0c;包括方法区…

比特币ETF广告战大爆发!

作者&#xff1a;秦晋 贝莱德主动发起广告攻势。 2月1日&#xff0c;据外媒Cryptoslate报道&#xff0c;贝莱德在提交给美国SEC的一份文件中显示&#xff0c;其提出一项在建筑物侧面投影比特币ETF广告计划。 据介绍&#xff0c;广告内容为&#xff1a;「IBIT」信号是一个以迈阿…

JAVA Web 学习(四)RabbitMQ、Zookeeper

十、消息队列服务器——RabbitMQ RabbitMQ是使用Erlang语言开发的开源消息队列系统&#xff0c;基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、 安全。AMQP协议更多用在企业系统内&#xff0c;对数据一致性、稳定性和可靠性要求…

QT研究笔记(一)windows 开发环境安装部署

一、Qt 是什么&#xff1f; Qt 是一个跨平台的应用程序开发框架&#xff0c;最初由挪威的 Trolltech 公司开发&#xff0c;并于2008年被诺基亚收购。后来&#xff0c;Qt 框架由 Digia 公司接手&#xff0c;并在2012年成立了 The Qt Company。Qt 提供了一套丰富的工具和类库&am…

Django模型(五)

一、数据的条件查询 参考文档:QuerySet API 参考 | Django 文档 | Django 1.1、常用检索字段 字段检索,是在字段名后加 __ 双下划线,再加关键字,类似 SQL 语句中的 where 后面的部分, 如: 字段名__关键字 exact :判断是否等于value,一般不使用,而直接使用 =contai…

微信网页授权之使用完整服务解决方案

目录 微信网页授权能力调整造成的问题 能力调整的内容和理由 原有运行方案 is_snapshotuser字段 改造原有方案 如何复现测试场景 小结 微信网页授权能力调整造成的问题 依附于第三方的开发&#xff0c;做为开发者经常会遇到第三方进行规范和开发的调整&#xff0c;如开…

2023年上-未来几年我要做什么

1月份&#xff0c;离职。 2月份&#xff0c;春节休假回来&#xff0c;中旬去参加了一个月的瑜伽培训&#xff0c;学会了倒立、鹤蝉。。。。 3月份&#xff0c;瑜伽培训结束&#xff0c;开始收拾房子&#xff0c;并调研各类项目。 4月份&#xff0c;参与了朋友的区块链项目 …

Java学习-常用API-ArrayList

ArrayList的遍历并删除元素&#xff08;案例&#xff09;ArrayList的常用apiArrayList是一种泛型集合ArrayList是什么&#xff1f;有何特点&#xff1f;作用&#xff1f; importjava.util.ArrayList; importjava.util.List; publicclassapiArrayListDemo1{ publicstaticvoidmai…

vue3.0中从proxy中取值

使用vue3.0时&#xff0c;因为底层是使用proxy进行代理的所以当我们打印一些值的时候是proxy代理之后的&#xff0c;是Proxy 对象&#xff0c;Proxy对象里边的[[Target]]才是真实的对象。也是我们需要的 第一种获取target值的方式&#xff1a; import { toRaw } from vue; le…

Logback学习

logback 1、logback介绍 Logback是由log4j创始人设计的另一个开源日志组件&#xff0c;性能比log4j要好。 lockback优点&#xff1a; 内核重写、测试充分、初始化内存加载更小&#xff0c;这一切让logback性能和log4j相比有诸多倍的提升。logback非常自然地直接实现了slf4j…

学习Spring的第十三天

非自定义bean注解开发 设置非自定义bean : 用bean去修饰一个方法 , 最后去返回 , spring就把返回的这个对象,放到Spring容器 一 :名字 : 如果bean配置了参数 , 名字就是参数名 , 如果没有 , 就是方法名字 二 : 如果方法产生对象时 , 需要注入数据 , 在方法参数设置即可 , …

基于springboot就业信息管理系统源码和论文

随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;就业信息管理系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的变化&#xff0c;而…