基于Pygame实现2048小游戏

目录

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] *= 2
            new_row[i+1] = 0
            self.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)*PADDING
                y = i*TILE_SIZE + (i+1)*PADDING
                pygame.draw.rect(screen, COLORS[value], (x,y,TILE_SIZE,TILE_SIZE))
                # 绘制数字...

5. 代码结构说明

  • Game2048类:封装游戏逻辑

    • move():处理移动操作

    • is_game_over():判断游戏结束

    • reset():重置游戏状态

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

6. 完整代码

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

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 = 0
        self.high_score = 0
        self.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 4

    def move(self, direction):
        moved = False
        original_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 = True
                self.grid[i] = new_row
        elif direction == 'right':
            for i in range(4):
                new_row = self.move_row_right(self.grid[i])
                if new_row != self.grid[i]:
                    moved = True
                self.grid[i] = new_row
        elif 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 = True
                self.grid[i] = new_row
            self.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 = True
                self.grid[i] = new_row
            self.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 moved

    def 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] *= 2
                self.score += new_row[i]
                new_row[i + 1] = 0
        new_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 False

        return True


def 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) + PADDING
            y = i * (TILE_SIZE + PADDING) + 80
            pygame.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的基本使用

  • 二维数组操作技巧

  • 游戏状态管理

  • 方向键事件处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听风吹等浪起

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值