<think>我们注意到代码中有一个问题:在初始化pygame之后,尝试加载字体时使用了变量`font_path`,但是在这个变量被使用之前,它并没有被赋值(除了在`Mam_draw_map`函数内部有一个同名的局部变量,但那是不同的作用域)。在代码中,我们在初始化字体之前尝试使用`font_path`,但实际上在初始化字体的时候,`font_path`还没有被赋值(赋值在后面的代码中)。这会导致在`font = font.SysFont(font_path, 36)`这一行出错,因为`font_path`此时是未定义的。
另外,在代码中,我们尝试加载字体时,如果失败则使用默认字体,但是`font_path`的赋值是在后面才进行的。因此,我们需要调整代码顺序,确保在初始化字体之前,`font_path`已经被正确赋值。
具体修改如下:
1. 将`font_path`的赋值移到初始化字体的代码之前。
2. 在初始化字体时,先检查`font_path`是否存在,如果存在则尝试加载该字体,否则使用默认字体。
另外,我们还注意到在`Mam_draw_map`函数内部,我们使用了`font`这个全局变量,但是在函数内部并没有声明它为全局变量,这没有问题,因为我们在函数内部只是读取它,而不是重新赋值。但是,如果字体加载失败,我们使用默认字体,那么`font`变量仍然会被创建。
但是,在代码中,我们尝试在加载图片之前加载字体,而加载图片如果失败会退出程序,这样字体加载的代码就不会执行。因此,我们需要确保字体加载在图片加载之前,或者即使图片加载失败,字体加载的异常也能被捕获。
不过,考虑到字体加载失败不会导致程序崩溃(我们有回退方案),我们可以将字体加载的代码放在图片加载之前,这样即使图片加载失败,我们也能显示错误信息(虽然错误信息可能无法用中文显示,但至少能显示默认字体)。
因此,我们调整代码顺序如下:
- 初始化pygame
- 设置屏幕
- 设置字体路径
- 加载字体(尝试加载指定字体,失败则用默认字体)
- 加载图片
这样修改后,即使图片加载失败,我们也能显示错误信息。
另外,在代码中,我们定义了一个`font_path`,但是在加载字体时,我们尝试使用这个路径,如果这个路径不存在,则使用默认字体。但是,我们并没有检查这个路径是否存在,所以我们可以先检查一下,如果不存在则使用默认字体。
修改后的代码:
```python
if __name__ == '__main__':
# 获取当前脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
images_dir = os.path.join(script_dir, 'images')
fonts_dir = os.path.join(script_dir, "fonts") # 字体目录
# 确保图片目录存在
if not os.path.exists(images_dir):
os.makedirs(images_dir)
print(f"已创建图片目录: {images_dir}")
# 初始化pygame
init()
screen = display.set_mode((1280, 720))
display.set_caption("Mamlab")
# 设置字体路径
font_path = os.path.join(fonts_dir, "HYPixel11pxU-2.ttf")
# 检查字体文件是否存在,如果不存在则使用None(系统默认字体)
if not os.path.exists(font_path):
print(f"警告:字体文件不存在: {font_path}")
font_path = None
# 加载字体
try:
if font_path:
font = font.SysFont(font_path, 36)
else:
font = font.SysFont(None, 36) # 使用系统默认字体
except Exception as e:
print(f"字体加载失败: {e}, 使用默认字体")
font = font.SysFont(None, 36)
# 直接加载图片,如果失败则退出程序
try:
# 加载所有图片资源
background = image.load(os.path.join(images_dir, 'background.png')).convert()
button_start_img = image.load(os.path.join(images_dir, 'button_start.png')).convert_alpha()
button_quit_img = image.load(os.path.join(images_dir, 'button_quit.png')).convert_alpha()
title_img = image.load(os.path.join(images_dir, 'title.png')).convert_alpha()
conversation_1 = image.load(os.path.join(images_dir, 'conversation_1.png')).convert_alpha()
conversation_2 = image.load(os.path.join(images_dir, 'conversation_2.png')).convert_alpha()
print("所有图片加载成功!")
except error as e:
print(f"图片加载失败: {e}")
print("请确保所有图片文件存在于 images/ 目录中")
quit()
sys.exit(1) # 退出程序并返回错误代码
# ... 其余代码保持不变 ...
```
此外,我们还注意到在`Mam_draw_map`函数中,我们使用了`font`变量来渲染文本,但是当字体加载失败时,我们使用了默认字体,所以这里应该没有问题。
但是,在函数`Mam_draw_map`中,我们使用了`font`变量,而这个变量是在全局作用域中定义的。在函数内部,我们并没有重新定义`font`,所以它会使用全局的`font`变量。
另一个问题是,在文本渲染部分,我们使用了`font.render`,但是当文本中包含中文字符时,如果字体不支持中文,可能会出现乱码。因此,我们确保字体文件是一个支持中文的字体。如果找不到指定的字体文件,我们使用默认字体,而默认字体可能不支持中文,所以会显示乱码。因此,我们最好在找不到指定字体时,尝试使用一个支持中文的系统字体,或者提供一个备选字体。
不过,由于我们无法控制用户的系统,所以我们可以尝试使用一个支持中文的字体名称,例如在Windows上可以使用"SimHei",在Linux上可以使用"WenQuanYi Micro Hei"等。但是,这样会增加代码的复杂性。为了简单起见,我们只使用指定的字体文件,如果不存在则使用默认字体,并打印警告。
最后,我们注意到在代码中,我们使用了`quit()`函数来退出pygame,但是在图片加载失败后,我们调用了`quit()`然后`sys.exit(1)`。实际上,在调用`quit()`之后,pygame已经被卸载,所以我们可以直接退出。
综上所述,我们主要调整了字体加载的顺序和方式,并确保字体路径在加载字体之前被正确设置。
现在,我们修改代码并运行。
注意:由于代码较长,我们只修改了主程序部分,其他部分保持不变。
如果还有问题,请根据错误信息进一步调整。
由于代码较长,我们只展示修改后的主程序入口部分。</think>您的代码是一个使用Pygame开发的游戏框架,主要实现了游戏界面、动画效果和对话系统。我已经分析了代码并发现几个需要改进的地方:
### 主要问题分析:
1. **字体加载问题**:`font_path`在初始化字体时还未定义
2. **资源加载顺序**:图片加载失败后程序退出,但字体尚未初始化
3. **文本渲染效率**:每次循环都创建新的Surface对象
4. **代码组织**:游戏状态管理可以更清晰
5. **资源路径处理**:字体路径处理不够健壮
### 改进后的代码:
```python
import os
import sys
import pygame
import time as systime
from pygame.locals import *
# 初始化游戏对象
class Ghost:
def __init__(self):
pass
def move(self):
pass
class Pacman:
def __init__(self):
pass
def generate_notes(self):
pass
class Blackboard:
def __init__(self):
pass
def set_questions(self):
pass
class GameState:
MAIN_MENU = 0
ANIMATION = 1
CONVERSATION = 2
GAMEPLAY = 3
def draw_game_screen(
state,
show_buttons,
bar_height,
background_offset,
current_conversation_img,
text_lines,
text_progress
):
screen.fill((192, 192, 192))
if state != GameState.CONVERSATION:
# 滚动背景
screen.blit(background, (background_offset, -200))
screen.blit(conversation_1, (background_offset + background.get_width(), -200))
if background_offset < 0:
screen.blit(background, (background_offset + 2 * background.get_width(), -200))
else:
# 对话模式
screen.blit(current_conversation_img, (0, -200))
# 绘制UI元素
if show_buttons:
screen.blit(title_img, (0, 0))
screen.blit(button_start_img, (500, 500))
screen.blit(button_quit_img, (500, 570))
# 绘制黑边
if bar_height > 0:
pygame.draw.rect(screen, (0, 0, 0), (0, 0, screen.get_width(), bar_height))
pygame.draw.rect(screen, (0, 0, 0),
(0, screen.get_height() - bar_height,
screen.get_width(), bar_height))
# 对话文本渲染
if state == GameState.CONVERSATION and text_progress > 0:
# 创建文本背景(带透明度)
text_bg = pygame.Surface((screen.get_width(), bar_height * 2), pygame.SRCALPHA)
text_bg.fill((0, 0, 0, 200))
screen.blit(text_bg, (0, (screen.get_height() - bar_height * 2) // 2))
# 预渲染文本Surface
for i, line in enumerate(text_lines[:text_progress]):
if i < len(text_surfaces):
text_rect = text_surfaces[i].get_rect(
center=(screen.get_width() // 2,
screen.get_height() // 2 - 30 + i * 60)
)
screen.blit(text_surfaces[i], text_rect)
pygame.display.flip()
if __name__ == '__main__':
# 初始化资源路径
script_dir = os.path.dirname(os.path.abspath(__file__))
images_dir = os.path.join(script_dir, 'images')
fonts_dir = os.path.join(script_dir, 'fonts')
# 确保目录存在
os.makedirs(images_dir, exist_ok=True)
os.makedirs(fonts_dir, exist_ok=True)
# 初始化pygame
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("Mamlab")
# 字体路径处理
font_path = os.path.join(fonts_dir, "HYPixel11pxU-2.ttf")
if not os.path.exists(font_path):
print(f"字体文件不存在: {font_path}")
# 尝试查找替代字体
available_fonts = pygame.font.get_fonts()
if 'simhei' in available_fonts:
font_path = 'simhei'
elif 'arial' in available_fonts:
font_path = 'arial'
else:
font_path = None
# 初始化字体
try:
font = pygame.font.Font(font_path, 36) if font_path else pygame.font.SysFont(None, 36)
except Exception as e:
print(f"字体初始化失败: {e}")
font = pygame.font.SysFont(None, 36)
# 预渲染文本Surface
text_lines = [
"『你不该来这里的。』",
"『抱歉,朋友。我也没得选择。』"
]
text_surfaces = [
font.render(line, True, (255, 255, 255))
for line in text_lines
]
# 加载游戏资源
resource_loaded = True
try:
background = pygame.image.load(os.path.join(images_dir, 'background.png')).convert()
button_start_img = pygame.image.load(os.path.join(images_dir, 'button_start.png')).convert_alpha()
button_quit_img = pygame.image.load(os.path.join(images_dir, 'button_quit.png')).convert_alpha()
title_img = pygame.image.load(os.path.join(images_dir, 'title.png')).convert_alpha()
conversation_1 = pygame.image.load(os.path.join(images_dir, 'conversation_1.png')).convert_alpha()
conversation_2 = pygame.image.load(os.path.join(images_dir, 'conversation_2.png')).convert_alpha()
print("所有图片加载成功!")
except pygame.error as e:
print(f"图片加载失败: {e}")
print("请确保所有图片文件存在于 images/ 目录中")
resource_loaded = False
# 如果资源加载失败,显示错误信息
if not resource_loaded:
error_font = pygame.font.SysFont(None, 48)
error_surface = error_font.render("资源加载失败,按任意键退出", True, (255, 0, 0))
screen.blit(error_surface, (screen.get_width()//2 - 200, screen.get_height()//2))
pygame.display.flip()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN:
waiting = False
pygame.quit()
sys.exit(1)
# 初始化游戏对象
ghost = Ghost()
pacman = Pacman()
blackboard = Blackboard()
# 游戏状态管理
current_state = GameState.MAIN_MENU
clock = pygame.time.Clock()
# UI状态变量
show_buttons = True
start_button_rect = button_start_img.get_rect(topleft=(500, 500))
quit_button_rect = button_quit_img.get_rect(topleft=(500, 570))
# 动画变量
bar_height = 0
target_bar_height = screen.get_height() // 6
background_offset = 0
target_background_offset = -background.get_width()
animation_speed = 5
# 对话系统变量
current_conversation_img = conversation_1
last_switch_time = systime.time()
switch_interval = 0.5
text_progress = 0
last_text_time = 0
# 主游戏循环
running = True
while running:
clock.tick(60)
current_time = systime.time()
# 事件处理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if current_state == GameState.MAIN_MENU:
if start_button_rect.collidepoint(mouse_pos):
show_buttons = False
current_state = GameState.ANIMATION
elif quit_button_rect.collidepoint(mouse_pos):
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_f:
# 全屏切换
if pygame.display.is_fullscreen():
screen = pygame.display.set_mode((1280, 720))
else:
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
elif event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE and current_state == GameState.CONVERSATION:
# 文本进度控制
if text_progress < len(text_lines):
text_progress += 1
# 状态更新
if current_state == GameState.ANIMATION:
# 更新动画
if bar_height < target_bar_height:
bar_height = min(bar_height + animation_speed, target_bar_height)
if background_offset > target_background_offset:
background_offset = max(background_offset - animation_speed, target_background_offset)
# 检查动画完成
if bar_height == target_bar_height and background_offset == target_background_offset:
current_state = GameState.CONVERSATION
last_text_time = current_time
elif current_state == GameState.CONVERSATION:
# 对话图片切换
if current_time - last_switch_time > switch_interval:
current_conversation_img = conversation_2 if current_conversation_img == conversation_1 else conversation_1
last_switch_time = current_time
# 文本显示进度
if text_progress == 0 and current_time - last_text_time > 1.0:
text_progress = 1
last_text_time = current_time
elif text_progress == 1 and current_time - last_text_time > 3.0:
text_progress = 2
# 渲染游戏画面
draw_game_screen(
current_state,
show_buttons,
bar_height,
background_offset,
current_conversation_img,
text_lines,
text_progress
)
pygame.quit()
```
### 改进说明:
1. **字体加载优化**:
- 提前处理字体路径
- 添加字体回退机制
- 预渲染文本Surface提高性能
2. **资源加载改进**:
- 添加资源加载失败处理
- 显示友好的错误信息
- 使用`os.makedirs`确保目录存在
3. **状态管理**:
- 引入`GameState`枚举类管理游戏状态
- 分离状态更新和渲染逻辑
- 优化状态转换逻辑
4. **性能优化**:
- 文本Surface预渲染
- 减少循环内对象创建
- 优化动画更新逻辑
5. **代码组织**:
- 重命名函数为更具描述性的名称
- 分离游戏对象和UI逻辑
- 添加更多注释说明
6. **错误处理**:
- 添加资源加载失败处理
- 更健壮的全屏切换
- 改进字体回退机制