视觉细节拉满:Python 数独游戏添加动画与过渡效果
·
Python 数独游戏:添加动画与过渡效果
下面是一个完整的Python数独游戏实现,包含丰富的视觉细节和动画效果,使用Pygame库实现。
import pygame
import sys
import random
import math
from pygame import gfxdraw
# 初始化Pygame
pygame.init()
# 游戏常量
WIDTH, HEIGHT = 540, 600
GRID_SIZE = 9
CELL_SIZE = WIDTH // GRID_SIZE
FPS = 60
# 颜色定义
BACKGROUND = (28, 35, 53)
GRID_LINES = (86, 98, 128)
GRID_BOLD = (64, 80, 127)
CELL_HIGHLIGHT = (40, 120, 180, 100)
NUMBER_BASE = (220, 230, 255)
NUMBER_HIGHLIGHT = (255, 215, 100)
NUMBER_ERROR = (255, 100, 100)
BUTTON_BG = (50, 100, 180)
BUTTON_HOVER = (70, 130, 220)
BUTTON_TEXT = (240, 245, 255)
# 创建游戏窗口
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("视觉细节拉满的数独游戏")
clock = pygame.time.Clock()
# 字体
font = pygame.font.SysFont(None, 40)
small_font = pygame.font.SysFont(None, 30)
title_font = pygame.font.SysFont(None, 48)
class Animation:
"""动画效果管理类"""
def __init__(self):
self.cell_highlights = {}
self.number_effects = {}
self.grid_pulse = 0
self.pulse_dir = 1
self.completion_effect = None
def add_cell_highlight(self, row, col):
"""添加单元格高亮动画"""
self.cell_highlights[(row, col)] = {
'alpha': 0,
'grow': True,
'duration': 0
}
def add_number_effect(self, row, col, correct=True):
"""添加数字输入效果"""
self.number_effects[(row, col)] = {
'scale': 0,
'color': NUMBER_HIGHLIGHT if correct else NUMBER_ERROR,
'duration': 0
}
def add_completion_effect(self):
"""添加游戏完成效果"""
self.completion_effect = {
'radius': 0,
'alpha': 255,
'particles': []
}
def generate_particle(self):
"""生成粒子效果"""
return {
'x': random.randint(0, WIDTH),
'y': random.randint(0, HEIGHT),
'size': random.randint(2, 8),
'speed': random.uniform(1.0, 3.0),
'angle': random.uniform(0, 2 * math.pi),
'color': (
random.randint(200, 255),
random.randint(200, 255),
random.randint(100, 200)
),
'life': 100
}
def update(self, dt):
"""更新所有动画状态"""
# 更新网格脉冲效果
self.grid_pulse += 0.5 * self.pulse_dir
if self.grid_pulse > 10:
self.pulse_dir = -1
elif self.grid_pulse < 0:
self.pulse_dir = 1
# 更新单元格高亮
for key in list(self.cell_highlights.keys()):
effect = self.cell_highlights[key]
effect['duration'] += dt
if effect['grow']:
effect['alpha'] = min(255, effect['alpha'] + 15)
if effect['alpha'] >= 255:
effect['grow'] = False
else:
effect['alpha'] = max(0, effect['alpha'] - 3)
if effect['alpha'] <= 0:
del self.cell_highlights[key]
# 更新数字效果
for key in list(self.number_effects.keys()):
effect = self.number_effects[key]
effect['duration'] += dt
if effect['scale'] < 1.0:
effect['scale'] = min(1.0, effect['scale'] + 0.15)
else:
effect['scale'] = max(1.0, effect['scale'] - 0.01)
if effect['duration'] > 1.5: # 1.5秒后移除效果
del self.number_effects[key]
# 更新完成效果
if self.completion_effect:
self.completion_effect['radius'] += 1
if self.completion_effect['radius'] > max(WIDTH, HEIGHT) * 1.5:
self.completion_effect['alpha'] = max(0, self.completion_effect['alpha'] - 3)
# 添加新粒子
if random.random() < 0.3:
self.completion_effect['particles'].append(self.generate_particle())
# 更新现有粒子
for particle in self.completion_effect['particles']:
particle['x'] += math.cos(particle['angle']) * particle['speed']
particle['y'] += math.sin(particle['angle']) * particle['speed']
particle['life'] -= 1
# 移除死亡的粒子
self.completion_effect['particles'] = [
p for p in self.completion_effect['particles'] if p['life'] > 0
]
# 效果结束
if self.completion_effect['alpha'] <= 0 and not self.completion_effect['particles']:
self.completion_effect = None
class SudokuGame:
"""数独游戏核心类"""
def __init__(self):
self.board = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
self.solution = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
self.original = [[False for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
self.selected = None
self.errors = set()
self.animation = Animation()
self.game_completed = False
self.generate_puzzle()
def generate_puzzle(self):
"""生成新的数独谜题"""
# 重置游戏状态
self.board = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
self.original = [[False for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
self.selected = None
self.errors = set()
self.game_completed = False
self.animation = Animation()
# 生成完整解
self.solve_board(True)
self.solution = [row[:] for row in self.board]
# 移除部分数字创建谜题
to_remove = 45 # 难度级别
removed = 0
while removed < to_remove:
row, col = random.randint(0, 8), random.randint(0, 8)
if self.board[row][col] != 0:
# 备份值
backup = self.board[row][col]
self.board[row][col] = 0
# 检查是否唯一解
temp_board = [row[:] for row in self.board]
solutions = self.count_solutions()
self.board = [row[:] for row in temp_board]
if solutions == 1:
self.original[row][col] = False
removed += 1
else:
self.board[row][col] = backup
self.original[row][col] = True
def solve_board(self, generate=False):
"""解数独板"""
empty = self.find_empty()
if not empty:
return True
row, col = empty
# 生成谜题时随机排序数字
numbers = list(range(1, 10))
if generate:
random.shuffle(numbers)
for num in numbers:
if self.is_valid(row, col, num):
self.board[row][col] = num
if self.solve_board(generate):
return True
self.board[row][col] = 0
return False
def count_solutions(self):
"""计算解的数量"""
empty = self.find_empty()
if not empty:
return 1
row, col = empty
count = 0
for num in range(1, 10):
if self.is_valid(row, col, num):
self.board[row][col] = num
count += self.count_solutions()
self.board[row][col] = 0
if count > 1: # 提前终止
return count
return count
def find_empty(self):
"""查找空白格子"""
for i in range(GRID_SIZE):
for j in range(GRID_SIZE):
if self.board[i][j] == 0:
return (i, j)
return None
def is_valid(self, row, col, num):
"""检查数字放置是否有效"""
# 检查行
for i in range(GRID_SIZE):
if self.board[row][i] == num and i != col:
return False
# 检查列
for i in range(GRID_SIZE):
if self.board[i][col] == num and i != row:
return False
# 检查3x3宫格
box_row, box_col = row // 3 * 3, col // 3 * 3
for i in range(3):
for j in range(3):
if self.board[box_row + i][box_col + j] == num and (box_row + i != row or box_col + j != col):
return False
return True
def place_number(self, num):
"""在选中的格子中放置数字"""
if self.selected and not self.original[self.selected[0]][self.selected[1]]:
row, col = self.selected
# 检查是否有效
valid = self.is_valid(row, col, num)
# 移除之前的错误标记
self.errors.discard((row, col))
# 放置数字
self.board[row][col] = num
# 添加动画效果
self.animation.add_number_effect(row, col, valid)
# 如果是错误放置,添加到错误集
if not valid:
self.errors.add((row, col))
# 检查游戏是否完成
if self.find_empty() is None and not self.errors:
self.game_completed = True
self.animation.add_completion_effect()
return True
return False
def handle_click(self, pos):
"""处理鼠标点击"""
x, y = pos
if y < WIDTH: # 点击在网格内
row, col = y // CELL_SIZE, x // CELL_SIZE
self.selected = (row, col)
self.animation.add_cell_highlight(row, col)
elif WIDTH + 10 <= x <= WIDTH + 100 and HEIGHT - 50 <= y <= HEIGHT - 10:
self.generate_puzzle() # 新游戏按钮
def draw(self, screen):
"""绘制游戏界面"""
# 绘制背景
screen.fill(BACKGROUND)
# 绘制标题
title = title_font.render("视觉细节数独", True, (220, 230, 255))
screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 10))
# 绘制网格线
for i in range(GRID_SIZE + 1):
# 绘制粗线
if i % 3 == 0:
line_width = 3 + self.animation.grid_pulse / 8
color = GRID_BOLD
else:
line_width = 1
color = GRID_LINES
# 水平线
pygame.draw.line(
screen, color,
(0, i * CELL_SIZE),
(WIDTH, i * CELL_SIZE),
int(line_width)
)
# 垂直线
pygame.draw.line(
screen, color,
(i * CELL_SIZE, 0),
(i * CELL_SIZE, WIDTH),
int(line_width)
)
# 绘制高亮效果
for (row, col), effect in self.animation.cell_highlights.items():
alpha = effect['alpha']
if alpha > 0:
s = pygame.Surface((CELL_SIZE, CELL_SIZE), pygame.SRCALPHA)
s.fill((*CELL_HIGHLIGHT[:3], alpha))
screen.blit(s, (col * CELL_SIZE, row * CELL_SIZE))
# 绘制选中的单元格
if self.selected:
row, col = self.selected
pygame.draw.rect(
screen, CELL_HIGHLIGHT,
(col * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE),
3
)
# 绘制数字
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
value = self.board[row][col]
if value != 0:
# 确定数字颜色
if (row, col) in self.errors:
color = NUMBER_ERROR
elif self.original[row][col]:
color = NUMBER_BASE
else:
color = NUMBER_HIGHLIGHT
# 检查是否有动画效果
effect = self.animation.number_effects.get((row, col), None)
if effect:
# 应用缩放效果
scale = effect['scale']
color = effect['color']
num_surface = font.render(str(value), True, color)
# 缩放表面
scaled_size = (int(num_surface.get_width() * scale),
int(num_surface.get_height() * scale))
if scaled_size[0] > 0 and scaled_size[1] > 0:
scaled_surface = pygame.transform.scale(num_surface, scaled_size)
pos_x = col * CELL_SIZE + (CELL_SIZE - scaled_size[0]) // 2
pos_y = row * CELL_SIZE + (CELL_SIZE - scaled_size[1]) // 2
screen.blit(scaled_surface, (pos_x, pos_y))
continue
# 普通绘制
num_surface = font.render(str(value), True, color)
screen.blit(
num_surface,
(col * CELL_SIZE + (CELL_SIZE - num_surface.get_width()) // 2,
row * CELL_SIZE + (CELL_SIZE - num_surface.get_height()) // 2)
)
# 绘制新游戏按钮
button_rect = pygame.Rect(WIDTH // 2 - 50, HEIGHT - 45, 100, 35)
mouse_pos = pygame.mouse.get_pos()
button_hover = button_rect.collidepoint(mouse_pos)
# 按钮背景
button_color = BUTTON_HOVER if button_hover else BUTTON_BG
pygame.draw.rect(screen, button_color, button_rect, border_radius=8)
pygame.draw.rect(screen, (200, 220, 255), button_rect, 2, border_radius=8)
# 按钮文字
button_text = small_font.render("新游戏", True, BUTTON_TEXT)
screen.blit(button_text, (button_rect.centerx - button_text.get_width() // 2,
button_rect.centery - button_text.get_height() // 2))
# 绘制完成效果
if self.animation.completion_effect:
effect = self.animation.completion_effect
radius = effect['radius']
alpha = effect['alpha']
# 绘制圆形扩散效果
if radius > 0 and alpha > 0:
s = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
pygame.draw.circle(
s, (255, 255, 200, alpha // 2),
(WIDTH // 2, WIDTH // 2), radius
)
screen.blit(s, (0, 0))
# 绘制粒子
for particle in effect['particles']:
pygame.draw.circle(
screen, particle['color'],
(int(particle['x']), int(particle['y'])),
particle['size']
)
# 绘制完成文本
if alpha > 100:
text = title_font.render("恭喜完成!", True, (255, 255, 200))
screen.blit(text, (WIDTH // 2 - text.get_width() // 2,
HEIGHT // 2 - text.get_height() // 2))
# 创建游戏实例
game = SudokuGame()
# 主游戏循环
running = True
while running:
dt = clock.tick(FPS) / 1000.0 # 转换为秒
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # 左键
game.handle_click(event.pos)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif pygame.K_1 <= event.key <= pygame.K_9:
num = event.key - pygame.K_0
game.place_number(num)
elif event.key == pygame.K_DELETE or event.key == pygame.K_BACKSPACE:
game.place_number(0)
elif event.key == pygame.K_n:
game.generate_puzzle()
# 更新动画
game.animation.update(dt)
# 绘制游戏
game.draw(screen)
# 更新显示
pygame.display.flip()
pygame.quit()
sys.exit()
视觉细节说明
这个数独游戏实现了丰富的视觉效果和动画:
1. 网格动画
- 粗网格线有轻微的脉动效果,增强视觉层次感
- 细网格线保持清晰,形成对比
2. 单元格交互效果
- 点击单元格时出现淡入淡出的高亮效果
- 选中单元格有蓝色边框突出显示
3. 数字动画
- 输入数字时有缩放动画效果
- 正确输入显示金色动画
- 错误输入显示红色动画
- 初始数字为浅蓝色,用户输入为金色
4. 游戏完成特效
- 金色粒子从屏幕中心向外扩散
- 背景出现淡黄色光晕效果
- "恭喜完成!"文字提示
5. 按钮效果
- 新游戏按钮有悬停效果
- 圆角设计和渐变颜色
6. 整体视觉风格
- 深蓝色背景提高数字可读性
- 精心选择的配色方案
- 平滑的动画过渡效果
要运行此游戏,您需要安装Pygame库:
pip install pygame
这个实现包含了完整的数独生成逻辑、解决算法和丰富的视觉反馈,让游戏体验更加生动有趣。
更多推荐
所有评论(0)