有没有一种有效的方法来制作拖放多个png的功能? [英] Is there an effiecient way of making a function to drag and drop multiple png's?

查看:24
本文介绍了有没有一种有效的方法来制作拖放多个png的功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm making a chess game, but I'm completely stuck on the drag and drop element, there's a few guides out there but they're all either dragging shapes, or only dragging one image.

我尝试了多种代码变体,但为了移动一个.png,所有代码都超过了 50 行,而且大多数代码效率低得令人难以置信

I've tried several variants of code, but all were 50+ lines just to move one .png and most were incredibly inefficient

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

图像根本不会拖动.

推荐答案

让我们一步一步来.

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()

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

We create a window and then start a loop to listen for events and drawing the window.

到目前为止,一切都很好.这里没什么可看的,让我们继续.

So far, so good. Nothing to see here, let's move on.

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

So, we want a chess game. So we need a board. We create a list of lists to represent our board, and we create a Surface that draws our board on the screen. We want to always seperate our game's state from the actual drawing functions, so we create a board variable and a 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()

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

We need to know which piece we want to select, so we have to translate the screen coordinates (where's the mouse relative to the window?) to the world coordinates (which square of the board is the mouse pointing to?).

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

So if the board is not located at the origin (the position (0, 0)), we also have to take this offset into account.

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

Basically, we have to substract that offset (which is the position of the board on the screen) from the mouse position (so we have the mouse position relative to the board), and divide by the size of the squares.

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

To see if this works, let's draw a red rectangle on the selected square.

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()

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

Chess is boring without some pieces to move around, so let's create some pieces.

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

I just use a SysFont to draw some text instead of using real images, so everyone can just copy/paste the code and run it immediately.

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

We store a tuple (color, type) in the nested board list. Also, let's use some other colors for our 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()

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

For drag and drop we need two things:

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

其实没那么复杂.要进入拖动模式",我们只需在 MOUSEBUTTONDOWN 事件发生时设置一个变量(selected_piece).由于我们已经有了 get_square_under_mouse 函数,所以很容易知道鼠标光标下是否真的有一块.

It's actually not that complicated. To enter the "drag-mode", we just set a variable (selected_piece) when the MOUSEBUTTONDOWN event occurs. Since we already have the get_square_under_mouse function, it's easy to know if there's actually a piece under the mouse cursor.

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

if selected_piece is set, we draw a line and the piece under the mouse cursor, and we keep track of the current square under the cursor in case the MOUSEBUTTONUP event occurs. If that's the case, we swap the position of the piece in our 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()

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

Of course there's a lot that can be improved (like using better datatypes than tuples, extract common logic into functions etc), but this should give you a good start on how to implement such things.

永远记住:

  • 编写一个处理事件、游戏逻辑和绘图的游戏循环
  • 确保每帧只调用一次 pygame.display.flip
  • 将游戏状态与绘图功能分开
  • 永远不要调用 time.sleeppygame.time.wait
  • 使用像Vector2Rect 这样的内置类,它们会让你的生活更轻松(我没有使用Sprite 类在这段代码中,但它也非常有用)
  • 使用函数来清理你的代码
  • 避免使用全局变量,常量除外
  • write a single game loop that handles events, game logic, and drawing
  • make sure to only call pygame.display.flip once per frame
  • seperate your game state from your drawing functions
  • never call time.sleep or pygame.time.wait
  • use the build-in classes like Vector2 and Rect, they'll make your live easier (I didn't the Sprite class in this code, but it's also very usefull)
  • use functions to clean up your code
  • avoid global variables, except for constants

这篇关于有没有一种有效的方法来制作拖放多个png的功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆