基于Pygame实现2048小游戏

embedded/2025/3/19 3:08:11/

目录

1. 项目概述

2. 开发环境

3. 核心代码

3.1 网格初始化

3.2 数字生成逻辑

3.3 移动与合并算法

4. 图形界面设计

4.1 颜色方案

4.2 界面元素

4.3 动画效果

5. 代码结构说明

6. 完整代码

7. 总结与扩展


1. 项目概述

2048是一款风靡全球的数字益智游戏,玩家通过滑动方向键合并相同数字,最终目标是合成"2048"这个数字。本文将使用Python的Pygame库实现该游戏的图形化版本,包含分数统计、动画效果和游戏结束判断等核心功能。

2. 开发环境

  • Python 3.8+

  • Pygame 2.0+

  • 任意代码编辑器(推荐VS Code/PyCharm)

安装依赖:

pip install pygame

3. 核心代码

游戏关键的代码如下

3.1 网格初始化

使用4x4二维数组存储数字信息,0表示空位:

self.grid = [[0]*4 for _ in range(4)]

3.2 数字生成逻辑

每次操作后在随机空位生成2或4:

def add_new_number(self):empty_cells = [(i,j) for i in range(4) for j in range(4) if self.grid[i][j] == 0]if empty_cells:i, j = random.choice(empty_cells)self.grid[i][j] = 2 if random.random() < 0.9 else 4

3.3 移动与合并算法

以左移为例的合并逻辑:

def move_row_left(row):# 移除0并合并相邻相同数字new_row = [i for i in row if i != 0]for i in range(len(new_row)-1):if new_row[i] == new_row[i+1]:new_row[i] *= 2new_row[i+1] = 0self.score += new_row[i]new_row = [i for i in new_row if i != 0]return new_row + [0]*(4-len(new_row))

其他方向通过矩阵转置/反转复用左移逻辑。

4. 图形界面设计

4.1 颜色方案

可以自行更换

COLORS = {0: (204, 192, 179),2: (238, 228, 218),4: (237, 224, 200),8: (242, 177, 121),16: (245, 149, 99),32: (246, 124, 95),64: (246, 94, 59),128: (237, 207, 114),256: (237, 204, 97),512: (237, 200, 80),1024: (237, 197, 63),2048: (237, 194, 46),
}

4.2 界面元素

  • 600x700像素窗口

  • 网格间距10像素

  • 分数显示区域

  • 数字块渐变色设计

# 常量配置
WIDTH = 450
HEIGHT = 550
TILE_SIZE = 100
PADDING = 10

4.3 动画效果

使用线性插值实现滑动动画:

def draw_tiles(self):for i in range(4):for j in range(4):value = self.grid[i][j]if value != 0:# 计算动画位置x = j*TILE_SIZE + (j+1)*PADDINGy = i*TILE_SIZE + (i+1)*PADDINGpygame.draw.rect(screen, COLORS[value], (x,y,TILE_SIZE,TILE_SIZE))# 绘制数字...

5. 代码结构说明

  • Game2048类:封装游戏逻辑

    • move():处理移动操作

    • is_game_over():判断游戏结束

    • reset():重置游戏状态

  • 主循环:处理事件和渲染

6. 完整代码

方向键控制移动,R键重新开始,ESC退出游戏。当网格填满且无法合并时游戏结束。

python">import pygame
import random
import sys# 常量配置
WIDTH = 450
HEIGHT = 550
TILE_SIZE = 100
PADDING = 10
COLORS = {0: (204, 192, 179),2: (238, 228, 218),4: (237, 224, 200),8: (242, 177, 121),16: (245, 149, 99),32: (246, 124, 95),64: (246, 94, 59),128: (237, 207, 114),256: (237, 204, 97),512: (237, 200, 80),1024: (237, 197, 63),2048: (237, 194, 46),
}class Game2048:def __init__(self):self.grid = [[0] * 4 for _ in range(4)]self.score = 0self.high_score = 0self.add_new_number()self.add_new_number()def add_new_number(self):empty_cells = [(i, j) for i in range(4) for j in range(4) if self.grid[i][j] == 0]if empty_cells:i, j = random.choice(empty_cells)self.grid[i][j] = 2 if random.random() < 0.9 else 4def move(self, direction):moved = Falseoriginal_grid = [row.copy() for row in self.grid]if direction == 'left':for i in range(4):new_row = self.move_row_left(self.grid[i])if new_row != self.grid[i]:moved = Trueself.grid[i] = new_rowelif direction == 'right':for i in range(4):new_row = self.move_row_right(self.grid[i])if new_row != self.grid[i]:moved = Trueself.grid[i] = new_rowelif direction == 'up':self.grid = [list(row) for row in zip(*self.grid)]for i in range(4):new_row = self.move_row_left(self.grid[i])if new_row != self.grid[i]:moved = Trueself.grid[i] = new_rowself.grid = [list(row) for row in zip(*self.grid)]elif direction == 'down':self.grid = [list(row) for row in zip(*self.grid)]for i in range(4):new_row = self.move_row_right(self.grid[i])if new_row != self.grid[i]:moved = Trueself.grid[i] = new_rowself.grid = [list(row) for row in zip(*self.grid)]if moved:self.add_new_number()self.high_score = max(self.high_score, self.score)return moveddef move_row_left(self, row):new_row = [num for num in row if num != 0]for i in range(len(new_row) - 1):if new_row[i] == new_row[i + 1]:new_row[i] *= 2self.score += new_row[i]new_row[i + 1] = 0new_row = [num for num in new_row if num != 0]return new_row + [0] * (4 - len(new_row))def move_row_right(self, row):return self.move_row_left(row[::-1])[::-1]def is_game_over(self):# 检查空单元格if any(0 in row for row in self.grid):return False# 检查水平方向可合并for i in range(4):for j in range(3):if self.grid[i][j] == self.grid[i][j + 1]:return False# 检查垂直方向可合并for j in range(4):for i in range(3):if self.grid[i][j] == self.grid[i + 1][j]:return Falsereturn Truedef draw_game(screen, game, font):screen.fill((187, 173, 160))# 绘制分数text = font.render(f"Score: {game.score}  High Score: {game.high_score}", True, (119, 110, 101))screen.blit(text, (20, 20))# 绘制网格for i in range(4):for j in range(4):value = game.grid[i][j]x = j * (TILE_SIZE + PADDING) + PADDINGy = i * (TILE_SIZE + PADDING) + 80pygame.draw.rect(screen, COLORS.get(value, (0, 0, 0)),(x, y, TILE_SIZE, TILE_SIZE))if value != 0:text_surface = font.render(str(value), True, (119, 110, 101))text_rect = text_surface.get_rect(center=(x + TILE_SIZE // 2, y + TILE_SIZE // 2))screen.blit(text_surface, text_rect)# 游戏结束提示if game.is_game_over():overlay = pygame.Surface((WIDTH, HEIGHT - 80), pygame.SRCALPHA)overlay.fill((255, 255, 255, 128))text = font.render("Game Over! Press R to restart", True, (255, 0, 0))text_rect = text.get_rect(center=(WIDTH // 2, (HEIGHT - 80) // 2 + 40))overlay.blit(text, text_rect)screen.blit(overlay, (0, 80))def main():pygame.init()screen = pygame.display.set_mode((WIDTH, HEIGHT))pygame.display.set_caption("2048")clock = pygame.time.Clock()font = pygame.font.Font(None, 36)game = Game2048()while True:draw_game(screen, game, font)for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()sys.exit()if event.type == pygame.KEYDOWN:if event.key == pygame.K_ESCAPE:pygame.quit()sys.exit()elif event.key == pygame.K_r:game = Game2048()elif event.key == pygame.K_LEFT:game.move('left')elif event.key == pygame.K_RIGHT:game.move('right')elif event.key == pygame.K_UP:game.move('up')elif event.key == pygame.K_DOWN:game.move('down')pygame.display.flip()clock.tick(60)if __name__ == "__main__":main()

7. 总结与扩展

本实现完整还原了2048的核心玩法,可以通过以下方式扩展:

  1. 添加音效和更流畅的动画

  2. 实现撤销功能

  3. 增加AI自动求解模块

  4. 支持不同尺寸的网格(5x5、6x6)

通过这个项目,可以学习到:

  • Pygame的基本使用

  • 二维数组操作技巧

  • 游戏状态管理

  • 方向键事件处理


http://www.ppmy.cn/embedded/173748.html

相关文章

SpringData Redis缓存:自定义序列化与过期策略

文章目录 引言一、Spring Cache与Redis集成基础二、Redis缓存配置基础三、自定义序列化策略四、实现自定义序列化器五、多级缓存配置六、自定义过期策略七、缓存注解的高级应用八、实现缓存预热与更新策略九、缓存监控与统计总结 引言 在现代高并发分布式系统中&#xff0c;缓…

记录一次wifi版有人物联串口服务器桥接网络调试经过

目前的项目想法是将一台设备IP192.168.3.56的设备通过网口发给串口服务器&#xff0c;然后串口服务器通过桥接&#xff0c;将这个数据通过wifi路由器转发给另外一台设备IP为192.168.3.17&#xff0c;其中串口服务器的IP为192.168.3.16&#xff0c;wifi路由器组成的局域网的网管…

【操作系统安全】任务4:Windows 系统网络安全实践里常用 DOS 命令

目录 一、引言 二、网络信息收集类命令 2.1 ipconfig 命令 2.1.1 功能概述 2.1.2 实例与代码 2.2 ping 命令 2.2.1 功能概述 2.2.2 实例与代码 2.3 tracert 命令 2.3.1 功能概述 2.3.2 实例与代码 三、网络连接与端口管理类命令 3.1 netstat 命令 3.1.1 功能概述…

Webpack 和 Vite 的主要区别

Webpack 和 Vite 的主要区别&#xff0c;从构建机制、开发体验、生产优化等多个维度进行对比&#xff1a; 1. 构建机制与速度 Webpack 全量打包&#xff1a;启动时必须分析所有模块依赖关系&#xff0c;进行全量打包&#xff0c;生成 Bundle 文件。项目越大&#xff0c;冷启动时…

16、JavaEE核心技术-EL与 JSTL

EL与 JSTL 实践 一. EL&#xff08;Expression Language&#xff09; EL&#xff08;表达式语言&#xff09;是 JSP 2.0 中引入的一种简单的脚本语言&#xff0c;用于在 JSP 页面中简化数据的访问和显示。它通过一种类似于 JavaScript 的语法&#xff0c;允许开发者在 JSP 页面…

六十天前端强化训练之第二十二天之React 框架 15天深度学习总结(大师版)

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 一、React 核心架构思想&#xff08;深度解析&#xff09; 1.1 组件化思维革命 1.2 虚拟DOM的智慧 二、关键技术深度剖析 2.1 JSX 本质揭秘 2.2 Hooks 设计哲学 三、企业…

vue computed 计算属性简述

Vue 的 ‌计算属性&#xff08;Computed Properties&#xff09;‌ 是 Vue 实例中一种特殊的属性&#xff0c;用于‌声明式地定义依赖其他数据动态计算得出的值‌。它的核心优势在于能够自动追踪依赖关系&#xff0c;并缓存计算结果&#xff0c;避免重复计算&#xff0c;提升性…

如何学习VBA_3.2.20:DTP与Datepicker实现日期的输入

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的劳动效率&#xff0c;而且可以提高数据处理的准确度。我推出的VBA系列教程共九套和一部VBA汉英手册&#xff0c;现在已经全部完成&#xff0c;希望大家利用、学习。 如果…