需要帮助提高PyGame中有关点击的性能和响应能力(MOUSEBUTTONDOWN)

2024-09-30 01:33:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用PyGame在python内部重新创建国际象棋,目前,我正在研究能够移动棋子的机制。我选择从编写骑士的移动脚本开始,因为它的移动是所有棋子中最复杂的。我已经成功地创建了一个系统,玩家可以选择棋子并相应地将其移动到网格上的任何位置。如果玩家最终决定不移动棋子,他们也可以取消选择棋子。我还没有把骑士所有可能的移动方式都编码好;只有几个;但这里的主要问题是反应极其缓慢。我的意思是,我必须点击多次才能得到一个被“选中”或未被选中的工件,并且点击更多次才能移动到所需的位置。我认为这与在更新中调用函数有关,该函数涉及使用for循环遍历pygame的事件来检测鼠标按下事件,但我不完全确定。如果您能帮助我们改进回复,我们将不胜感激!注意:我正在将棋子的代码导入主游戏程序,并将主代码导入Knight类中的select方法,以访问一些重要变量,如pGrid和方法,如player_updategrid()

主要游戏逻辑:

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

Tags: andinimportselfeventforifcolumn
2条回答

我建议您通过以下方式减少代码中嵌套循环的数量:

  • 只更新所移动工件的矩形(之前和之后),因此需要两个变量来存储选定工件的旧位置和新位置。这样可以减少更新过程中的迭代次数
  • 移除你用来知道有哪种颜色的矩阵(你可以保留它,你想设置它并放置其他块的位置,这样你就不会把一块放在另一块上面-相同颜色-),但是要选择颜色,你可以使用位置(x和y)示例:if x+y %2 == 0 这意味着它是白色的else然后是另一种颜色
  • 如果你想避免在新旧位置上使用这两个变量,你可以使用一个电路板的图像来代替,并将其作为带有透明背景的PNG图像,然后在另一个图像上闪烁一个图像将比在每个单元的所有外壳上循环快得多
  • 降低执行速度的一个因素是,您多次导入了main,并在先前执行的循环之外重新启动了一个新的循环(如果您的主游戏逻辑文件是main.py)

我希望有帮助。 One-tipp:现在不要忘记添加友好片段的案例,以免重做部分代码

问题是您正在处理Knight.move()函数中Knight的用户输入事件。这是而不是应该去的地方。最好只有一个地方来处理用户输入,然后根据当时游戏的状态来决定输入的位置。这段代码的一个好地方是主循环

目前,事件仅在调用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

回到主事件循环:

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

    ...

这使得骑士精灵变得简单了一点。它只需要有一个imagerect,还需要为这种类型的工件实现各种规则

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

相关问题 更多 >

    热门问题