# 导入pygame模块,用于游戏开发
import pygame
import os
import random
CELL_SIZE = 20
GRID_WIDTH = 640 // CELL_SIZE
GRID_HEIGHT = 480 // CELL_SIZE
# 定义自定义事件 - 蛇移动更新事件
SNAKE_UPDATE_EVENT = pygame.USEREVENT + 1
# 定义自定义事件 - 食物更新事件
FOOD_UPDATE_ENENT = pygame.USEREVENT
# 定义全局常量 - 游戏主窗口背景颜色 (RGB值: 232,232,232 浅灰色)
BACKGROUND_COLOR = (232, 232, 232)
# 定义全局常量 - 分数文字颜色 (RGB值: 192,192,192 灰色)
SCORE_TEXT_COLOR = (192, 192, 192)
# 定义全局常量 - 提示文字颜色 (RGB值: 64,64,64 深灰色)
TIP_TEXT_COLOR = (64, 64, 64)
# 定义全局常量 - 游戏屏幕矩形区域,左上角坐标(0,0),宽640像素,高480像素
SCREEN_RECT = pygame.Rect(0, 0, 640, 480)
# 定义全局常量 - 网格单元格大小,每个单元格20x20像素
CELL_SIZE = 20
# 定义Label类,用于创建和显示文字标签
class Label(object):
# 初始化方法,设置标签的字体和属性
def __init__(self, size=48, is_score=True):
# 创建字体对象,使用黑体("simhei")和指定大小
self.font = pygame.font.SysFont("simhei", size)
# 标识是否是分数标签(True为分数标签,False为提示标签)
self.is_score = is_score
# 绘制标签方法,在指定窗口上显示文字
def draw(self, window, text):
# 根据标签类型选择文字颜色:分数标签用SCORE_TEXT_COLOR,提示标签用TIP_TEXT_COLOR
color = SCORE_TEXT_COLOR if self.is_score else TIP_TEXT_COLOR
# 使用字体渲染文字:
# - text: 要显示的文字内容
# - True: 启用抗锯齿,使文字边缘更平滑
# - color: 文字颜色
text_surface = self.font.render(text, True, color)
# 获取文字表面的矩形区域,用于定位
text_rect = text_surface.get_rect()
# 获取窗口的矩形区域
window_rect = window.get_rect()
# 设置文字矩形区域的位置:
# 如果是分数标签,显示在窗口左下角;如果是提示标签,显示在窗口中央
if self.is_score:
text_rect.bottomleft = window_rect.bottomleft
else:
text_rect.center = window_rect.center
# 将渲染好的文字表面绘制到游戏窗口的指定位置:
# - text_surface: 要绘制的文字表面
# - text_rect: 文字表面的矩形区域,确定了绘制位置
window.blit(text_surface, text_rect)
# 定义Food类,用于创建和管理食物
class Food(object):
def __init__(self):
# 食物颜色 - 红色
self.color = (255, 0, 0)
# 食物得分值 - 吃到食物得10分
self.score = 10
# 食物的矩形区域
self.rect = pygame.Rect(0, 0, CELL_SIZE, CELL_SIZE)
# 初始化食物位置
self.random_rect()
# 添加食物图片加载
try:
self.image = pygame.image.load('./贪吃蛇/img/11.jpg')
self.image = pygame.transform.scale(self.image, (CELL_SIZE, CELL_SIZE))
self.use_image = True # 标记使用图片
except:
self.use_image = False # 图片加载失败,使用颜色绘制
print("食物图片加载失败,使用颜色绘制")
def random_rect(self):
"""随机生成食物的位置"""
# 使用整数除法计算网格范围
col = SCREEN_RECT.w / CELL_SIZE - 1
row = SCREEN_RECT.h / CELL_SIZE - 1
# 随机生成食物的网格坐标
x = random.randint(0, col) * CELL_SIZE
y = random.randint(0, row) * CELL_SIZE
# 更新食物的矩形区域(保持CELL_SIZE大小)
self.rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE)
# 设置食物更新定时器
pygame.time.set_timer(FOOD_UPDATE_ENENT, 30000)
def draw(self, window):
"""在指定窗口上绘制食物"""
if self.use_image:
# 使用图片绘制
window.blit(self.image, self.rect)
else:
# 使用颜色绘制(椭圆形)
# 创建一个稍小的矩形用于绘制,让食物看起来在格子中间
draw_rect = self.rect.inflate(-4, -4) # 向内缩小4像素
pygame.draw.ellipse(window, self.color, draw_rect)
# 定义Snake类,用于创建和管理蛇
class Snake(object):
def __init__(self):
# 蛇的移动方向,初始为向右
self.dir = pygame.K_RIGHT
# 蛇移动的时间间隔(毫秒)
self.time_interval = 500
# 蛇的得分
self.score = 0
# 蛇的颜色 - 深灰色
self.color = (255, 255, 255)
# 蛇身体的矩形列表,每个矩形代表一节身体
self.body_list = []
# 初始化蛇
self.reset_snake()
def is_dead(self):
head = self.body_list[0]
if not SCREEN_RECT.contains(head):
return True
for body in self.body_list[1:]:
if head.contains(body):
return True
return False
def has_eat(self,food):
if self.body_list[0].contains(food.rect):
self.score += food.score #增加分数
if self.time_interval >100:
self.time_interval -= 50
self.add_node()
return True
try:
self.snake_image = pygame.image.load('./贪吃蛇/img/21.png')
self.snake_image = pygame.transform.scale(self.snake_image, (CELL_SIZE, CELL_SIZE))
self.use_snake_image = True # 标记使用图片
except:
self.use_snake_image = False # 图片加载失败,使用颜色绘制
print("食物图片加载失败,使用颜色绘制")
#控制蛇上下左右移动
def change_dir(self,to_dir):
hor_dirs = (pygame.K_RIGHT,pygame.K_LEFT)
ver_dirs = (pygame.K_UP,pygame.K_DOWN)
if ((self.dir in hor_dirs and to_dir not in hor_dirs) or
(self.dir in ver_dirs and to_dir not in ver_dirs)):
self.dir = to_dir
def update(self):
"""更新蛇的位置 - 移动蛇"""
# 在头部添加新节点(根据方向移动)
self.add_node()
# 移除尾部节点,保持蛇的长度不变
self.body_list.pop()
def draw(self, window):
"""在指定窗口上绘制蛇"""
for idx, rect in enumerate(self.body_list):
# 绘制蛇的每一节身体,蛇头用实心矩形,其他部分用空心矩形
pygame.draw.rect(window, self.color, rect, idx == 0)
def reset_snake(self):
"""重置蛇的状态到初始状态"""
# 重置方向为向右
self.dir = pygame.K_RIGHT
# 重置移动时间间隔为500毫秒
self.time_interval = 500
# 重置得分为0
self.score = 0
# 清空身体列表
self.body_list = []
# 初始化蛇的3节身体
for _ in range(3):
self.add_node()
def load_images(self):
"""加载蛇的头和身体图片"""
try:
# 加载头图
head_img = pygame.image.load('./贪吃蛇/img/21.png')
self.head_image = pygame.transform.scale(head_img, (CELL_SIZE, CELL_SIZE))
# 可选:如果想让身体用另一张图,否则复用头图或缩放
body_img = pygame.image.load('./贪吃蛇/img/111.jpg') # 假设身体图片是22.png
self.body_image = pygame.transform.scale(body_img, (CELL_SIZE, CELL_SIZE))
self.use_image = True
print("蛇图片加载成功")
except:
self.use_snake_image = False # 图片加载失败,使用颜色绘制
print("食物图片加载失败,使用颜色绘制")
def add_node(self):
"""在蛇的头部添加新节点"""
# 如果蛇身体不为空,复制头部作为新头部的基础
if self.body_list:
head = self.body_list[0].copy()
else:
# 如果蛇身体为空,创建初始头部(在屏幕外左侧)
head = pygame.Rect(-CELL_SIZE, 0, CELL_SIZE, CELL_SIZE)
# 根据当前方向移动头部
if self.dir == pygame.K_RIGHT:
head.x += CELL_SIZE # 向右移动一个单元格
elif self.dir == pygame.K_LEFT:
head.x -= CELL_SIZE # 向左移动一个单元格
elif self.dir == pygame.K_UP:
head.y -= CELL_SIZE # 向上移动一个单元格(注意:pygame坐标系y轴向下为正)
elif self.dir == pygame.K_DOWN:
head.y += CELL_SIZE # 向下移动一个单元格
# 将新头部插入到身体列表的开头
self.body_list.insert(0, head)
# 设置蛇移动定时器,根据当前时间间隔触发SNAKE_UPDATE_EVENT事件
pygame.time.set_timer(SNAKE_UPDATE_EVENT, self.time_interval)
# 定义Game类,用于管理游戏主要逻辑
class Game(object):
def __init__(self):
# 创建游戏窗口,宽640像素,高480像素
self.main_window = pygame.display.set_mode((640,480))
# 设置窗口标题为"贪吃蛇"
pygame.display.set_caption("贪吃蛇")
# 初始化游戏分数
self.score = 0
# 创建Label类对象,用于显示分数
self.score_label = Label()
# 创建提示标签,参数可能表示字体大小和是否加粗
self.tip_label = Label(24,False)
# 游戏结束状态标志,初始为False
self.is_game_over = False
self.is_pause = False
self.snake = Snake()
# 背景图片 - 修正路径和加载方式
self.background = pygame.image.load('./贪吃蛇/img/1.jpg')
self.background = pygame.transform.scale(self.background, (640,480))
# 创建食物对象
self.food = Food()
# 游戏开始方法,包含游戏主循环
def start(self):
# 创建时钟对象,用于控制游戏帧率
clock = pygame.time.Clock()
# 游戏主循环(死循环),持续运行直到退出
while True:
# 遍历同一时刻发生的事件列表
for event in pygame.event.get():
# 处理退出事件(点击窗口关闭按钮)
if event.type == pygame.QUIT:
return # 退出游戏
# 处理键盘按下事件
elif event.type == pygame.KEYDOWN:
# 处理ESC键按下事件
if event.key == pygame.K_ESCAPE:
return # 退出游戏
elif event.key == pygame.K_SPACE:
if self.is_game_over:
self.reset_game()
else:
self.is_pause = not self.is_pause
if not self.is_pause and not self.is_game_over:
if event.type == FOOD_UPDATE_ENENT:
self.food.random_rect()
elif event.type == SNAKE_UPDATE_EVENT:
self.snake.update()
elif event.type == pygame.KEYDOWN:
if event.key in (pygame.K_UP,pygame.K_DOWN,pygame.K_LEFT,pygame.K_RIGHT):
self.snake.change_dir(event.key)
# 绘制背景图片
self.main_window.blit(self.background, (0, 0))
# 判断游戏状态
if self.is_game_over:
self.tip_label.draw(self.main_window, "游戏结束,按空格键开启新游戏...")
elif self.is_pause:
self.tip_label.draw(self.main_window, "游戏暂停,按空格键继续游戏...")
# 正常游戏状态
else:
if self.snake.has_eat(self.food):
self.food.random_rect()
self.food.draw(self.main_window)
self.snake.draw(self.main_window)
# 在窗口上绘制分数标签,显示当前得分
self.score_label.draw(self.main_window, "得分: %d" % self.snake.score)
# 更新显示,将绘制的内容显示到屏幕上
pygame.display.update()
# 控制游戏帧率为60FPS,减轻CPU功耗
clock.tick(60)
def draw_food(self):
"""绘制食物"""
self.main_window.blit(self.food_image, self.food.rect)
def reset_game(self):
self.score = 0
self.is_game_over = False
self.is_pause = False
# 重置游戏时重新生成食物位置
self.food.random_rect()
self.snake.rect_snake()
# 主程序入口,当直接运行此脚本时执行
if __name__ == '__main__':
# 初始化Pygame所有模块
pygame.init()
# 创建Game实例并调用start方法启动游戏
Game().start()
# 游戏结束后,取消初始化Pygame,释放资源
pygame.quit()
替换为图片的蛇头和蛇身如何被调用显示
最新发布