pygame interchangeable puzzle design

run screenshot

insert image description here
insert image description here
insert image description here

Screenshot when finished
insert image description here

game design ideas

On the 600*600 screen, divide the puzzle. (It can be N*M, not necessarily N*N)
Use the mouse to operate the puzzle to move

game design

Make a class: PartPicture is used to save the information of the block picture (block image, position x, y, and the id of each picture)

class PartPicture:
    # 初始化一个图片分块,保存图像和图像的位置, id代表原图第几块的位置
    def __init__(self, img, x, y, id):
        self.img = img
        self.x = x
        self.y = y
        self.id = id
	# get和set方法
    def getX(self):
        return self.x;
    def getY(self):
        return self.y
    # 获取图片坐标
    def getXY(self):
        return self.x, self.y
    # 修改图片坐标
    def setXY(self,x, y):
        self.x = x
        self.y = y
    # 获取id
    def getId(self):
        return self.id

Now this class is not perfect, we will improve it later

Initial information of the pygame game

Set the default game screen size, the number of puzzle divisions, and the size of each puzzle piece. firstClickCell is used to record the picture clicked for the first time in the future, and successFlag is used to save whether the game is successful

SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
ROW = 0
COL = 0
CELL_WIDTH = 0#SCREEN_WIDTH / COL
CELL_HEIGHT = 0#SCREEN_HEIGHT / ROW
firstClickCell = None
successFlag = False

Set the initial parameters of pygame

pygame.init()  # 初始化pygame
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))  # 创建了一个窗口 大小是(宽,高)
pygame.display.set_caption('交换式拼图')  # 设置窗口标题

Make start interface

Set the color RGB and set the related font

GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
myfont = pygame.font.Font(None, 70)
textImage1 = myfont.render("simple", True , GREEN)
textImage2 = myfont.render("difficult", True , BLUE)
textImage3 = myfont.render("bow!!!", True , RED)
setInit = False

Select the puzzle difficulty on the start interface, setInit is used to save whether to initialize the settings, click on the relevant difficulty, and then start to initialize the settings (each size of the puzzle can be N*M such as 3 * 4 or 4 * 3), that is, ROW and COL value

setInit = False
while True:
    if setInit:
        break
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()  # 接收到退出事件后退出程序
        elif event.type == MOUSEBUTTONDOWN:
            b1, b2, b3 = pygame.mouse.get_pressed()
            if b1 == 1:
                point_x, point_y = pygame.mouse.get_pos()  # 返回鼠标当前坐标
                if 100 < point_y < 250:
                    ROW = 4
                    COL = 4
                    setInit = True
                elif 250 < point_y < 400:
                    ROW = 10
                    COL = 10
                    setInit = True
                elif 400 < point_y < 550:
                    ROW = 100
                    COL = 100
                    setInit = True
    screen.fill((0,0,0))
    screen.blit(textImage1, (200, 100))
    screen.blit(textImage2, (200, 250))
    screen.blit(textImage3, (200, 400))
    pygame.display.update()
CELL_WIDTH = SCREEN_WIDTH / COL
CELL_HEIGHT = SCREEN_HEIGHT / ROW

Loading pictures and picture segmentation

imgSrc = pygame.image.load('src.jpg').convert()  # 原图 提高 blit 的速度 convert_alpha相对于convert,保留了图像的Alpha 通道信息,可以认为是保留了透明的部分,实现了透明转换
PictureList = devide(imgSrc)

The core code of image segmentation

# 图片分块的方法
def devide(imgSrc):
    # 切割原来的图片
    pictures = []
    ScPictures = []
    id = 0 # 给每个分块的图片设置下标
    for i in range(ROW):
        for j in range(COL):
            # 提取部分图片
            partOfPicutre = imgSrc.subsurface(j * CELL_WIDTH, i * CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT)
            # 保存第一组图片
            tempPicture = PartPicture(partOfPicutre, j * CELL_WIDTH, i * CELL_HEIGHT, id)
            pictures.append(tempPicture)
            # 保存第二组图片
            tempPicture = PartPicture(partOfPicutre, j * CELL_WIDTH, i * CELL_HEIGHT, id)
            ScPictures.append(tempPicture)
            id += 1
    random.shuffle(pictures)
    # 开始利用第二组图片来打乱原来的图片
    for i in range(len(pictures)):
        pictures[i].setXY(ScPictures[i].getX(), ScPictures[i].getY())
    return pictures # 把打乱的图片返回

We complete the PartPicture class

We let this class operate the movement of the picture, isPressed() we need to judge the first picture clicked by the mouse, and use the isOver() method to lock the picture we selected. After saving the first picture, firstClickCell has a value. When we click again, we exchange the first picture with the second selected picture (it can be ourselves), and then restore firstClickCell after the exchange . For each operation, we need to judge whether the jigsaw puzzle is completed. After completing the jigsaw puzzle, we can judge whether the jigsaw puzzle is in its original position. We can use the id attribute to obtain the position of the row and column blocks, and then calculate the relevant coordinates. If there is one that does not match the jigsaw puzzle, there is no Finish

class PartPicture:
    # 初始化一个图片分块,保存图像和图像的位置, id代表原图第几块的位置
    def __init__(self, img, x, y, id):
        self.img = img
        self.x = x
        self.y = y
        self.id = id
    # 判断是否在图片内
    def isOver(self):
        w, h = self.img.get_size()
        point_x, point_y = pygame.mouse.get_pos()  # 返回鼠标当前坐标
        in_x = self.x < point_x < self.x + w
        in_y = self.y < point_y < self.y + h
        #('id,x,y', self.id,':' ,self.getX(), ',',self.getY())
        #print('id', self.id, in_x, in_y)
        return in_x and in_y
    # 检测移动
    def isPressed(self, pictureList):
        # print('is_pressed')
        if self.isOver():
            b1, b2, b3 = pygame.mouse.get_pressed()
            if b1 == 1:
                global firstClickCell
                global successFlag

                if firstClickCell is None:
                    firstClickCell = self
                    print('id为{}的块被点击'.format(firstClickCell.getId()))
                else:
                    print('交换{}与{}的坐标'.format(firstClickCell.getId(), self.getId()))
                    self.pictureSwitch(firstClickCell, self)
                    if self.isFinish(pictureList):
                        print('成功!')
                        successFlag = True
                    else:
                        successFlag = False
                    firstClickCell = None
                return True
        return False
    # 判断拼图完成
    def isFinish(self, pictureList):
        for cell in  pictureList:
            nowp_x, nowp_y = cell.getXY()
            p_x = cell.getId() % COL * CELL_WIDTH
            p_y = (cell.getId() // COL) * CELL_HEIGHT
            print("id{} nowx{}与nowy{}的坐标 本来的坐标x{}y{}".format(cell.getId(), nowp_x, nowp_y, p_x, p_y))
            if nowp_x != p_x or nowp_y != p_y:
                return False
        return True

    def pictureSwitch(self, cell1, cell2):
        tempX = cell1.getX()
        tempY = cell1.getY()
        cell1.setXY(cell2.getX(), cell2.getY())
        cell2.setXY(tempX, tempY)
    def render(self, screen):
        screen.blit(self.img, (self.x, self.y))

real game loop

while True:  # 游戏主循环
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()  # 接收到退出事件后退出程序
        elif event.type == MOUSEBUTTONDOWN:
            for cell in PictureList:
                # 检测按键按下,并且交换图片位置
                if cell.isPressed(PictureList):
                    break
    for partPicture in PictureList:
        partPicture.render(screen)
    if not successFlag:
        # # Sta 绘制分割线
        for i in range(1, COL):
            pygame.draw.lines(screen, GREEN, 0, [(i * SCREEN_WIDTH // COL, 0), (i * SCREEN_WIDTH // COL, SCREEN_HEIGHT)], 1)
        for i in range(1, ROW):
            pygame.draw.lines(screen, GREEN, 0, [(0, i * SCREEN_HEIGHT // ROW), (SCREEN_WIDTH, i * SCREEN_HEIGHT // ROW)], 1)
        # End 绘制分割线
    pygame.display.update()  # 刷新一下画面

full code

The image is src, 600*600 size

import random
from sys import exit  # 使用sys模块的exit函数来退出游戏
import pygame
from pygame.locals import *  # 导入一些常用的函数和常量

SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
ROW = 100
COL = 100
CELL_WIDTH = 0#SCREEN_WIDTH / COL
CELL_HEIGHT = 0#SCREEN_HEIGHT / ROW
firstClickCell = None

class PartPicture:
    # 初始化一个图片分块,保存图像和图像的位置, id代表原图第几块的位置
    def __init__(self, img, x, y, id):
        self.img = img
        self.x = x
        self.y = y
        self.id = id
    # 判断是否在图片内
    def isOver(self):
        w, h = self.img.get_size()
        point_x, point_y = pygame.mouse.get_pos()  # 返回鼠标当前坐标
        in_x = self.x < point_x < self.x + w
        in_y = self.y < point_y < self.y + h
        #('id,x,y', self.id,':' ,self.getX(), ',',self.getY())
        #print('id', self.id, in_x, in_y)
        return in_x and in_y
    # 检测移动
    def isPressed(self, pictureList):
        # print('is_pressed')
        if self.isOver():
            b1, b2, b3 = pygame.mouse.get_pressed()
            if b1 == 1:
                global firstClickCell
                global successFlag

                if firstClickCell is None:
                    firstClickCell = self
                    print('id为{}的块被点击'.format(firstClickCell.getId()))
                else:
                    print('交换{}与{}的坐标'.format(firstClickCell.getId(), self.getId()))
                    self.pictureSwitch(firstClickCell, self)
                    if self.isFinish(pictureList):
                        print('成功!')
                        successFlag = True
                    else:
                        successFlag = False
                    firstClickCell = None
                return True
        return False
    # 判断拼图完成
    def isFinish(self, pictureList):
        for cell in  pictureList:
            nowp_x, nowp_y = cell.getXY()
            p_x = cell.getId() % COL * CELL_WIDTH
            p_y = (cell.getId() // COL) * CELL_HEIGHT
            print("id{} nowx{}与nowy{}的坐标 本来的坐标x{}y{}".format(cell.getId(), nowp_x, nowp_y, p_x, p_y))
            if nowp_x != p_x or nowp_y != p_y:
                return False
        return True

    def pictureSwitch(self, cell1, cell2):
        tempX = cell1.getX()
        tempY = cell1.getY()
        cell1.setXY(cell2.getX(), cell2.getY())
        cell2.setXY(tempX, tempY)
    def render(self, screen):
        screen.blit(self.img, (self.x, self.y))
    # get和set方法
    def getX(self):
        return self.x;
    def getY(self):
        return self.y
    # 获取图片坐标
    def getXY(self):
        return self.x, self.y
    # 修改图片坐标
    def setXY(self,x, y):
        self.x = x
        self.y = y
    # 获取id
    def getId(self):
        return self.id
# 图片分块的方法
def devide(imgSrc):
    # 切割原来的图片
    pictures = []
    ScPictures = []
    id = 0 # 给每个分块的图片设置下标
    for i in range(ROW):
        for j in range(COL):
            # 提取部分图片
            partOfPicutre = imgSrc.subsurface(j * CELL_WIDTH, i * CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT)
            # 保存第一组图片
            tempPicture = PartPicture(partOfPicutre, j * CELL_WIDTH, i * CELL_HEIGHT, id)
            pictures.append(tempPicture)
            # 保存第二组图片
            tempPicture = PartPicture(partOfPicutre, j * CELL_WIDTH, i * CELL_HEIGHT, id)
            ScPictures.append(tempPicture)
            id += 1
    random.shuffle(pictures)
    # 开始利用第二组图片来打乱原来的图片
    for i in range(len(pictures)):
        pictures[i].setXY(ScPictures[i].getX(), ScPictures[i].getY())
    return pictures # 把打乱的图片返回

pygame.init()  # 初始化pygame
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))  # 创建了一个窗口 大小是(宽,高)
pygame.display.set_caption('交换式拼图')  # 设置窗口标题
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
myfont = pygame.font.Font(None, 70)
textImage1 = myfont.render("simple", True , GREEN)
textImage2 = myfont.render("difficult", True , BLUE)
textImage3 = myfont.render("bow!!!", True , RED)
setInit = False
while True:
    if setInit:
        break
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()  # 接收到退出事件后退出程序
        elif event.type == MOUSEBUTTONDOWN:
            b1, b2, b3 = pygame.mouse.get_pressed()
            if b1 == 1:
                point_x, point_y = pygame.mouse.get_pos()  # 返回鼠标当前坐标
                if 100 < point_y < 250:
                    ROW = 4
                    COL = 4
                    setInit = True
                elif 250 < point_y < 400:
                    ROW = 10
                    COL = 10
                    setInit = True
                elif 400 < point_y < 550:
                    ROW = 100
                    COL = 100
                    setInit = True
    screen.fill((0,0,0))
    screen.blit(textImage1, (200, 100))
    screen.blit(textImage2, (200, 250))
    screen.blit(textImage3, (200, 400))
    pygame.display.update()
CELL_WIDTH = SCREEN_WIDTH / COL
CELL_HEIGHT = SCREEN_HEIGHT / ROW
imgSrc = pygame.image.load('src.jpg').convert()  # 原图 提高 blit 的速度 convert_alpha相对于convert,保留了图像的Alpha 通道信息,可以认为是保留了透明的部分,实现了透明转换
PictureList = devide(imgSrc)

# 标记是否游戏成功
successFlag = False

while True:  # 游戏主循环
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()  # 接收到退出事件后退出程序
        elif event.type == MOUSEBUTTONDOWN:
            for cell in PictureList:
                # 检测按键按下,并且交换图片位置
                if cell.isPressed(PictureList):
                    break
    for partPicture in PictureList:
        partPicture.render(screen)
    if not successFlag:
        # # Sta 绘制分割线
        for i in range(1, COL):
            pygame.draw.lines(screen, GREEN, 0, [(i * SCREEN_WIDTH // COL, 0), (i * SCREEN_WIDTH // COL, SCREEN_HEIGHT)], 1)
        for i in range(1, ROW):
            pygame.draw.lines(screen, GREEN, 0, [(0, i * SCREEN_HEIGHT // ROW), (SCREEN_WIDTH, i * SCREEN_HEIGHT // ROW)], 1)
        # End 绘制分割线
    pygame.display.update()  # 刷新一下画面

attached pictureattached picture

Guess you like

Origin blog.csdn.net/Fly_the_start/article/details/116839430