345 lines
12 KiB
Python
345 lines
12 KiB
Python
import pygame
|
||
import sys
|
||
import random
|
||
import time
|
||
|
||
# 初始化 Pygame
|
||
pygame.init()
|
||
|
||
# 游戏常量
|
||
WIDTH, HEIGHT = 800, 600
|
||
GRID_SIZE = 20
|
||
GRID_WIDTH = WIDTH // GRID_SIZE
|
||
GRID_HEIGHT = HEIGHT // GRID_SIZE
|
||
FPS = 10 # 控制游戏速度
|
||
|
||
# 颜色定义
|
||
BACKGROUND = (15, 15, 30)
|
||
GRID_COLOR = (30, 30, 60)
|
||
SNAKE_HEAD = (50, 205, 50) # 蛇头颜色 - 浅绿色
|
||
SNAKE_BODY = (34, 139, 34) # 蛇身颜色 - 绿色
|
||
FOOD_COLOR = (220, 20, 60) # 食物颜色 - 红色
|
||
TEXT_COLOR = (220, 220, 220)
|
||
SCORE_COLOR = (255, 215, 0)
|
||
|
||
# 方向常量
|
||
UP = (0, -1)
|
||
DOWN = (0, 1)
|
||
LEFT = (-1, 0)
|
||
RIGHT = (1, 0)
|
||
|
||
class Snake:
|
||
def __init__(self):
|
||
self.reset()
|
||
|
||
def reset(self):
|
||
"""重置蛇的状态"""
|
||
# 蛇初始位置在屏幕中央
|
||
self.length = 3
|
||
self.positions = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
|
||
self.direction = RIGHT
|
||
self.score = 0
|
||
self.grow_to = 3 # 初始长度
|
||
self.is_alive = True
|
||
|
||
# 初始添加蛇身
|
||
for i in range(1, self.length):
|
||
self.positions.append((self.positions[0][0] - i, self.positions[0][1]))
|
||
|
||
def get_head_position(self):
|
||
"""获取蛇头位置"""
|
||
return self.positions[0]
|
||
|
||
def turn(self, direction):
|
||
"""改变蛇的方向(不能直接反向)"""
|
||
# 防止蛇直接反向移动
|
||
if (direction[0] * -1, direction[1] * -1) != self.direction:
|
||
self.direction = direction
|
||
|
||
def move(self):
|
||
"""移动蛇"""
|
||
if not self.is_alive:
|
||
return
|
||
|
||
head = self.get_head_position()
|
||
new_x = (head[0] + self.direction[0]) % GRID_WIDTH
|
||
new_y = (head[1] + self.direction[1]) % GRID_HEIGHT
|
||
new_position = (new_x, new_y)
|
||
|
||
# 检查是否撞到自己
|
||
if new_position in self.positions[1:]:
|
||
self.is_alive = False
|
||
return
|
||
|
||
# 添加新的头部
|
||
self.positions.insert(0, new_position)
|
||
|
||
# 如果蛇需要生长,则保留尾部,否则移除尾部
|
||
if len(self.positions) > self.grow_to:
|
||
self.positions.pop()
|
||
|
||
def draw(self, surface):
|
||
"""绘制蛇"""
|
||
for i, pos in enumerate(self.positions):
|
||
# 蛇头用不同颜色
|
||
color = SNAKE_HEAD if i == 0 else SNAKE_BODY
|
||
|
||
# 绘制蛇身矩形
|
||
rect = pygame.Rect(
|
||
pos[0] * GRID_SIZE,
|
||
pos[1] * GRID_SIZE,
|
||
GRID_SIZE,
|
||
GRID_SIZE
|
||
)
|
||
pygame.draw.rect(surface, color, rect)
|
||
pygame.draw.rect(surface, (color[0]//2, color[1]//2, color[2]//2), rect, 1)
|
||
|
||
# 绘制蛇眼睛(头部)
|
||
if i == 0:
|
||
# 根据方向确定眼睛位置
|
||
eye_size = GRID_SIZE // 5
|
||
if self.direction == RIGHT:
|
||
left_eye = (rect.right - eye_size*2, rect.top + eye_size*2)
|
||
right_eye = (rect.right - eye_size*2, rect.bottom - eye_size*3)
|
||
elif self.direction == LEFT:
|
||
left_eye = (rect.left + eye_size, rect.top + eye_size*2)
|
||
right_eye = (rect.left + eye_size, rect.bottom - eye_size*3)
|
||
elif self.direction == UP:
|
||
left_eye = (rect.left + eye_size*2, rect.top + eye_size)
|
||
right_eye = (rect.right - eye_size*3, rect.top + eye_size)
|
||
else: # DOWN
|
||
left_eye = (rect.left + eye_size*2, rect.bottom - eye_size*2)
|
||
right_eye = (rect.right - eye_size*3, rect.bottom - eye_size*2)
|
||
|
||
pygame.draw.circle(surface, (255, 255, 255), left_eye, eye_size)
|
||
pygame.draw.circle(surface, (255, 255, 255), right_eye, eye_size)
|
||
pygame.draw.circle(surface, (0, 0, 0), left_eye, eye_size//2)
|
||
pygame.draw.circle(surface, (0, 0, 0), right_eye, eye_size//2)
|
||
|
||
def grow(self):
|
||
"""蛇生长"""
|
||
self.grow_to += 1
|
||
self.score += 10
|
||
|
||
class Food:
|
||
def __init__(self):
|
||
self.position = (0, 0)
|
||
self.randomize_position()
|
||
|
||
def randomize_position(self):
|
||
"""随机生成食物位置"""
|
||
self.position = (
|
||
random.randint(0, GRID_WIDTH - 1),
|
||
random.randint(0, GRID_HEIGHT - 1)
|
||
)
|
||
|
||
def draw(self, surface):
|
||
"""绘制食物"""
|
||
rect = pygame.Rect(
|
||
self.position[0] * GRID_SIZE,
|
||
self.position[1] * GRID_SIZE,
|
||
GRID_SIZE, GRID_SIZE
|
||
)
|
||
|
||
# 绘制一个苹果形状的食物
|
||
pygame.draw.rect(surface, FOOD_COLOR, rect, border_radius=GRID_SIZE//3)
|
||
|
||
# 添加一个小茎
|
||
stem_rect = pygame.Rect(
|
||
rect.centerx - GRID_SIZE//10,
|
||
rect.top - GRID_SIZE//4,
|
||
GRID_SIZE//5,
|
||
GRID_SIZE//4
|
||
)
|
||
pygame.draw.rect(surface, (139, 69, 19), stem_rect)
|
||
|
||
# 添加一个高光
|
||
highlight = pygame.Rect(
|
||
rect.left + GRID_SIZE//4,
|
||
rect.top + GRID_SIZE//4,
|
||
GRID_SIZE//4,
|
||
GRID_SIZE//4
|
||
)
|
||
pygame.draw.ellipse(surface, (255, 100, 100), highlight)
|
||
|
||
class Game:
|
||
def __init__(self):
|
||
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||
pygame.display.set_caption("贪吃蛇游戏 - Python版")
|
||
self.clock = pygame.time.Clock()
|
||
self.font = pygame.font.SysFont('simhei', 25)
|
||
self.big_font = pygame.font.SysFont('simhei', 50)
|
||
self.snake = Snake()
|
||
self.food = Food()
|
||
self.speed = FPS
|
||
self.game_over = False
|
||
self.paused = False
|
||
|
||
# 确保食物不在蛇身上
|
||
while self.food.position in self.snake.positions:
|
||
self.food.randomize_position()
|
||
|
||
def draw_grid(self):
|
||
"""绘制游戏网格"""
|
||
for x in range(0, WIDTH, GRID_SIZE):
|
||
pygame.draw.line(self.screen, GRID_COLOR, (x, 0), (x, HEIGHT))
|
||
for y in range(0, HEIGHT, GRID_SIZE):
|
||
pygame.draw.line(self.screen, GRID_COLOR, (0, y), (WIDTH, y))
|
||
|
||
def draw_score(self):
|
||
"""绘制分数"""
|
||
score_text = self.font.render(f"分数: {self.snake.score}", True, SCORE_COLOR)
|
||
length_text = self.font.render(f"长度: {len(self.snake.positions)}", True, SCORE_COLOR)
|
||
self.screen.blit(score_text, (10, 10))
|
||
self.screen.blit(length_text, (10, 40))
|
||
|
||
# 绘制操作说明
|
||
controls = [
|
||
"方向键: 控制移动",
|
||
"空格键: 暂停/继续",
|
||
"R键: 重新开始",
|
||
"ESC键: 退出游戏"
|
||
]
|
||
|
||
for i, text in enumerate(controls):
|
||
control_text = self.font.render(text, True, TEXT_COLOR)
|
||
self.screen.blit(control_text, (WIDTH - control_text.get_width() - 10, 10 + i * 30))
|
||
|
||
def draw_game_over(self):
|
||
"""绘制游戏结束画面"""
|
||
game_over_surface = self.big_font.render("游戏结束!", True, (255, 50, 50))
|
||
score_surface = self.font.render(f"最终分数: {self.snake.score}", True, SCORE_COLOR)
|
||
restart_surface = self.font.render("按R键重新开始,按ESC键退出", True, TEXT_COLOR)
|
||
|
||
# 居中显示
|
||
self.screen.blit(game_over_surface, (WIDTH//2 - game_over_surface.get_width()//2, HEIGHT//2 - 60))
|
||
self.screen.blit(score_surface, (WIDTH//2 - score_surface.get_width()//2, HEIGHT//2))
|
||
self.screen.blit(restart_surface, (WIDTH//2 - restart_surface.get_width()//2, HEIGHT//2 + 50))
|
||
|
||
def draw_pause(self):
|
||
"""绘制暂停画面"""
|
||
# 半透明覆盖层
|
||
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
|
||
overlay.fill((0, 0, 0, 150))
|
||
self.screen.blit(overlay, (0, 0))
|
||
|
||
# 暂停文字
|
||
pause_surface = self.big_font.render("游戏暂停", True, (100, 200, 255))
|
||
continue_surface = self.font.render("按空格键继续游戏", True, TEXT_COLOR)
|
||
|
||
self.screen.blit(pause_surface, (WIDTH//2 - pause_surface.get_width()//2, HEIGHT//2 - 40))
|
||
self.screen.blit(continue_surface, (WIDTH//2 - continue_surface.get_width()//2, HEIGHT//2 + 20))
|
||
|
||
def check_food_collision(self):
|
||
"""检查是否吃到食物"""
|
||
if self.snake.get_head_position() == self.food.position:
|
||
self.snake.grow()
|
||
|
||
# 重新生成食物,确保不在蛇身上
|
||
while True:
|
||
self.food.randomize_position()
|
||
if self.food.position not in self.snake.positions:
|
||
break
|
||
|
||
# 每得100分增加一点速度
|
||
if self.snake.score % 100 == 0:
|
||
self.speed = min(self.speed + 1, 20) # 最大速度限制
|
||
|
||
def handle_events(self):
|
||
"""处理游戏事件"""
|
||
for event in pygame.event.get():
|
||
if event.type == pygame.QUIT:
|
||
pygame.quit()
|
||
sys.exit()
|
||
|
||
elif event.type == pygame.KEYDOWN:
|
||
if event.key == pygame.K_ESCAPE:
|
||
pygame.quit()
|
||
sys.exit()
|
||
|
||
elif event.key == pygame.K_r:
|
||
# 重置游戏
|
||
self.__init__()
|
||
|
||
elif event.key == pygame.K_SPACE:
|
||
# 暂停/继续游戏
|
||
self.paused = not self.paused
|
||
|
||
# 方向控制
|
||
if not self.paused and not self.game_over:
|
||
if event.key == pygame.K_UP:
|
||
self.snake.turn(UP)
|
||
elif event.key == pygame.K_DOWN:
|
||
self.snake.turn(DOWN)
|
||
elif event.key == pygame.K_LEFT:
|
||
self.snake.turn(LEFT)
|
||
elif event.key == pygame.K_RIGHT:
|
||
self.snake.turn(RIGHT)
|
||
|
||
def run(self):
|
||
"""运行游戏主循环"""
|
||
while True:
|
||
self.handle_events()
|
||
|
||
# 如果游戏暂停,只绘制界面不更新游戏状态
|
||
if self.paused:
|
||
self.screen.fill(BACKGROUND)
|
||
self.draw_grid()
|
||
self.snake.draw(self.screen)
|
||
self.food.draw(self.screen)
|
||
self.draw_score()
|
||
self.draw_pause()
|
||
pygame.display.update()
|
||
self.clock.tick(FPS)
|
||
continue
|
||
|
||
# 如果游戏结束,显示结束画面
|
||
if self.game_over:
|
||
self.screen.fill(BACKGROUND)
|
||
self.draw_grid()
|
||
self.snake.draw(self.screen)
|
||
self.food.draw(self.screen)
|
||
self.draw_score()
|
||
self.draw_game_over()
|
||
pygame.display.update()
|
||
self.clock.tick(FPS)
|
||
continue
|
||
|
||
# 更新游戏状态
|
||
self.snake.move()
|
||
|
||
# 检查游戏是否结束
|
||
if not self.snake.is_alive:
|
||
self.game_over = True
|
||
|
||
# 检查是否吃到食物
|
||
self.check_food_collision()
|
||
|
||
# 绘制游戏
|
||
self.screen.fill(BACKGROUND)
|
||
self.draw_grid()
|
||
self.snake.draw(self.screen)
|
||
self.food.draw(self.screen)
|
||
self.draw_score()
|
||
|
||
# 如果游戏快结束了,闪烁提示
|
||
if not self.snake.is_alive:
|
||
# 蛇死亡时的闪烁效果
|
||
if int(time.time() * 10) % 2 == 0:
|
||
death_overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
|
||
death_overlay.fill((255, 50, 50, 50))
|
||
self.screen.blit(death_overlay, (0, 0))
|
||
|
||
pygame.display.update()
|
||
self.clock.tick(self.speed)
|
||
|
||
if __name__ == "__main__":
|
||
# 检查是否安装了pygame
|
||
try:
|
||
game = Game()
|
||
game.run()
|
||
except pygame.error as e:
|
||
print(f"错误: {e}")
|
||
print("请确保已安装pygame库,可以使用以下命令安装:")
|
||
print("pip install pygame")
|
||
input("按回车键退出...") |