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()