python制作2048游戏

import pygame
import random
import sys
import os
import math

初始化 Pygame

pygame.init()
pygame.mixer.init() # 初始化音效系统

游戏常量

WIDTH, HEIGHT = 500, 650
GRID_SIZE = 4
CELL_SIZE = 100
GRID_PADDING = 15
TITLE_FONT_SIZE = 56
FONT_SIZE = 40
SMALL_FONT_SIZE = 20
SCORE_FONT_SIZE = 24

颜色定义 - 使用更现代的配色方案

BACKGROUND_COLOR = (250, 248, 239)
GRID_COLOR = (187, 173, 160)
EMPTY_CELL_COLOR = (205, 193, 180)
TEXT_COLOR = (119, 110, 101)
BRIGHT_TEXT_COLOR = (249, 246, 242)
TITLE_COLOR = (119, 110, 101)
SCORE_BG_COLOR = (187, 173, 160)
SCORE_TEXT_COLOR = (238, 228, 218)
RESTART_BG_COLOR = (143, 122, 102)
RESTART_HOVER_COLOR = (165, 144, 125)
RESTART_TEXT_COLOR = (255, 255, 255)
GAME_OVER_OVERLAY = (0, 0, 0, 180) # RGBA with transparency
WIN_OVERLAY = (255, 215, 0, 180) # RGBA with transparency

方块颜色映射 - 使用更现代的配色

TILE_COLORS = {
0: (205, 193, 180),
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),
128: (237, 207, 114),
256: (237, 204, 97),
512: (237, 200, 80),
1024: (237, 197, 63),
2048: (237, 194, 46),
4096: (60, 58, 50),
8192: (60, 58, 50),
16384: (60, 58, 50)
}

文字颜色映射

TEXT_COLORS = {
2: TEXT_COLOR,
4: TEXT_COLOR,
8: BRIGHT_TEXT_COLOR,
16: BRIGHT_TEXT_COLOR,
32: BRIGHT_TEXT_COLOR,
64: BRIGHT_TEXT_COLOR,
128: BRIGHT_TEXT_COLOR,
256: BRIGHT_TEXT_COLOR,
512: BRIGHT_TEXT_COLOR,
1024: BRIGHT_TEXT_COLOR,
2048: BRIGHT_TEXT_COLOR,
4096: BRIGHT_TEXT_COLOR,
8192: BRIGHT_TEXT_COLOR,
16384: BRIGHT_TEXT_COLOR
}

创建游戏窗口

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("2048 游戏")

加载中文字体

def load_font(size):
"""尝试加载中文字体,失败则使用默认"""
try:
return pygame.font.Font("SourceHanSansSC-Bold.otf", size)
except:
try:
return pygame.font.Font("C:/Windows/Fonts/simhei.ttf", size)
except:
return pygame.font.SysFont(["SimHei", "WenQuanYi Micro Hei", "Heiti TC"], size)

创建字体

title_font = load_font(TITLE_FONT_SIZE)
font = load_font(FONT_SIZE)
small_font = load_font(SMALL_FONT_SIZE)
score_font = load_font(SCORE_FONT_SIZE)

class Game2048:
def init(self):
self.grid = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
self.score = 0
self.high_score = 0
self.game_over = False
self.won = False
# 确保音效在游戏开始时就被加载
self.load_sounds()
self.add_new_tile()
self.add_new_tile()

def load_sounds(self):
    """加载游戏音效"""
    try:
        self.sounds = {
            'move': pygame.mixer.Sound('move.wav'),
            'merge': pygame.mixer.Sound('merge.wav'),
            'win': pygame.mixer.Sound('win.wav'),
            'game_over': pygame.mixer.Sound('game_over.wav'),
            'new_tile': pygame.mixer.Sound('new_tile.wav')
        }
        # 设置音量
        for sound in self.sounds.values():
            sound.set_volume(0.5)
    except:
        # 如果音效加载失败,创建空对象
        class DummySound:
            def play(self): pass
            def set_volume(self, vol): pass
        self.sounds = {name: DummySound() for name in ['move', 'merge', 'win', 'game_over', 'new_tile']}

def add_new_tile(self):
    """在空白位置随机添加一个新方块(90%概率为2,10%概率为4)"""
    empty_cells = []
    for i in range(GRID_SIZE):
        for j in range(GRID_SIZE):
            if self.grid[i][j] == 0:
                empty_cells.append((i, j))
    
    if empty_cells:
        i, j = random.choice(empty_cells)
        self.grid[i][j] = 2 if random.random() < 0.9 else 4
        # 确保音效已加载
        if hasattr(self, 'sounds'):
            self.sounds['new_tile'].play()
        return True
    return False

def move(self, direction):
    """移动方块
    方向: 0: 上, 1: 右, 2: 下, 3: 左
    """
    if self.game_over:
        return
    
    moved = False
    merged = False
    
    # 转置矩阵以便上下移动
    if direction == 0 or direction == 2:
        self.grid = [list(row) for row in zip(*self.grid)]
    
    # 反转矩阵以便右和下移动
    if direction == 1 or direction == 2:
        for row in self.grid:
            row.reverse()
    
    for i in range(GRID_SIZE):
        # 移除空格
        non_zero = [num for num in self.grid[i] if num != 0]
        # 合并相同数字
        for j in range(len(non_zero) - 1):
            if non_zero[j] == non_zero[j + 1]:
                non_zero[j] *= 2
                non_zero[j + 1] = 0
                self.score += non_zero[j]
                merged = True
        
        # 再次移除空格
        non_zero = [num for num in non_zero if num != 0]
        # 填充剩余位置
        non_zero += [0] * (GRID_SIZE - len(non_zero))
        
        # 检查是否有移动发生
        if self.grid[i] != non_zero:
            moved = True
        
        self.grid[i] = non_zero
    
    # 反转矩阵回来
    if direction == 1 or direction == 2:
        for row in self.grid:
            row.reverse()
    
    # 转置矩阵回来
    if direction == 0 or direction == 2:
        self.grid = [list(row) for row in zip(*self.grid)]
    
    # 更新最高分
    if self.score > self.high_score:
        self.high_score = self.score
    
    # 播放音效
    if moved and hasattr(self, 'sounds'):
        self.sounds['move'].play()
        if merged:
            self.sounds['merge'].play()
        self.add_new_tile()
        # 检查游戏是否结束
        if not self.has_valid_moves():
            self.game_over = True
            if hasattr(self, 'sounds'):
                self.sounds['game_over'].play()
        # 检查是否获胜
        if any(2048 in row for row in self.grid) and not self.won:
            self.won = True
            if hasattr(self, 'sounds'):
                self.sounds['win'].play()

def has_valid_moves(self):
    """检查是否还有有效的移动"""
    # 检查是否有空格
    for i in range(GRID_SIZE):
        for j in range(GRID_SIZE):
            if self.grid[i][j] == 0:
                return True
    
    # 检查相邻方块是否有相同数字
    for i in range(GRID_SIZE):
        for j in range(GRID_SIZE):
            if j < GRID_SIZE - 1 and self.grid[i][j] == self.grid[i][j + 1]:
                return True
            if i < GRID_SIZE - 1 and self.grid[i][j] == self.grid[i + 1][j]:
                return True
    
    return False

def reset(self):
    """重置游戏"""
    self.grid = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
    self.score = 0
    self.game_over = False
    self.won = False
    self.add_new_tile()
    self.add_new_tile()

def draw_grid(game):
"""绘制游戏网格和方块"""
# 绘制背景
screen.fill(BACKGROUND_COLOR)

# 绘制标题
title_text = title_font.render("2048", True, TITLE_COLOR)
screen.blit(title_text, (20, 15))


# 调整分数区域位置
score_y = 5
score_width = 130
score_height = 60

# 绘制分数
score_bg = pygame.Rect(WIDTH - score_width - 20, score_y, score_width, score_height)
pygame.draw.rect(screen, SCORE_BG_COLOR, score_bg, border_radius=10)

score_text = score_font.render("分数", True, SCORE_TEXT_COLOR)
score_value = score_font.render(str(game.score), True, BRIGHT_TEXT_COLOR)

screen.blit(score_text, (WIDTH - score_width - 15, score_y + 5))
screen.blit(score_value, (WIDTH - score_width - 15, score_y + 30))

# 绘制最高分
high_score_bg = pygame.Rect(WIDTH - score_width - 20, score_y + score_height + 10, score_width, score_height)
pygame.draw.rect(screen, SCORE_BG_COLOR, high_score_bg, border_radius=10)

high_score_text = score_font.render("最高分", True, SCORE_TEXT_COLOR)
high_score_value = score_font.render(str(game.high_score), True, BRIGHT_TEXT_COLOR)

screen.blit(high_score_text, (WIDTH - score_width - 15, score_y + score_height + 15))
screen.blit(high_score_value, (WIDTH - score_width - 15, score_y + score_height + 40))

# 绘制新游戏按钮
restart_btn = pygame.Rect(WIDTH - 320, 85, 130, 40)
# 检查鼠标悬停状态
mouse_pos = pygame.mouse.get_pos()
is_hovering = restart_btn.collidepoint(mouse_pos)
btn_color = RESTART_HOVER_COLOR if is_hovering else RESTART_BG_COLOR

pygame.draw.rect(screen, btn_color, restart_btn, border_radius=10)
restart_text = small_font.render("新游戏", True, RESTART_TEXT_COLOR)
screen.blit(restart_text, (WIDTH - 305, 95))

# 绘制游戏区域背景
grid_bg = pygame.Rect(GRID_PADDING, 140, WIDTH - 2 * GRID_PADDING, WIDTH - 2 * GRID_PADDING)
pygame.draw.rect(screen, GRID_COLOR, grid_bg, border_radius=10)

# 绘制方块
for i in range(GRID_SIZE):
    for j in range(GRID_SIZE):
        # 计算方块位置
        x = GRID_PADDING + j * (CELL_SIZE + GRID_PADDING) + GRID_PADDING
        y = 140 + i * (CELL_SIZE + GRID_PADDING) + GRID_PADDING
        
        # 绘制方块背景
        value = game.grid[i][j]
        color = TILE_COLORS[value]
        cell_rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE)
        
        # 添加圆角和阴影效果
        pygame.draw.rect(screen, color, cell_rect, border_radius=5)
        pygame.draw.rect(screen, (0, 0, 0, 30), cell_rect, 1, border_radius=5)
        
        # 如果方块有值,绘制数字
        if value != 0:
            text_color = TEXT_COLORS[value]
            # 根据数字大小调整字体
            if value < 100:
                text = font.render(str(value), True, text_color)
            elif value < 1000:
                text = pygame.font.SysFont(None, FONT_SIZE - 8).render(str(value), True, text_color)
            elif value < 10000:
                text = pygame.font.SysFont(None, FONT_SIZE - 15).render(str(value), True, text_color)
            else:
                text = pygame.font.SysFont(None, FONT_SIZE - 20).render(str(value), True, text_color)
            
            text_rect = text.get_rect(center=(x + CELL_SIZE // 2, y + CELL_SIZE // 2))
            screen.blit(text, text_rect)

# 绘制游戏结束信息
if game.game_over:
    overlay = pygame.Surface((WIDTH - 2 * GRID_PADDING, WIDTH - 2 * GRID_PADDING), pygame.SRCALPHA)
    overlay.fill(GAME_OVER_OVERLAY)
    screen.blit(overlay, (GRID_PADDING, 140))
    
    game_over_text = font.render("游戏结束!", True, (255, 255, 255))
    restart_text = small_font.render("按空格键或点击按钮重新开始", True, (255, 255, 255))
    
    screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, 
                                 HEIGHT // 2 - game_over_text.get_height() // 2 - 10))
    screen.blit(restart_text, (WIDTH // 2 - restart_text.get_width() // 2, 
                              HEIGHT // 2 + 20))

# 绘制胜利信息
if game.won and not game.game_over:
    overlay = pygame.Surface((WIDTH - 2 * GRID_PADDING, WIDTH - 2 * GRID_PADDING), pygame.SRCALPHA)
    overlay.fill(WIN_OVERLAY)
    screen.blit(overlay, (GRID_PADDING, 140))
    
    win_text = font.render("你赢了!", True, (255, 255, 255))
    continue_text = small_font.render("继续游戏以获得更高分数", True, (255, 255, 255))
    
    screen.blit(win_text, (WIDTH // 2 - win_text.get_width() // 2, 
                           HEIGHT // 2 - win_text.get_height() // 2 - 10))
    screen.blit(continue_text, (WIDTH // 2 - continue_text.get_width() // 2, 
                                HEIGHT // 2 + 20))

def main():
game = Game2048()
clock = pygame.time.Clock()

# 主游戏循环
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                game.move(0)
            elif event.key == pygame.K_RIGHT:
                game.move(1)
            elif event.key == pygame.K_DOWN:
                game.move(2)
            elif event.key == pygame.K_LEFT:
                game.move(3)
            elif event.key == pygame.K_SPACE:
                game.reset()
        
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_pos = pygame.mouse.get_pos()
            # 检查是否点击了新游戏按钮
            restart_btn = pygame.Rect(WIDTH - 320, 85, 130, 40)
            if restart_btn.collidepoint(mouse_pos):
                game.reset()
    
    draw_grid(game)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()

if name == "main":
main()

posted @ 2025-06-18 09:50  XiaoguoLu  阅读(24)  评论(0)    收藏  举报