Нужна помощь в повышении производительности и скорости отклика PyGame в отношении кликов (MOUSEBUTTONDOWN) - PullRequest
1 голос
/ 09 июля 2020

Я пытаюсь воссоздать шахматы внутри python с помощью PyGame, и в настоящее время я работаю над механикой, позволяющей перемещать фигуры. Я решил начать с написания сценария движения коня, так как это движение является самым сложным из всех шахматных фигур. Я успешно создал систему, в которой игрок может выбрать фигуру и переместить ее в любое место на сетке. Игрок также может отменить выбор фишки, если он решит не перемещать ее в конце. Я не закодировал все возможные способы передвижения коня; их всего пара; но основная проблема здесь в том, что ответы очень медленные. Я имею в виду, что мне приходится нажимать несколько раз, чтобы кусок был «выбран» или чтобы его не выделяли, и еще несколько раз, чтобы переместиться в желаемое место. Я думаю, что это как-то связано с вызовом функции в обновлении, которая включает использование for l oop для циклического перебора событий pygame для обнаружения событий нажатия мыши, но я не совсем уверен. Любая помощь по улучшению ответов будет принята с благодарностью! Примечание: я импортирую код для фигур в основную программу игры, а также импортирую основной код в методы выбора в классе Knight для доступа к некоторым важным переменным, таким как pGrid, и таким методам, как player_updategrid()

Основное логирование игры c:

import pygame
import pieces
import itertools
import numpy as np


#2050/8 = 256.25
#2050/960 = 2.135
WINWIDTH = 960
WINHEIGHT = 960
WHITE = (200,200,200)
LIGHTTAN = (247, 245, 218)
BROWN = (158, 114, 73)

pygame.init()
win = pygame.display.set_mode((WINWIDTH,WINHEIGHT))
pygame.display.set_caption('Chess')
run = True
bPieces = pygame.sprite.Group()
wPieces = pygame.sprite.Group()
bKnight = pieces.knight(win, 7, 7)
bPieces.add(bKnight)
pgrid = []

#SET UP THE BOARD
board = np.ones((3,3))
board = np.zeros((8,8),dtype=int)
board[1::2,::2] = 1
board[::2,1::2] = 1
def bgupdategrid():
    for row in range(0,8):
        for column in range(0,8):
            if board[row][column] == 1:
                pygame.draw.rect(win, (LIGHTTAN),
                                    (((column * (WINWIDTH) // 8)), ((row * (WINWIDTH) // 8))
                                    , (WINWIDTH - 24) // 8, (WINHEIGHT - 24) // 8))
            else:
                pygame.draw.rect(win, (BROWN),
                                 (((column * (WINWIDTH) // 8)), ((row * (WINWIDTH) // 8))
                                  , (WINWIDTH - 24) // 8, (WINHEIGHT - 24) // 8))


def player_updategrid(pos = None):
    for row in range(0,8):
        pgrid.append([])
        for column in range(0,8):

            pgrid[row].append(0)
            if pgrid[row][column] == 1 and pos == 'y':
                return ((column * (WINWIDTH) // 8) + column * 2)
            elif pgrid[row][column] == 1 and pos == 'x':
                return ((row * (WINWIDTH) // 8) + row * 2)

while run:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False


    #UPDATE BOARD UI
    bgupdategrid()




    #UPDATE PIECE DISPLAY



    bPieces.draw(win)
    player_updategrid()
    bPieces.update()
    pygame.display.flip()
pygame.quit()

Код для Фигуры рыцаря:

import pygame

class knight(pygame.sprite.Sprite):
    def __init__(self, surface, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.surface = surface
        self.image = pygame.image.load("D:\PythonProjects\Chess\Assets/blackKnight.png")
        self.image = pygame.transform.scale(self.image, (120,120))
        self.rect = pygame.Rect(x, y, 120,120)  #self.image.get_rect()
        self.x = x
        self.y = y
        self.selected = False
    def update(self):
        import main as m
        self.move()
        m.pgrid[self.x][self.y] = 1
        self.rect.x = m.player_updategrid('x')
        self.rect.y = m.player_updategrid('y')


    def getmouse(self):
        getmouse = pygame.mouse.get_pos()
        mposx = getmouse[0]
        mposy = getmouse[1]
        return mposx//120, mposy//120
    def move(self):
        import main as m
        mposx, mposy = self.getmouse()
        #print(self.selected)
        if mposy == self.y and mposx == self.x and self.selected is False:
            for event in pygame.event.get():
                if self.selected == False:
                    if event.type == pygame.MOUSEBUTTONDOWN:
                        print('selected')
                        self.selected = True
        elif mposy == self.y and mposx == self.x and self.selected == True:
            #print('yoyoyoyo')
            for event in pygame.event.get():
                if event.type == pygame.MOUSEBUTTONDOWN:
                    print('unselected')
                    self.selected = False
        elif mposy != self.y and mposx != self.x and self.selected is True:
                for event in pygame.event.get():
                    print(mposx, mposy)
                    if event.type == pygame.MOUSEBUTTONDOWN:
                        if (((self.x - 2) == mposx) and ((self.y - 1) == mposy)) or (((self.x - 2) == mposx) and ((self.y - 1) == mposy)):
                            #print('hi')
                            m.pgrid[self.x][self.y] = 0
                            self.x = mposx
                            self.y = mposy
                            self.selected = False

Ответы [ 2 ]

2 голосов
/ 09 июля 2020

Проблема в том, что вы обрабатываете событие пользовательского ввода для Knight в функции Knight.move(). Это , а не , где должно go. Лучше всего иметь только одно место для обработки пользовательского ввода, а затем решить, где этот ввод должен быть go, исходя из состояния игры в данный момент. Хорошее место для этого кода - основной l oop.

В настоящее время события проверяются только в течение доли секунды, в которую вызывается функция Knight.move(), поэтому, вероятно, большую часть времени , события не обрабатываются, и это затрудняет щелчок. Вы действительно не хотите, чтобы обработка ввода игроком распространялась на 6 различных реализаций типов шахматных фигур.

Итак ... код нуждается в значительной реструктуризации. Но сначала давайте добавим несколько вспомогательных функций:

Добавьте второй 2D-список, чтобы сохранить логическую позицию всех ваших частей. Как насчет того, чтобы он содержал None для пустой ячейки или ссылку на объект Sprite (например: Knight), если эта ячейка занята. Например:

# Create an empty board
board_pieces = []
for i in range( 8 ):
    board_pieces.append( [None] * 8 )

Возможно, он у вас уже есть, но я его не видел. Затем мы напишем функцию, которая может выполнять щелчок мышью и определять, был ли этот щелчок на части. Но сначала нам нужно преобразовать координаты мыши в координаты платы и обратно.

def windowToBoardCoord( win_x, win_y ):
    """ Convert the window co-ordinates to board co-ordinates """
    board_x = win_x // ( WINDOW_WIDTH  // 8 )
    board_y = win_y // ( WINDOW_HEIGHT // 8 )
    # Debugging, comment out later when it all works
    print( "Window (%d,%d) => Board (%d,%d)" % ( win_x, win_y, board_x, board_y ) )
    return board_x, board_y

def boardToWindowCoord( board_x, board_y ):
    """ Convert the board co-ordinates to window co-ordinates """
    # NOTE: returns the top-left corner
    win_x = board_x * 8
    win_y = board_y * 8
    # Debugging, comment out later when it all works
    print( "Board (%d,%d) => Window (%d,%d)" % ( board_x, board_y, win_x, win_y ) )
    return win_x, win_y

def getPieceAt( board, board_x, board_y ):
    """ Get the piece on the board, or None """
    any_piece = board[board_y][board_x]    # could be None
    # Debugging, comment out later when it all works
    if ( any_piece == None ):
        print( "No piece at board[%d][%d]" % ( board_y, board_x ) )
    else:
        print( "Piece [%s] is at board[%d][%d]here" % ( str( any_piece ), board_y, board_x  ) )
    return any_piece

Это позволяет нам видеть щелчок мышью, получать позицию мыши, а затем определять, где это щелкнуло по доске.

Мы добавим переменную с именем current_selection, чтобы сохранить выбранную в данный момент фишку игрока, которая изначально равна None.

Итак, вернемся к основному событию l oop:

current_selection = None     # any piece the player has selected

while run:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.MOUSEBUTTONDOWN:   # user clicked the mouse
            mouse_x, mouse_y = event.pos             # where was it clicked
            board_x, board_y = windowToBoardCoord( mouse_x, mouse_y )

            # Is there a piece at those co-ordinates?
            piece = getPieceAt( board_pieces, board_x, board_y )

            # If the player has already selected a piece, this is a move
            # to that new location
            if ( current_selected != None ):
                if ( piece == current_selected ):
                    # clicked on the same piece twice, un-select it
                    current_selected = None
                else:
                    # This is a move, but is it valid?
                    if ( current_selected.isLegalMove( board_pieces, board_x, board_y )
                        # Valid move
                        # Update the board ( maybe this should be a function )
                        current_x, current_y = current_selected.getBoardPosition()
                        board[ current_y ][ current_x ] = None
                        board[ board_y ][ board_x ]     = current_selected
                        # Update the screen
                        current_selected.moveTo( board_x, board_y )
                        # ... handle taking pieces, etc. TODO  
            elif ( piece != None ):
                # Nothing selected, is this a new selection
                if ( piece.isOwnedByPlayer() ):
                    current_selected = piece
                else:
                    # can't select opponents pieces
                    makeErrorBeep()
                 

    #UPDATE BOARD UI
    bgupdategrid()

    ...

Это оставляет Knight Sprite немного проще. Он просто должен иметь image и rect, но также реализовывать различные правила для этого типа фигур.

class Knight( pygame.sprite.Sprite ):
    def __init__( self, x, y, colour ):
        pygame.sprite.Sprite.__init__(self)
        self.name = 'Knight'
        # TODO: use os.path.join() here ~
        self.image = pygame.image.load("D:\PythonProjects\Chess\Assets/blackKnight.png")
        self.image = pygame.transform.scale(self.image, (120,120))
        self.rect  = self.image.get_rect()
        self.rect.topleft = ( x, y )

    def update(self):
        # image does not change (yet)
        pass

    def moveTo( self, board_x, board_y ):
        win_x, win_y = boardToWindowCoord( board_x, board_y )
        self.rect.topleft = ( win_x, win_y )

    def getBoardPosition( self ):
        x, y = self.rect.topleft
        board_x, board_y = windowToBoardCoord( x, y )
        return board_x, board_y

    def isLegalMove( self, board, move_x, move_y ):
        """ Is it a legal move from current-position to (x,y) """
        board_x, board_y = self.getBoardPostion()

        # TODO: check all those L-shaped movements
        #       Can we move from (board_x, board_y) to (move_x, move_y)
        result = True   # TODO: just accept any movement for testing
        return result
0 голосов
/ 09 июля 2020

Я предлагаю вам уменьшить количество вложенных циклов в вашем коде следующим образом:

  • обновлять только прямоугольник перемещенного фрагмента (до и после), поэтому вам понадобятся 2 переменные для хранения старая и новая позиция выбранной фигуры. так что вы уменьшите количество итераций во время обновления.
  • удалите матрицу, которую вы использовали, чтобы знать, какой цвет есть (вы можете сохранить ее, если хотите настроить, и расположите другие части, чтобы вы не t положить кусок поверх другого - того же цвета-), но чтобы выбрать цвет, вы можете использовать пример положения (x и y): if x+y %2 == 0 это означает, что он белый, а else, то это другой цвет.
  • Если вы хотите избежать использования двух переменных для старой и новой позиций, вы можете вместо этого использовать изображение доски и получить части в виде изображений PNG с прозрачным фоном, а наложение изображения поверх другого будет намного быстрее чем цикл по всем случаям каждой ячейки
  • Одна вещь, которая снижает скорость выполнения, - это тот факт, что вы импортировали основное несколько раз и перезапустили новый l oop в дополнение к l oop ранее выполненный (в случае, если ваш файл Main Game Logi c был main.py)

Надеюсь, это поможет. Один совет : не забудьте пока добавить ящики с дружественными фигурами, чтобы не переделывать части вашего кода

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...