Python 使用 Pygame 实现五子棋游戏完整教程
前言
本文详细介绍如何使用 Python 语言和 Pygame 图形库从零开发一个双人对战五子棋游戏。项目采用模块化设计,包含配置管理、棋盘绘制、信息面板、核心逻辑及主程序入口。通过本教程,读者可以掌握 Pygame 的基本窗口创建、事件监听、图像渲染以及简单的游戏状态管理逻辑。
本文介绍了如何使用 Python 和 Pygame 库从零开发一个双人对战五子棋游戏。项目包含棋盘绘制、落子逻辑、胜负判定及界面交互等核心模块。通过分文件管理配置、渲染、面板和逻辑函数,实现了清晰的项目结构。文章详细解析了坐标转换算法与四方向连珠检测逻辑,并提供了完整的代码示例及运行环境配置指南,适合希望学习游戏开发基础及 Pygame 框架的开发者参考。

本文详细介绍如何使用 Python 语言和 Pygame 图形库从零开发一个双人对战五子棋游戏。项目采用模块化设计,包含配置管理、棋盘绘制、信息面板、核心逻辑及主程序入口。通过本教程,读者可以掌握 Pygame 的基本窗口创建、事件监听、图像渲染以及简单的游戏状态管理逻辑。
在开始编写代码之前,请确保您的开发环境满足以下要求:
pygame 库。
在终端或命令行中执行以下命令进行安装:
pip install pygame
images 文件夹下:
checkerboard_bg.png:棋盘背景图black_chess.png:黑棋子图片(透明背景)white_chess.png:白棋子图片(透明背景)
注:若暂无图片,代码中可暂时注释掉图片加载部分,改用圆形绘制代替。建议将项目文件组织如下:
gobang_project/
├── images/
│ ├── checkerboard_bg.png
│ ├── black_chess.png
│ └── white_chess.png
├── settings.py # 全局配置与初始值
├── checkerboard.py # 棋盘绘制模块
├── infopanel.py # 右侧信息面板模块
├── game_functions.py # 游戏核心逻辑函数
└── gobang.py # 主程序入口
此文件用于集中管理游戏的各项参数,如屏幕尺寸、字体设置、棋盘规格等。修改此处即可快速调整游戏外观。
class Settings():
def __init__(self):
"""初始化的游戏配置"""
# 屏幕宽高
self.width = 700
self.height = 554
# 文字颜色和大小
self.fontsize = 14
self.fonttype = 'simsun' # 建议使用系统支持的字体,如 simsun 或 Arial
# 棋盘格数 (15x15)
self.number = 15
# 棋盘左边距、上边距和间隔
self.bd_left = 30
self.bd_top = 30
self.bd_space = 36
# 判断游戏是否结束(默认开始)
self.game_active = True
# 判断哪方下棋(默认黑子先写,1 为黑,-1 为白)
self.chess_player = 1
self.prompt_info = '当前棋手:黑棋'
# 开始校验输赢(两边合计 9,因为已经有一边 5 步)
self.win_number = 0
# 设置背景图、黑棋图片、白棋图片路径
self.checkerboard_bg = 'images/checkerboard_bg.png'
self.black_chess = 'images/black_chess.png'
self.white_chess = 'images/white_chess.png'
# 存储落子数据
self.move_chess = []
负责加载资源并在屏幕上绘制棋盘网格、坐标轴及棋子图像。使用了 pygame.image.load 加载图片,并使用 convert_alpha() 处理透明通道。
import sys
import pygame
class Checkerboard():
def __init__(self, ck_settings, screen, position):
self.ck_settings = ck_settings
self.screen = screen
self.position = position
# 颜色和坐标大小
self.text_color = (0, 0, 0)
self.font = pygame.font.SysFont(ck_settings.fonttype, ck_settings.fontsize)
# 存储棋子坐标
self.checkerboard = []
# 加载背景图、黑棋和白棋(当有图片不存在时,打印错误并退出游戏)
try:
self.bg_image = pygame.image.load(ck_settings.checkerboard_bg)
self.black_image = pygame.image.load(ck_settings.black_chess).convert_alpha() # convert_alpha 背景透明
self.white_image = pygame.image.load(ck_settings.white_chess).convert_alpha()
self.chess_rect = self.black_image.get_rect()
except Exception as e:
print('error:', e)
sys.exit()
def draw_board(self):
# 存储棋子坐标
for i in range(self.ck_settings.number):
self.checkerboard.append([])
for j in range(self.ck_settings.number):
self.checkerboard[i].append(self.position(
self.ck_settings.bd_left + i * self.ck_settings.bd_space,
self.ck_settings.bd_top + j * self.ck_settings.bd_space
))
# 绘制棋盘坐标
for i in range(0, self.ck_settings.number):
# ord 返回字符的 ASCII 数值,chr 再返回字符
x_text = self.font.render(chr(ord('A') + i), True, self.text_color) # A-O
y_text = self.font.render(str(i + 1), True, self.text_color) # 1-15
# 绘制 xy 轴坐标(在棋盘背景图绘制)
self.bg_image.blit(x_text, (
self.checkerboard[i][0].x - x_text.get_width() / 2,
self.checkerboard[i][0].y - 20
))
self.bg_image.blit(y_text, (
self.checkerboard[0][i].x - 20,
self.checkerboard[0][i].y - y_text.get_height() / 2
))
# 绘制横竖线(在棋盘背景图绘制)
pygame.draw.line(self.bg_image, self.text_color,
self.checkerboard[0][i],
self.checkerboard[self.ck_settings.number-1][i])
pygame.draw.line(self.bg_image, self.text_color,
self.checkerboard[i][0],
self.checkerboard[i][self.ck_settings.number-1])
# 绘制棋盘背景图到屏幕
self.screen.blit(self.bg_image, (0, 0))
位于屏幕右侧,用于显示当前执棋方、提示信息或获胜宣言。使用 render 方法将文本转换为图像表面。
import pygame.font
class Infopanel():
def __init__(self, ck_settings, screen):
"""初始化属性"""
self.settings = ck_settings
self.screen = screen
self.screen_rect = screen.get_rect()
# 设置文字颜色和字体大小
self.info_color = (217, 8, 10)
self.font = pygame.font.SysFont(ck_settings.fonttype, 16)
def draw_info(self, info):
"""将文字渲染为图像,并定位到右边水平居中"""
self.info_image = self.font.render(info, True, self.info_color)
self.info_image_rect = self.info_image.get_rect()
# 计算右侧居中位置
self.info_image_rect.right = self.screen_rect.right - (self.screen_rect.width - 536 - self.info_image_rect.width) / 2
self.info_image_rect.top = 50
# 绘制到屏幕
self.screen.blit(self.info_image, self.info_image_rect)
这是游戏的核心,包含了落子更新、位置检查、胜负判定及事件监听。
update_board 函数负责切换棋手颜色、记录落子坐标并触发胜负检测。
import sys
import pygame
# 棋
def update_board(ck_settings, cb, index_coordinates, position):
"""更新棋盘信息"""
# 判断棋手(黑棋或白棋)
if ck_settings.chess_player == 1:
ck_settings.prompt_info = '当前棋手:白棋'
img = cb.black_image
chess_type = 'black'
else:
ck_settings.prompt_info = '当前棋手:黑棋'
img = cb.white_image
chess_type = 'white'
"""落棋"""
dropState = check_at(ck_settings, index_coordinates)
if dropState:
i, j = index_coordinates
chess_x = cb.checkerboard[j][i].x - cb.chess_rect.width / 2
chess_y = cb.checkerboard[j][i].y - cb.chess_rect.height / 2
# 累计步数(两边合计)
ck_settings.win_number += 1
# 落子并转换棋手
ck_settings.move_chess.append({'type': chess_type, 'coord': position(i, j)})
cb.bg_image.blit(img, (chess_x, chess_y))
ck_settings.chess_player *= -1
# 合计 9 步开始校验输赢
if ck_settings.win_number >= 9:
check_stats(ck_settings, (i, j))
else:
ck_settings.prompt_info = '已经有其他棋子'
check_at 函数遍历已落子列表,防止重复落子。
# 检查 (i,j) 位置是否已占用
def check_at(ck_settings, index_coordinates):
for item in ck_settings.move_chess:
if index_coordinates == item['coord']:
return False
return True
check_stats 函数是算法核心。它以后一手棋为中心,向四个方向(横、竖、斜、反斜)延伸,统计连续同色棋子数量。如果达到 5 个则判定获胜。
def check_stats(ck_settings, pos):
"""校验四个方向,是否有了输赢"""
pos_i, pos_j = pos
directs = [(1, 0), (0, 1), (1, 1), (1, -1)] # 横、竖、斜、反斜 四个方向检查
for direct in directs:
line_checkerboard = []
d_x, d_y = direct
last = ck_settings.move_chess[-1]
line_ball = [] # 存放在一条线上的棋子
for ball in ck_settings.move_chess:
# 跟最后落子判断
if ball['type'] == last['type']:
x = ball['coord'].x - last['coord'].x
y = ball['coord'].y - last['coord'].y
if d_x == 0:
if x == 0:
line_ball.append(ball['coord'])
if d_y == 0:
if y == 0:
line_ball.append(ball['coord'])
if x * d_y == y * d_x:
line_ball.append(ball['coord'])
if len(line_ball) >= 5: # 只有 5 子及以上才继续判断
sorted_line = sorted(line_ball)
for i, item in enumerate(sorted_line):
index = i + 4
if index < len(sorted_line):
if d_x == 0:
y1 = item.y
y2 = sorted_line[index].y
# 此点和第 5 个点比较 y 值,如相差为 4 则连成 5 子
if abs(y1 - y2) == 4:
ck_settings.prompt_info = '黑棋获胜' if last['type'] == 'black' else '白棋获胜'
else:
x1 = item.x
x2 = sorted_line[index].x
# 此点和第 5 个点比较 x 值,如相差为 4 则连成 5 子
if abs(x1 - x2) == 4:
ck_settings.prompt_info = '黑棋获胜' if last['type'] == 'black' else '白棋获胜'
else:
break
check_events 监听鼠标点击和窗口关闭事件。to_index 将屏幕像素坐标转换为棋盘网格索引。
# 事件
def check_events(ck_settings, cb, position):
"""监听事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# 点击左键
if event.button == 1:
pos = pygame.mouse.get_pos() # 获取点击实际坐标
# 判断是否溢出
x_first = cb.checkerboard[0][0].x
x_last = cb.checkerboard[ck_settings.number - 1][ck_settings.number - 1].x
y_first = cb.checkerboard[0][0].y
y_last = cb.checkerboard[ck_settings.number - 1][ck_settings.number - 1].y
if pos[0] < x_first or pos[0] > x_last or pos[1] < y_first or pos[1] > y_last:
ck_settings.prompt_info = '落子位置不正确!'
else:
index_coordinates = to_index(ck_settings, pos)
update_board(ck_settings, cb, index_coordinates, position)
def to_index(ck_settings, pos):
"""实际坐标转换为棋盘下标"""
i = round((pos[1] - ck_settings.bd_top) / ck_settings.bd_space)
j = round((pos[0] - ck_settings.bd_left) / ck_settings.bd_space)
return (i, j)
整合所有模块,初始化 Pygame,进入主循环。
import pygame
from settings import Settings
from checkerboard import Checkerboard
from collections import namedtuple
import game_functions as gf
from infopanel import Infopanel
def run_game():
"""运行游戏"""
# 初始化游戏屏幕
pygame.init()
# 创建时钟对象 (可以控制游戏循环频率)
clock = pygame.time.Clock()
# 配置实例化
ck_settings = Settings()
screen = pygame.display.set_mode((ck_settings.width, ck_settings.height))
pygame.display.set_caption('五子棋游戏')
# namedtuple 创建类似于元组的数据类型,除了可以用索引访问,能够迭代,还能用属性名访问数据
position = namedtuple('Position', ['x', 'y'])
# 创建实例
cb = Checkerboard(ck_settings, screen, position)
# 实例化面板信息
infopanel = Infopanel(ck_settings, screen)
while ck_settings.game_active:
# 绘制棋盘
cb.draw_board()
# 绘制面板信息
infopanel.draw_info(ck_settings.prompt_info)
# 检查玩家事件并更新棋盘
gf.check_events(ck_settings, cb, position)
# 让最近绘制的屏幕可见
pygame.display.flip()
# 通过时钟对象指定循环频率
clock.tick(60) # 每秒循环 60 次
if __name__ == '__main__':
run_game()
images 文件夹存在。python gobang.py
images 文件夹路径是否正确,以及文件名是否完全匹配(包括后缀)。settings.py 中的 fonttype 为系统通用的字体名称,如 'Arial' 或 'SimHei'。check_events 中的坐标转换逻辑,确保鼠标坐标与棋盘边界匹配。当前版本实现了基础的双人对战功能,后续可根据兴趣添加以下特性:
本教程通过分模块的方式,清晰地展示了使用 Python 和 Pygame 开发桌面小游戏的全过程。从配置管理到核心算法,每个环节都经过详细拆解。希望读者能通过此案例,进一步探索游戏开发的更多可能性,如物理引擎集成、网络联机等功能。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online