Python学习笔记(七)--游戏项目(2)
前篇请见:
Python学习笔记(六)--游戏项目(上)
重构:模块game_functions
在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易拓展。我们将创建一个名为game_functions
的新模块,它将存储大量让《外星人入侵》运行的函数。通过创建模块game_functions
,可避免alien_invasion.py
太长,并使其逻辑更容易理解。
函数check_events()
我们首先把管理时间的代码移动到一个名为check_events()
的函数中,以简化`run_ganme()``并隔离事件管理循环。通过隔离时间循环,可将时间管理和游戏的其他方面(如更新屏幕)分离。
将check_events()
放在一个名为game_functions
的新模块中:
game_functions.py
文件
import sys
import pygame
def check_events():
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
主文件alien_invasion.py
:
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
# 初始化游戏并创建一个屏幕对象,设置和pygame
pygame.init()
#seting类
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Aline Invasion")
#创建一艘飞船
ship = Ship(screen)
#游戏主循环
while True:
gf.check_events()
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 重绘飞船
#让最近绘制的屏幕可见
pygame.display.flip()
run_game()
函数update_screen()
为进一步简化run_game()
,下面将更新屏幕的代码移动到update_screen()
的函数中,并将这个函数放在模块game_functions.py
中:
import sys
import pygame
def check_events():
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def update_screen(ai_settings,screen,ship):
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 重绘飞船
# 让最近绘制的屏幕可见
pygame.display.flip()
新函数update_screen()
包含三个形参,ai_settings
,screen
,ship
。现在需要将alien_invasion.py
中的while循环中更新屏幕的代码替换为对函数update_sceen()
的调用。
alien_invasion.py
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
# 初始化游戏并创建一个屏幕对象,设置和pygame
pygame.init()
#seting类
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Aline Invasion")
#创建一艘飞船
ship = Ship(screen)
#游戏主循环
while True:
gf.check_events()
gf.update_screen(ai_settings,screen,ship)
run_game()
这两个函数让while循环变得更加简单,并让后续开发变得更加容易。(在模块game_functions
中而不是run_game
中完成大部分工作。)
对代码进行重构使其更容易拓展后,我们可以开始处理游戏的动态方面 了。
驾驶飞船
响应按键
每当用户按键是,都将在Pygame中注册一个事件。事件都是通过方法pygame.event.getf()
获取的,因此在函数check_events()
中,我们需要指定要检查哪些类型的事件。每次按键都被注册为一个KEYDOWN
事件。
检测到KEYDOWN
事件时,我们需要检查按下的是否是特定的键。例如,如果按下的是右箭头键,我们就增大飞船的rect.centerx
值,将飞船向右移动:
game_ functions.py
import sys
import pygame
def check_events(ship):
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
#向右移动飞船
ship.rect.centerx += 1
def update_screen(ai_settings,screen,ship):
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 重绘飞船
# 让最近绘制的屏幕可见
pygame.display.flip()
我们在函数check_events()
中包含形参ship,因为玩家按右箭头键时,需要将飞船向右移动。在函数check_events()
内部,我们在事件循环中添加了一个elif代码块,以便在Pygame 检测到KEYDOWN事件时作出响应。
我们读取属性event.key
,以检查按下的是否是右箭头键。如果按下的是右箭头键,就将ship.rect.centerx
的值加1,从而将飞船向右移动。
(这里需要在alien_invasion.py
中更新函数的调用)
允许不断移动
玩家按住右箭头键不放时,我们希望飞船不断地向右移动,直到玩家松开为止。我们将让游戏检测pygame.KEYUP
事件,以便玩家松开右箭头键时我们能够知道这一点;然后,我们将结合使用KEYDOWN和KEYUP事件,以及一个名为moving_right
的标志来实现持续移动。
飞船不动时,标志moving_right
将为False。玩家按下右箭头键时,我们将这个标志设置为True;而玩家松开时,我们将这个标志重新设置为False。
飞船的属性都由Ship类控制,因此我们将给这个类添加一个名为moving_right
的属性和一个名为update()
的方法。方法update()
检查标志moving_right
的状态,如果这个标志为True,就调整飞船的位置。每当需要调整飞船的位置时,我们都调用这个方法。
更新ship.py
:
import pygame
class Ship():
def __init__(self, screen):
self.screen = screen
#加载飞船图片并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# 移动标志
self.moving_right = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.centerx +=1
def blitme(self):
""""在指定位置绘制飞船"""
self.screen.blit(self.image,self.rect)
在方法__init__()
中,我们添加了属性self.moving_right
,并将其初始值设置为False
。
接下来,我们添加了方法update()
,它在前述标识为True时向右移动飞船。
下面来修改check_events()
,使其在玩家按下右箭头键时将moving_right
甚至为True,并在玩家松开时将moving_right
设置为False。
import sys
import pygame
def check_events(ship):
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
# 修改移动参数
ship.moving_right = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
def update_screen(ai_settings,screen,ship):
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 重绘飞船
# 让最近绘制的屏幕可见
pygame.display.flip()
我们修改了游戏在玩家按下右箭头键时响应的方式:不直接调整飞船的位置,而只是将moving_right设置为True。我们添加了一个新的elif代码块,用于响应KEYUP事件:玩家松开右箭头键(K_RIGHT)时,我们将moving_right设置为False。
最后,我们需要修改alien_invasion.py中的while循环,以便每次执行循环时都调用飞船的方法update()
:
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
# 初始化游戏并创建一个屏幕对象,设置和pygame
pygame.init()
# setting类
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Aline Invasion")
# 创建一艘飞船
ship = Ship(screen)
# 游戏主循环
while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)
run_game()
飞船的位置将在检测到键盘事件后(但在更新屏幕前)更新。这样,玩家输入时,飞船的位置将更新,从而确保使用更新后的位置将飞船绘制到屏幕上。
左右移动
飞船能够不断地向右移动后,添加向左移动的逻辑很容易。我们将再次修改Ship类和函数check_events()
。
import pygame
class Ship():
def __init__(self, screen):
self.screen = screen
#加载飞船图片并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.centerx +=1
if self.moving_left:
self.rect.centerx -=1
def blitme(self):
""""在指定位置绘制飞船"""
self.screen.blit(self.image,self.rect)
在方法__init__()
中,我们添加了标志self.moving_left
;在方法update()中,我们添加了一个if代码块而不是elif代码块,这样如果玩家同时按下了左右箭头键,将先增大飞船的rect.centerx
值,再降低这个值,即飞船的位置保持不变。
import sys
import pygame
def check_events(ship):
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
# 修改移动参数
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def update_screen(ai_settings,screen,ship):
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 重绘飞船
# 让最近绘制的屏幕可见
pygame.display.flip()
调整飞船的速度
当前,每次执行while循环时,飞船最多移动1像素,但我们可以在Settings类中添加属性ship_speed_factor
,用于控制飞船的速度。我们将根据这个属性决定飞船在每次循环时最多移动多少距离.
settings.py
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (20, 46, 93)
# 飞船速度的设置
self.ship_speed_factor = 1.5
本行代码加在__init__()
中,但是rect
的centerx
等属性只能存储整数值,因此我们需要对ship类做些修改
import pygame
class Ship():
def __init__(self, ai_settings, screen):
self.screen = screen
self.ai_settings = ai_settings
#加载飞船图片并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.center = float(self.rect.centerx)
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
# 更新飞船的center值而不是rect
if self.moving_right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left:
self.center -= self.ai_settings.ship_speed_factor
# 根据self.center更新rect对象
self.rect.centerx = self.center
def blitme(self):
""""在指定位置绘制飞船"""
self.screen.blit(self.image,self.rect)
首先,我们在__init__()
的形参列表中添加了ai_settings
,让飞船能够获取其速度设置。接下来,我们将形参ai_settings
的值存储在一个属性中,以便能在update()
中使用它。鉴于现在调整飞船的位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一个能够存储小数值的变量中。可以使用小数来设置rect
的属性,但rect将只存储这个值的整数部分。为准确地存储飞船的位置,我们定义了一个可存储小数值的新属性self.center
。我们使用函数float()
将self.rect.centerx
的值转换为小数,并将结果存储到self.center
中。
在alien_invasion.py
中创建传入实参ai_settings
:
# 创建一艘飞船
ship = Ship(ai_settings, screen)
现在,只要ship_speed_factor
的值大于1,飞船的移动速度就会比以前更快。
限制飞船的活动范围
当前,如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面,消失得无影无踪。因此我们要限制飞船的活动范围,让飞船到达屏幕边缘后停止移动。
我们修改Ship类的方法update()
:
import pygame
class Ship():
def __init__(self, ai_settings, screen):
self.screen = screen
self.ai_settings = ai_settings
# 加载飞船图片并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# 在飞船的属性center中存储小数值
self.center = float(self.rect.centerx)
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
# 更新飞船的center值而不是rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# 根据self.center更新rect对象
self.rect.centerx = self.center
def blitme(self):
""""在指定位置绘制飞船"""
self.screen.blit(self.image,self.rect)
注意update()函数
def update(self):
"""根据移动标志调整飞船的位置"""
# 更新飞船的center值,而不是rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# 根据self.center更新rect对象
self.rect.centerx = self.center
重构check_events()
随着我们开发的进行,函数check_event()
变得越来越长,我们将其部分代码放在两个函数中:
一个处理KEYDOWN
函数,另一个处理KEYUP
import sys
import pygame
def check_keydown_events(event, ship):
"""响应按键"""
if event.key == pygame.K_RIGHT:
# 修改移动参数
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
def check_keyup_events(event, ship):
"""响应松开"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ship):
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings,screen,ship):
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme() # 重绘飞船
# 让最近绘制的屏幕可见
pygame.display.flip()
我们创建了两个新函数:check_keydown_events()
和check_keyup_events()
,它们都包含形参event
和ship
。这两个函数的代码是从check_events()
中复制而来的,因此我们将函数check_events
中相应的代码替换成了对这两个函数的调用。现在,函数check_events()
更简单,代码结构更清晰。这样,在其中响应其他玩家输入时将更容易。
后续内容详见下节博客。
本文来自作者:CK_0ff,转载请注明原文链接:https://www.cnblogs.com/Ck-0ff/p/15843817.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~