📜  井字游戏 GUI 在Python中使用 PyGame

📅  最后修改于: 2022-05-13 01:54:25.413000             🧑  作者: Mango

井字游戏 GUI 在Python中使用 PyGame

这篇文章将指导您并让您了解使用Python的pygame库设计井字游戏的基本思路。 Pygame 是一组用于编写视频游戏的跨平台Python模块。它包括旨在与Python编程语言一起使用的计算机图形和声音库。

让我们将任务分为五个部分:

  1. 导入所需的库并设置所需的全局变量。
  2. 设计游戏显示函数,为其他组件在屏幕上显示设置平台。
  3. 主要的获胜和平局算法
  4. 获取用户输入并在用户单击鼠标的适当位置显示“X”或“O”。
  5. 运行一个无限循环,并在其中包含定义的方法。

注意:所需的PNG文件可以在下面下载——

修改封面.png

X_modified.png

o_modified.png

导入所需的库并设置所需的全局变量

我们将使用Python的pygametimesys库。时间库用于跟踪我们将在代码中使用的时间和sleep()方法。看看下面的代码。

# importing the required libraries
import pygame as pg
import sys
import time
from pygame.locals import *
   
# declaring the global variables
  
# for storing the 'x' or 'o' 
# value as character
XO = 'x'
  
# storing the winner's value at
# any instant of code
winner = None
  
# to check if the game is a draw
draw = None
  
# to set width of the game window
width = 400
  
# to set height of the game window
height = 400
  
# to set background color of the 
# game window
white = (255, 255, 255)
  
# color of the straightlines on that 
# white game board, dividing board 
# into 9 parts
line_color = (0, 0, 0)
   
# setting up a 3 * 3 board in canvas
board = [[None]*3, [None]*3, [None]*3]

设计游戏画面

这是比较棘手的部分,它在游戏开发中至关重要。我们可以使用display.set_mode()方法来设置我们的显示窗口。这需要三个参数,第一个是具有我们想要的显示的(宽度,高度)的元组,另外两个参数分别是深度和 fps。 display.set_caption() ,在我们的显示器的名称标签上设置一个标题。 pg.image.load()是加载背景图像以自定义显示的有用方法。此方法将文件名与扩展名一起作为参数。 image.load()有一个小问题,它将图像加载为其原生大小的Python对象,这可能不会与显示一起优化。所以我们在 pygame 中使用另一种方法,称为pg.transform.scale() 。这个方法有两个参数,一个是图像对象的名称,另一个是一个具有 (width, height) 的元组,我们希望我们的图像可以缩放到。

最后我们转到第一个函数game_initiating_window() 。在第一行有一个screen.blit()函数。 screen 是Python函数,而 blit 是使 pygame 能够在另一事物上显示某些内容的方法。此处的图像对象已显示在屏幕上,最初设置为白色。 pg.display.update()是游戏开发中的另一个重要函数。它在调用时更新我们窗口的显示。 Pygame 还使我们能够绘制直线、圆等几何对象。在这个项目中,我们使用了 pg.draw.line()方法,该方法接受五个参数,即 - (显示、线条颜色、起点、终点、宽度) .这涉及到一点坐标几何来正确绘制线条。

这还不够。在每次更新显示时,我们都需要知道游戏状态,天气是输是赢。 draw_status()帮助我们在主窗口底部显示另一个 100pc 窗口,在用户每次点击时更新状态。

# initializing the pygame window
pg.init()
  
# setting fps manually
fps = 30
  
# this is used to track time
CLOCK = pg.time.Clock()
  
# this method is used to build the
# infrastructure of the display
screen = pg.display.set_mode((width, height + 100), 0, 32)
  
# setting up a nametag for the 
# game window
pg.display.set_caption("My Tic Tac Toe")
   
# loading the images as python object
initiating_window = pg.image.load("modified_cover.png")
x_img = pg.image.load("X_modified.png")
y_img = pg.image.load("o_modified.png")
   
# resizing images
initiating_window = pg.transform.scale(initiating_window, (width, height + 100))
x_img = pg.transform.scale(x_img, (80, 80))
o_img = pg.transform.scale(y_img, (80, 80))
   
def game_initiating_window():
      
    # displaying over the screen
    screen.blit(initiating_window, (0, 0))
      
    # updating the display
    pg.display.update()
    time.sleep(3)                    
    screen.fill(white)
   
    # drawing vertical lines
    pg.draw.line(screen, line_color, (width / 3, 0), (width / 3, height), 7)
    pg.draw.line(screen, line_color, (width / 3 * 2, 0), (width / 3 * 2, height), 7)
   
    # drawing horizontal lines
    pg.draw.line(screen, line_color, (0, height / 3), (width, height / 3), 7)
    pg.draw.line(screen, line_color, (0, height / 3 * 2), (width, height / 3 * 2), 7)
    draw_status()
   
def draw_status():
      
    # getting the global variable draw
    # into action
    global draw
      
    if winner is None:
        message = XO.upper() + "'s Turn"
    else:
        message = winner.upper() + " won !"
    if draw:
        message = "Game Draw !"
   
    # setting a font object
    font = pg.font.Font(None, 30)
      
    # setting the font properties like 
    # color and width of the text
    text = font.render(message, 1, (255, 255, 255))
   
    # copy the rendered message onto the board
    # creating a small block at the bottom of the main display
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect(center =(width / 2, 500-50))
    screen.blit(text, text_rect)
    pg.display.update()

主要算法

主要算法有一个直接的方法。用户可以按行、按列和对角获胜。因此,通过使用多维数组,我们可以轻松设置条件。

def check_win():
    global board, winner, draw
   
    # checking for winning rows
    for row in range(0, 3):
        if((board[row][0] == board[row][1] == board[row][2]) and (board [row][0] is not None)):
            winner = board[row][0]
            pg.draw.line(screen, (250, 0, 0),
                         (0, (row + 1)*height / 3 -height / 6),
                         (width, (row + 1)*height / 3 - height / 6 ),
                         4)
            break
   
    # checking for winning columns
    for col in range(0, 3):
        if((board[0][col] == board[1][col] == board[2][col]) and (board[0][col] is not None)):
            winner = board[0][col]
            pg.draw.line (screen, (250, 0, 0), ((col + 1)* width / 3 - width / 6, 0), \
                          ((col + 1)* width / 3 - width / 6, height), 4)
            break
   
    # check for diagonal winners
    if (board[0][0] == board[1][1] == board[2][2]) and (board[0][0] is not None):
          
        # game won diagonally left to right
        winner = board[0][0]
        pg.draw.line (screen, (250, 70, 70), (50, 50), (350, 350), 4)
          
    if (board[0][2] == board[1][1] == board[2][0]) and (board[0][2] is not None):
          
        # game won diagonally right to left
        winner = board[0][2]
        pg.draw.line (screen, (250, 70, 70), (350, 50), (50, 350), 4)
   
    if(all([all(row) for row in board]) and winner is None ):
        draw = True
  
    draw_status()

获取用户输入并显示“X”或“O”

这部分处理电路板的可视化和一些坐标几何。 drawXO()接受两个参数 row 和 col。首先,我们必须设置正确的几何位置,将我们存储的 X 图像和 O 图像分别存储为两个Python对象“x_img”和“y_img”。查看代码以获得正确的理解。

user_click()是我们设计的一个函数,用于从用户鼠标单击中获取输入。想象一下,您点击了九个部分之一(框除以我们绘制的水平和垂直线),该函数将定义您单击的位置的坐标。 pg.mouse.get_pos()获取用户鼠标点击的x坐标和y坐标,并返回一个元组。根据 (x, y),我们可以定义用户单击的确切行和确切列。最后,当我们有 row 和 col 时,我们将这两个作为参数传递给函数drawXO(row, col)以在游戏中用户所需的位置绘制“X”或“O”的图像屏幕。

def drawXO(row, col):
    global board, XO
      
    # for the first row, the image
    # should be pasted at a x coordinate
    # of 30 from the left margin
    if row == 1:
        posx = 30
          
    # for the second row, the image 
    # should be pasted at a x coordinate 
    # of 30 from the game line     
    if row == 2:
  
        # margin or width / 3 + 30 from 
        # the left margin of the window
        posx = width / 3 + 30
          
    if row == 3:
        posx = width / 3 * 2 + 30
   
    if col == 1:
        posy = 30
          
    if col == 2:
        posy = height / 3 + 30
      
    if col == 3:
        posy = height / 3 * 2 + 30
          
    # setting up the required board 
    # value to display
    board[row-1][col-1] = XO
      
    if(XO == 'x'):
          
        # pasting x_img over the screen 
        # at a coordinate position of
        # (pos_y, posx) defined in the
        # above code
        screen.blit(x_img, (posy, posx))
        XO = 'o'
      
    else:
        screen.blit(o_img, (posy, posx))
        XO = 'x'
    pg.display.update()
   
def user_click():
    # get coordinates of mouse click
    x, y = pg.mouse.get_pos()
   
    # get column of mouse click (1-3)
    if(x

运行无限循环

这是无限运行我们的游戏直到用户点击退出的最后一个重要步骤。在运行无限循环之前,我们需要设置一个函数,该函数可以将所有全局值和参数重置为初始值,以便重新开始游戏。
reset_game()用于此目的。它再次将板值重置为 3 * 3 None 值并初始化全局参数。

在游戏开发中,玩家的每一个动作都是一个事件。他是点击窗口还是点击退出/关闭图标。要将这些事件作为对象获取,pygame 有一个用作pg.event.get()的内置方法。如果事件类型为“QUIT”,我们使用Python的 sys 库退出游戏。但是如果按下鼠标, event.get()将返回“MOUSEBUTTONDOWN”,而我们对user_click()的调用恰好知道用户点击的棋盘的确切坐标。

在整个代码中,我们使用了.sleep()方法来暂停我们的游戏,让游戏变得友好和流畅。

def reset_game():
    global board, winner, XO, draw
    time.sleep(3)
    XO = 'x'
    draw = False
    game_initiating_window()
    winner = None
    board = [[None]*3, [None]*3, [None]*3]
   
game_initiating_window()
   
while(True):
    for event in pg.event.get():
  
        if event.type == QUIT:
            pg.quit()
            sys.exit()
  
        elif event.type is MOUSEBUTTONDOWN:
            user_click()
  
            if(winner or draw):
                reset_game()
  
    pg.display.update()
    CLOCK.tick(fps)

完整代码:

# importing the required libraries
import pygame as pg
import sys
import time
from pygame.locals import *
   
# declaring the global variables
  
# for storing the 'x' or 'o' 
# value as character
XO = 'x'
  
# storing the winner's value at
# any instant of code
winner = None
  
# to check if the game is a draw
draw = None
  
# to set width of the game window
width = 400
  
# to set height of the game window
height = 400
  
# to set background color of the 
# game window
white = (255, 255, 255)
  
# color of the straightlines on that 
# white game board, dividing board 
# into 9 parts
line_color = (0, 0, 0)
   
# setting up a 3 * 3 board in canvas
board = [[None]*3, [None]*3, [None]*3]
  
  
# initializing the pygame window
pg.init()
  
# setting fps manually
fps = 30
  
# this is used to track time
CLOCK = pg.time.Clock()
  
# this method is used to build the
# infrastructure of the display
screen = pg.display.set_mode((width, height + 100), 0, 32)
  
# setting up a nametag for the 
# game window
pg.display.set_caption("My Tic Tac Toe")
   
# loading the images as python object
initiating_window = pg.image.load("modified_cover.png")
x_img = pg.image.load("X_modified.png")
y_img = pg.image.load("o_modified.png")
   
# resizing images
initiating_window = pg.transform.scale(initiating_window, (width, height + 100))
x_img = pg.transform.scale(x_img, (80, 80))
o_img = pg.transform.scale(y_img, (80, 80))
   
def game_initiating_window():
      
    # displaying over the screen
    screen.blit(initiating_window, (0, 0))
      
    # updating the display
    pg.display.update()
    time.sleep(3)                    
    screen.fill(white)
   
    # drawing vertical lines
    pg.draw.line(screen, line_color, (width / 3, 0), (width / 3, height), 7)
    pg.draw.line(screen, line_color, (width / 3 * 2, 0), (width / 3 * 2, height), 7)
   
    # drawing horizontal lines
    pg.draw.line(screen, line_color, (0, height / 3), (width, height / 3), 7)
    pg.draw.line(screen, line_color, (0, height / 3 * 2), (width, height / 3 * 2), 7)
    draw_status()
   
def draw_status():
      
    # getting the global variable draw
    # into action
    global draw
      
    if winner is None:
        message = XO.upper() + "'s Turn"
    else:
        message = winner.upper() + " won !"
    if draw:
        message = "Game Draw !"
   
    # setting a font object
    font = pg.font.Font(None, 30)
      
    # setting the font properties like 
    # color and width of the text
    text = font.render(message, 1, (255, 255, 255))
   
    # copy the rendered message onto the board
    # creating a small block at the bottom of the main display
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect(center =(width / 2, 500-50))
    screen.blit(text, text_rect)
    pg.display.update()
      
def check_win():
    global board, winner, draw
   
    # checking for winning rows
    for row in range(0, 3):
        if((board[row][0] == board[row][1] == board[row][2]) and (board [row][0] is not None)):
            winner = board[row][0]
            pg.draw.line(screen, (250, 0, 0),
                         (0, (row + 1)*height / 3 -height / 6),
                         (width, (row + 1)*height / 3 - height / 6 ),
                         4)
            break
   
    # checking for winning columns
    for col in range(0, 3):
        if((board[0][col] == board[1][col] == board[2][col]) and (board[0][col] is not None)):
            winner = board[0][col]
            pg.draw.line (screen, (250, 0, 0), ((col + 1)* width / 3 - width / 6, 0), \
                          ((col + 1)* width / 3 - width / 6, height), 4)
            break
   
    # check for diagonal winners
    if (board[0][0] == board[1][1] == board[2][2]) and (board[0][0] is not None):
          
        # game won diagonally left to right
        winner = board[0][0]
        pg.draw.line (screen, (250, 70, 70), (50, 50), (350, 350), 4)
          
    if (board[0][2] == board[1][1] == board[2][0]) and (board[0][2] is not None):
          
        # game won diagonally right to left
        winner = board[0][2]
        pg.draw.line (screen, (250, 70, 70), (350, 50), (50, 350), 4)
   
    if(all([all(row) for row in board]) and winner is None ):
        draw = True
    draw_status()
      
def drawXO(row, col):
    global board, XO
      
    # for the first row, the image
    # should be pasted at a x coordinate
    # of 30 from the left margin
    if row == 1:
        posx = 30
          
    # for the second row, the image 
    # should be pasted at a x coordinate 
    # of 30 from the game line     
    if row == 2:
  
        # margin or width / 3 + 30 from 
        # the left margin of the window
        posx = width / 3 + 30
          
    if row == 3:
        posx = width / 3 * 2 + 30
   
    if col == 1:
        posy = 30
          
    if col == 2:
        posy = height / 3 + 30
      
    if col == 3:
        posy = height / 3 * 2 + 30
          
    # setting up the required board 
    # value to display
    board[row-1][col-1] = XO
      
    if(XO == 'x'):
          
        # pasting x_img over the screen 
        # at a coordinate position of
        # (pos_y, posx) defined in the
        # above code
        screen.blit(x_img, (posy, posx))
        XO = 'o'
      
    else:
        screen.blit(o_img, (posy, posx))
        XO = 'x'
    pg.display.update()
   
def user_click():
    # get coordinates of mouse click
    x, y = pg.mouse.get_pos()
   
    # get column of mouse click (1-3)
    if(x

输出: