You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在基于Pygame的基础国际象棋引擎中实现拖拽功能

问题描述

我正在用Pygame开发国际象棋引擎作为个人项目,之前跟着教程学了基础功能,现在想优化。目前已实现:

  • 棋盘显示、棋子初始布局
  • 按Z键撤销上一步操作
  • 点击选中棋子后点击目标位置完成移动(但无任何规则校验,比如首回合能用兵吃己方国王、不强制玩家交替回合)
  • 仅关闭窗口可结束游戏

现在想给棋子添加拖拽功能,但一直没实现,找不到贴合现有项目结构的教程——网上的示例都是极简main.py里的Rect对象操作,没有类似我的代码里的设计约束。我的代码已经把玩家操作管理和显示逻辑分离开,希望能在尽可能保留这个结构的前提下实现拖拽功能,或者说明为什么不可行。

编辑说明:我已经尽可能简化了代码,ChessMain_2_simple.py不能再精简了,因为我想明确看到拖拽功能如何和现有的点击选子落子逻辑共存。


ChessMain_2_simple.py

"""
This is our main driver file. It will be responsible for handling user input and displaying the current GameState object.
"""

import pygame as p
import ChessEngine_2_simple

WIDTH = HEIGHT = 512 #400 is another option
DIMENSION = 4
SQ_SIZE = HEIGHT // DIMENSION
MAX_FPS = 15 #for animations later on
IMAGES = {} #dictionnary of images


"""
Initialize a global dictionnary of images. It will be called exactly once in the main
"""
def loadImage():
    pieces = ['bP', 'wP']
    for piece in pieces:
        unscaledImage = p.image.load('images/' + piece + '.png')
        IMAGES[piece] = p.transform.scale(unscaledImage, (SQ_SIZE, SQ_SIZE))
    #note: now can access an image by saying "IMAGES['bP']"


"""
The main driver for our code. This will handle user input and update graphics
"""
def main():
    p.init()
    screen = p.display.set_mode((WIDTH, HEIGHT))
    clock = p.time.Clock()
    screen.fill(p.Color('white'))
    gs = ChessEngine_2_simple.GameState()
    loadImage() #only do this once before the while loop

    running = True
    sqSelected = () #no square is selected initially, keep track of the last click of the user (tuple: (row, col))
    playerClicks = [] #keep track of player clicks (two tuples: [(6, 4), (4, 4)] )
    while running:
        for e in p.event.get():
            if e.type == p.QUIT:
                running = False
            elif e.type == p.MOUSEBUTTONDOWN:
                location = p.mouse.get_pos() #x, y) location of the mouse
                #so make sure to take it in account when adding a side panel
                col = location[0] // SQ_SIZE
                row = location[1] // SQ_SIZE
                if sqSelected == (row, col): #the user clicked the same square twice
                    sqSelected = () #deselect
                    playerClicks = [] #clear the player clicks
                else:
                    sqSelected = (row, col)
                    playerClicks.append(sqSelected) #append for both 1st and 2nd clicks
                if len(playerClicks) == 2: #after 2nd click
                    move = ChessEngine_2_simple.Move(playerClicks[0], playerClicks[1], gs.board)
                    gs.makeMove(move)
                    sqSelected = () #reset user clicks
                    playerClicks = []

        drawGameState(screen, gs)
        clock.tick(MAX_FPS)
        p.display.flip()


"""
Responsible for all the graphics within the current game state.
"""
def drawGameState(screen, gs):
    drawBoard(screen) #draw squares on the board
    #add in piece highlighting or move suggestions (later)
    drawPiece(screen, gs.board) #draw pieces on top of those squares

"""
Draw the squares on the board.
"""
def drawBoard(screen):
    colors = [p.Color('antiquewhite1'), p.Color('burlywood3')]
    for r in range(DIMENSION):
        for c in range(DIMENSION):
            color = colors[((r+c) % 2)]
            rectToDraw = p.Rect(c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE) #position then dimensions
            p.draw.rect(screen, color, rectToDraw)


"""
Draw the pieces on the board using the current GameState.board
"""
def drawPiece(screen, board):
    for r in range(DIMENSION):
        for c in range(DIMENSION):
            piece = board[r][c]
            if piece != '--': #not empty square
                rectWhereToDrawPiece = p.Rect(c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE) #position then dimensions
                screen.blit(IMAGES[piece], rectWhereToDrawPiece)


if __name__ == '__main__': #google to understand why that "if" statement
    main()

ChessEngine_2_simple.py

"""
This class is responsible for storing all the information about the current state of a chess game. It will also be responsible
for determining the valid moves at the current state. It will also keep a move log.
"""

class GameState():
    def __init__(self):
        #Board is 4x4 2d list, each element of the list has 2 characters
        #The first character represent the color of the piece: 'b' or 'w'
        #The second character represent the type of the piece: 'P'
        #'--' represents an empty square with no piece
        self.board = [
            ["bP", "bP", "bP", "bP"],
            ["--", "--", "--", "--"],
            ["--", "--", "--", "--"],
            ["wP", "wP", "wP", "wP"],
        ]

    def makeMove(self, move):
        self.board[move.startRow][move.startCol] = '--' #clear the square from which the piece has moved
        self.board[move.endRow][move.endCol] = move.pieceMoved #we've stored what pîece was moved in Move instance before overwrite its square content in Board instance


class Move():
    def __init__(self, startSq, endSq, board):
        self.startRow = startSq[0]
        self.startCol = startSq[1]
        self.endRow = endSq[0]
        self.endCol = endSq[1]
        self.pieceMoved = board[self.startRow][self.startCol]
        self.pieceCaptured = board[self.endRow][self.endCol]

棋子图片

  • 黑兵:黑兵
  • 白兵:白兵

内容的提问来源于stack exchange,提问作者coding_chicken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:07:08