大家好,我是赛博红兔。欢迎来到《和我一起做3A游戏》第三集!这个系列是对pygame这个Python的游戏库的所有主要功能的介绍。通过这个教程,你应该能掌握制作任何2D游戏的工具,今天我们继续来做《归乡之路》这个打字游戏。先来回顾一下上一集的内容,我们设计了小猫的动画音效和打字的核心玩法,还有分数统计和显示。主要是了解了用矩形绘画,精灵这个pygame核心的类,还有游戏事件的触发和监听。那么今天,我们就在这个代码基础上继续做游戏。我们最后要完成的任务是创建背景的花草树木Trees和家House的类,毕竟小猫还需要回家。最后还要加上游戏BGM一首轻松愉快的背景音乐。今天,我们最主要要学习的核心是处理精灵之间的碰撞。
游戏完整代码及资源链接:https://pan.baidu.com/s/1AvAGRJI2ipDgyGEF0KJVdQ 提取码:gxzl
游戏GitHub项目:https://github.com/XingshengXu/A-Trip-Home
Trees类
class Trees(pg.sprite.Sprite):
def __init__(self, treeType):
super().__init__()
# Load Tree Image
file_path = TREE_TYPE.get(treeType)
tree_image = pg.image.load(file_path).convert_alpha()
if treeType not in ['grass_tree1', 'grass_tree2']:
tree_image = pg.transform.scale(tree_image, (TREE_WIDTH, TREE_HEIGHT))
self.image = tree_image
self.rect = self.image.get_rect(midbottom=(randint(WIDTH, WIDTH * 2), GROUND_HEIGHT))
def animation(self):
self.rect.x -= MOVING_SPEED
def destroy(self):
if self.rect.x <= -WIDTH:
self.kill()
def update(self):
self.animation()
self.destroy()
- 构造函数 (
__init__):super().__init__()调用父类的构造函数,为创建的树实例初始化所有的pygame精灵组件。file_path = TREE_TYPE.get(treeType)从TREE_TYPE字典中获取特定树型的文件路径。treeType参数决定树的种类。tree_image = pg.image.load(file_path).convert_alpha()加载树的图像文件,并转换格式以支持透明层(如果有的话),这使得树图像的边缘更加平滑。if treeType not in ['grass_tree1', 'grass_tree2']:检查树的类型,如果不是特定的两种草地树,则执行缩放。tree_image = pg.transform.scale(tree_image, (TREE_WIDTH, TREE_HEIGHT))缩放图像到指定的宽和高。self.image = tree_image将加载和可能缩放后的图像赋值给精灵的图像属性。self.rect = self.image.get_rect(midbottom=(randint(WIDTH, WIDTH * 2), GROUND_HEIGHT))设置精灵的矩形位置。这里midbottom属性表示图像底部中心的位置,randint(WIDTH, WIDTH * 2)生成一个随机的x坐标,使得树木可以在屏幕右侧的不同位置出现。
- 动画 (
animation):self.rect.x -= MOVING_SPEED更新树木的横向位置,通过每帧减去设定的移动速度,使树木向左移动。
- 销毁 (
destroy):if self.rect.x <= -WIDTH:检查树木是否移动到了屏幕左侧之外(即完全不可见)。self.kill()如果树木已经移出屏幕,从所有pygame精灵组和所有组中删除该树精灵,释放其资源。
- 更新 (
update):self.animation()调用动画方法,处理位置更新。self.destroy()调用销毁方法,检查是否需要从游戏中移除树木精灵。
House类
class House(pg.sprite.Sprite):
def __init__(self):
super().__init__()
house_image = pg.image.load(HOUSE).convert_alpha()
self.image = pg.transform.scale(
house_image, (HOUSE_WIDTH, HOUSE_HEIGHT))
self.rect = self.image.get_rect(midbottom=(
WIDTH + HOUSE_WIDTH, GROUND_HEIGHT + HOUSE_GROUND_OFFSET))
def animation(self):
self.rect.x -= MOVING_SPEED
def update(self):
self.animation()
- 构造函数 (
__init__):super().__init__()同样调用pygame精灵的构造函数,初始化精灵基础设置。house_image = pg.image.load(HOUSE).convert_alpha()加载房屋图像,并使其支持透明度。self.image = pg.transform.scale(house_image, (HOUSE_WIDTH, HOUSE_HEIGHT))将房屋图像缩放到指定尺寸。self.rect = self.image.get_rect(midbottom=(WIDTH + HOUSE_WIDTH, GROUND_HEIGHT + HOUSE_GROUND_OFFSET))设置房屋的位置,确保其出现在屏幕右侧,并调整y坐标以考虑地面高度偏移。
- 动画 (
animation):self.rect.x -= MOVING_SPEED更新房屋位置,使其与树木类似向左移动。
- 更新 (
update):self.animation()在每帧调用动画函数更新房屋的位置。
碰撞
def collision(self):
if collided_houses := pg.sprite.spritecollide(cat.sprite, house, False):
for collided_house in collided_houses:
if collided_house.rect.centerx <= cat.sprite.rect.centerx:
self.win_sound.play()
trees.empty()
house.empty()
return False
else:
return True
else:
return True
- 定义碰撞检测方法:
def collision(self):这一行定义了Game类中的collision方法。这个方法将用于检测游戏中的碰撞,并根据碰撞结果决定游戏是否继续。
- 检测碰撞:
if collided_houses := pg.sprite.spritecollide(cat.sprite, house, False):这行代码使用pg.sprite.spritecollide函数来检查cat.sprite(猫的精灵)和house(房屋的精灵组)之间是否发生了碰撞。这个函数返回一个列表,包含所有与猫发生碰撞的房屋精灵。参数False表示不使用像素级碰撞检测,即使用边界框(bounding boxes)进行碰撞检测。
- 处理碰撞结果:
for collided_house in collided_houses:这行代码遍历所有与猫发生碰撞的房屋精灵。对于每一个碰撞的房屋,执行以下判断和操作。if collided_house.rect.centerx <= cat.sprite.rect.centerx:检查碰撞的房屋的中心 x 坐标是否小于或等于猫的中心 x 坐标。这个条件通常用来判断房屋是否在猫的前方或正与猫重合。self.win_sound.play()如果上述条件为真,即碰撞的房屋位于猫的前方或重合,播放胜利的声音效果。trees.empty()清空所有树木精灵。empty()方法会移除所有精灵组中的精灵。house.empty()清空所有房屋精灵。同样使用empty()方法。return False返回False,表示游戏应该结束,因为发生了有效的碰撞。else:如果检查的房屋中心 x 坐标大于猫的中心 x 坐标,即房屋在猫的后方。return True返回True,表示游戏可以继续进行。
- 如果没有发生碰撞:
else:如果pg.sprite.spritecollide函数没有检测到任何碰撞,即collided_houses为空。return True同样返回True,表示游戏可以继续进行,因为没有发生碰撞。
Game类中的main_loop方法扩展
# Generate Tree
if event.type == self.tree_timer:
tree_type = choice(list(TREE_TYPE.keys()))
trees.add(Trees(tree_type))
# Generate House
if event.type == self.house_timer:
house.add(House())
# Reset Game Parameters and Timers
if event.type == pg.KEYDOWN and event.key == pg.K_SPACE:
self.game_active = True
text_target.sprite.score = 0
text_target.sprite.letter_count = 0
text_target.sprite.candidate = choice(WORDBANK)
text_target.sprite.update_text()
pg.time.set_timer(self.tree_timer, TREE_SPAWN_FREQ)
pg.time.set_timer(self.house_timer, HOUSE_SPAWN_FREQ)
# Play In-game Music
pg.mixer.music.stop()
pg.mixer.music.set_volume(0.3)
pg.mixer.music.play()
# Active/Deactivate Game Based on Collision Event
self.game_active = self.collision()
- 树木生成:
- 在游戏事件循环中,当检测到特定的定时器事件(
self.tree_timer),随机选择一个树木类型,并创建一个新的树木精灵,然后将其添加到树木组中。
- 在游戏事件循环中,当检测到特定的定时器事件(
- 房屋生成:
- 类似地,当房屋的定时器事件触发时(
self.house_timer),创建一个新的房屋精灵并添加到房屋组中。
- 类似地,当房屋的定时器事件触发时(
- 游戏重置:
- 如果玩家按下空格键,游戏的活动状态被重置为激活状态,同时重置得分和打字目标,重新设置树木和房屋的生成定时器,并重启背景音乐。
- 碰撞检测:
- 游戏会检查猫精灵和房屋精灵之间是否发生碰撞。如果检测到碰撞,根据碰撞的位置可能会结束游戏或继续进行。
好了,随着《归乡之路》的打字游戏的完成,我们的pygame基础教学就该一段落了,我们已经学到了pygame大部分的游戏制作功能。当然,大家可以看到,在我放出的这个完整版游戏里还有更多的内容,比如有游戏开始画面,有三首背景音乐循环播放,还有藏着一个彩蛋不知道到今天大家发现没有。这个彩蛋的触发也是用到了事件触发器。大家可以去研究我的源文件学习这些内容。不知道大家一路跟下来学到了多少,谢谢大家的陪伴,你有什么问题请给我留言。我还会继续放出其他的3A游戏,咱们一起继续边玩边学一些pygame和制作游戏相关高阶的内容。那就这样,咱们下期再见!

Leave a comment