有没有一种有效的方法来制作拖放多个png的功能?

yoy*_*123 4 python pygame chess

我正在制作一个国际象棋游戏,但我完全停留在拖放元素上,那里有一些指南,但它们要么拖动形状,要么只拖动一个图像。

我已经尝试了几种代码变体,但都是 50 多行只是为了移动一个,.png而且大多数都非常低效

pygame.init()

pygame.display.set_caption("Python Chess")

clock = pygame.time.Clock()
red = (213,43,67)
chew = pygame.image.load("chew.png")

gameDisplay.fill(red)
gameDisplay.blit(chew, (400, 400))
pygame.display.update()

drag = 0
if pygame.MOUSEBUTTONDOWN:
    drag = 1
if pygame.MOUSEBUTTONUP:
    drag = 0

gameExit = False

while not gameExit:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            gameExit = True
Run Code Online (Sandbox Code Playgroud)

图像根本不会拖动。

slo*_*oth 11

让我们一步一步地完成这个过程。

第 1 步:让我们从每个 pygame 游戏的基本骨架开始:

import pygame

def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill(pygame.Color('grey'))
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

我们创建一个窗口,然后启动一个循环来监听事件并绘制窗口。

在此处输入图片说明

到现在为止还挺好。这里没什么可看的,让我们继续。


第二步:棋盘

所以,我们想要一个国际象棋游戏。所以我们需要一个板子。我们创建一个列表列表来代表我们的板,我们创建一个Surface在屏幕上绘制我们的板。我们希望始终将游戏状态与实际绘图函数分开,因此我们创建了一个board变量和一个board_surf.

import pygame

TILESIZE = 32

def create_board_surf():
    board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
    dark = False
    for y in range(8):
        for x in range(8):
            rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
            pygame.draw.rect(board_surf, pygame.Color('black' if dark else 'white'), rect)
            dark = not dark
        dark = not dark
    return board_surf

def create_board():
    board = []
    for y in range(8):
        board.append([])
        for x in range(8):
            board[y].append(None)
    return board

def main():
    screen = pygame.display.set_mode((640, 480))
    board = create_board()
    board_surf = create_board_surf()
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill(pygame.Color('grey'))
        screen.blit(board_surf, (0, 0))
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


第 3 步:鼠标在哪里?

我们需要知道我们想要选择哪一块,所以我们必须将屏幕坐标鼠标相对于窗口的位置?)转换为世界坐标鼠标指向棋盘的哪个方块?)。

所以如果棋盘不在原点(位置(0, 0)),我们也必须考虑这个偏移

基本上,我们必须从鼠标位置(因此我们有鼠标相对于板的位置)中减去偏移量(即板在屏幕上的位置),然后除以方块的大小。

为了看看这是否有效,让我们在选定的正方形上绘制一个红色矩形。

import pygame

TILESIZE = 32
BOARD_POS = (10, 10)

def create_board_surf():
    board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
    dark = False
    for y in range(8):
        for x in range(8):
            rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
            pygame.draw.rect(board_surf, pygame.Color('black' if dark else 'white'), rect)
            dark = not dark
        dark = not dark
    return board_surf

def create_board():
    board = []
    for y in range(8):
        board.append([])
        for x in range(8):
            board[y].append(None)
    return board

def get_square_under_mouse(board):
    mouse_pos = pygame.Vector2(pygame.mouse.get_pos()) - BOARD_POS
    x, y = [int(v // TILESIZE) for v in mouse_pos]
    try: 
        if x >= 0 and y >= 0: return (board[y][x], x, y)
    except IndexError: pass
    return None, None, None

def main():
    screen = pygame.display.set_mode((640, 480))
    board = create_board()
    board_surf = create_board_surf()
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        piece, x, y = get_square_under_mouse(board)

        screen.fill(pygame.Color('grey'))
        screen.blit(board_surf, BOARD_POS)

        if x != None:
            rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
            pygame.draw.rect(screen, (255, 0, 0, 50), rect, 2)
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


第4步:让我们画一些碎片

国际象棋很无聊,没有一些棋子可以移动,所以让我们创造一些棋子。

我只是使用 aSysFont来绘制一些文本而不是使用真实图像,因此每个人都可以复制/粘贴代码并立即运行它。

我们(color, type)在嵌套board列表中存储一个元组。另外,让我们为我们的电路板使用一些其他颜色。

import pygame

TILESIZE = 32
BOARD_POS = (10, 10)

def create_board_surf():
    board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
    dark = False
    for y in range(8):
        for x in range(8):
            rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
            pygame.draw.rect(board_surf, pygame.Color('darkgrey' if dark else 'beige'), rect)
            dark = not dark
        dark = not dark
    return board_surf

def get_square_under_mouse(board):
    mouse_pos = pygame.Vector2(pygame.mouse.get_pos()) - BOARD_POS
    x, y = [int(v // TILESIZE) for v in mouse_pos]
    try: 
        if x >= 0 and y >= 0: return (board[y][x], x, y)
    except IndexError: pass
    return None, None, None

def create_board():
    board = []
    for y in range(8):
        board.append([])
        for x in range(8):
            board[y].append(None)

    for x in range(0, 8):
        board[1][x] = ('black', 'pawn')
    for x in range(0, 8):
        board[6][x] = ('white', 'pawn') 

    return board

def draw_pieces(screen, board, font):
    for y in range(8):
        for x in range(8): 
            piece = board[y][x]
            if piece:
                color, type = piece
                s1 = font.render(type[0], True, pygame.Color(color))
                s2 = font.render(type[0], True, pygame.Color('darkgrey'))
                pos = pygame.Rect(BOARD_POS[0] + x * TILESIZE+1, BOARD_POS[1] + y * TILESIZE + 1, TILESIZE, TILESIZE)
                screen.blit(s2, s2.get_rect(center=pos.center).move(1, 1))
                screen.blit(s1, s1.get_rect(center=pos.center))

def draw_selector(screen, piece, x, y):
    if piece != None:
        rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
        pygame.draw.rect(screen, (255, 0, 0, 50), rect, 2)

def main():
    pygame.init()
    font = pygame.font.SysFont('', 32)
    screen = pygame.display.set_mode((640, 480))
    board = create_board()
    board_surf = create_board_surf()
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        piece, x, y = get_square_under_mouse(board)

        screen.fill(pygame.Color('grey'))
        screen.blit(board_surf, BOARD_POS)
        draw_pieces(screen, board, font)
        draw_selector(screen, piece, x, y)

        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


第 5 步:拖放

对于拖放,我们需要做两件事:

  • 我们必须改变你的游戏状态(进入“拖动模式”)
  • 进入和离开“拖动模式”的事件处理

其实没那么复杂。要进入“拖动模式”,我们只需selected_pieceMOUSEBUTTONDOWN事件发生时设置一个变量 ( ) 。因为我们已经有了这个get_square_under_mouse函数,所以很容易知道鼠标光标下是否真的有一块。

如果selected_piece设置,我们在鼠标光标下画一条线和棋子,并在MOUSEBUTTONUP事件发生时跟踪光标下的当前方块。如果是这种情况,我们将在我们的board.

import pygame

TILESIZE = 32
BOARD_POS = (10, 10)

def create_board_surf():
    board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
    dark = False
    for y in range(8):
        for x in range(8):
            rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
            pygame.draw.rect(board_surf, pygame.Color('darkgrey' if dark else 'beige'), rect)
            dark = not dark
        dark = not dark
    return board_surf

def get_square_under_mouse(board):
    mouse_pos = pygame.Vector2(pygame.mouse.get_pos()) - BOARD_POS
    x, y = [int(v // TILESIZE) for v in mouse_pos]
    try: 
        if x >= 0 and y >= 0: return (board[y][x], x, y)
    except IndexError: pass
    return None, None, None

def create_board():
    board = []
    for y in range(8):
        board.append([])
        for x in range(8):
            board[y].append(None)

    for x in range(0, 8):
        board[1][x] = ('black', 'pawn')
    for x in range(0, 8):
        board[6][x] = ('white', 'pawn') 

    return board

def draw_pieces(screen, board, font, selected_piece):
    sx, sy = None, None
    if selected_piece:
        piece, sx, sy = selected_piece

    for y in range(8):
        for x in range(8): 
            piece = board[y][x]
            if piece:
                selected = x == sx and y == sy
                color, type = piece
                s1 = font.render(type[0], True, pygame.Color('red' if selected else color))
                s2 = font.render(type[0], True, pygame.Color('darkgrey'))
                pos = pygame.Rect(BOARD_POS[0] + x * TILESIZE+1, BOARD_POS[1] + y * TILESIZE + 1, TILESIZE, TILESIZE)
                screen.blit(s2, s2.get_rect(center=pos.center).move(1, 1))
                screen.blit(s1, s1.get_rect(center=pos.center))

def draw_selector(screen, piece, x, y):
    if piece != None:
        rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
        pygame.draw.rect(screen, (255, 0, 0, 50), rect, 2)

def draw_drag(screen, board, selected_piece, font):
    if selected_piece:
        piece, x, y = get_square_under_mouse(board)
        if x != None:
            rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
            pygame.draw.rect(screen, (0, 255, 0, 50), rect, 2)

        color, type = selected_piece[0]
        s1 = font.render(type[0], True, pygame.Color(color))
        s2 = font.render(type[0], True, pygame.Color('darkgrey'))
        pos = pygame.Vector2(pygame.mouse.get_pos())
        screen.blit(s2, s2.get_rect(center=pos + (1, 1)))
        screen.blit(s1, s1.get_rect(center=pos))
        selected_rect = pygame.Rect(BOARD_POS[0] + selected_piece[1] * TILESIZE, BOARD_POS[1] + selected_piece[2] * TILESIZE, TILESIZE, TILESIZE)
        pygame.draw.line(screen, pygame.Color('red'), selected_rect.center, pos)
        return (x, y)

def main():
    pygame.init()
    font = pygame.font.SysFont('', 32)
    screen = pygame.display.set_mode((640, 480))
    board = create_board()
    board_surf = create_board_surf()
    clock = pygame.time.Clock()
    selected_piece = None
    drop_pos = None
    while True:
        piece, x, y = get_square_under_mouse(board)
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == pygame.MOUSEBUTTONDOWN:
                if piece != None:
                    selected_piece = piece, x, y
            if e.type == pygame.MOUSEBUTTONUP:
                if drop_pos:
                    piece, old_x, old_y = selected_piece
                    board[old_y][old_x] = 0
                    new_x, new_y = drop_pos
                    board[new_y][new_x] = piece
                selected_piece = None
                drop_pos = None

        screen.fill(pygame.Color('grey'))
        screen.blit(board_surf, BOARD_POS)
        draw_pieces(screen, board, font, selected_piece)
        draw_selector(screen, piece, x, y)
        drop_pos = draw_drag(screen, board, selected_piece, font)

        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

当然,还有很多可以改进的地方(比如使用比元组更好的数据类型,将公共逻辑提取到函数中等),但这应该会给你一个关于如何实现这些东西的良好开端。

始终牢记:

  • 编写一个处理事件、游戏逻辑和绘图的游戏循环
  • 确保pygame.display.flip每帧只调用一次
  • 将您的游戏状态与您的绘图功能分开
  • 从不打电话time.sleeppygame.time.wait
  • 使用内置的像类Vector2Rect,它们会让你的生活更容易(我没有的Sprite类在此代码,但它也是非常有用)
  • 使用函数来清理你的代码
  • 避免使用全局变量,常量除外