几十年来,平台游戏一直是深受玩家喜爱的游戏类型,它提供了令人兴奋的挑战和令人怀念的游戏玩法。在本教程中,我们将指导您使用 Python 中的 PyGame 库构建自己的平台游戏。
无论您是希望深入游戏开发的初学者,还是希望探索 Pygame 的资深程序员,本教程都将为您提供实现平台游戏创意所需的知识和技能。
在本教程中,我们将介绍平台游戏的基本组成部分,包括:
- 安装和设置
- 添加基本游戏组件
- 游戏循环
- 游戏世界
- 世界设置
- 添加世界功能
- 移动和处理碰撞
- 处理世界陷阱
- 更新世界变化
- 世界组件:瓦块、目标和陷阱
- 添加玩家
- 玩家动画
- 添加玩家能力
- 识别玩家活动
- 更新玩家
- 应用游戏目标
- 添加游戏指标
- 游戏检查
- 总结
我们将指导您逐步完成整个过程,并解释每个功能背后的代码和概念。
本教程结束时,您将拥有一个可以自豪地与他人分享的游戏,并可以根据自己的喜好进一步定制。话不多说,让我们开始吧。
书接上回
添加玩家
现在我们的游戏世界已经有了玩家角色可以交互的对象,我们可以开始在游戏中添加玩家角色了。在 player.py 中创建一个名为 Player 的新类,它是 pygame.sprite.Sprite 的子类,代表游戏世界中的玩家角色:
# player.py
import pygame
from support import import_spriteclass Player(pygame.sprite.Sprite):def __init__(self, pos):super().__init__()self._import_character_assets()self.frame_index = 0self.animation_speed = 0.15self.image = self.animations["idle"][self.frame_index]self.rect = self.image.get_rect(topleft=pos)self.mask = pygame.mask.from_surface(self.image)# player movementself.direction = pygame.math.Vector2(0, 0)self.speed = 5self.jump_move = -16# player statusself.life = 5self.game_over = Falseself.win = Falseself.status = "idle"self.facing_right = Trueself.on_ground = Falseself.on_ceiling = Falseself.on_left = Falseself.on_right = False# gets all the image needed for animating specific player actiondef _import_character_assets(self):character_path = "assets/player/"self.animations = {"idle": [], "walk": [],"jump": [], "fall": [], "lose": [], "win": []}for animation in self.animations.keys():full_path = character_path + animationself.animations[animation] = import_sprite(full_path)
在__init__()方法中;_import_character_assets 方法被调用,以根据动画类型从不同的目录中导入并存储播放器动画的精灵图像。frame_index 属性初始化为 0,表示动画的当前帧。animation_speed 属性设置为 0.15,表示每个动画帧变化之间的时间间隔。self.image 属性将根据 frame_index 从动画字典中的 "idle "动画中分配初始图像。通过 get_rect(),rect 属性被设置为与图像相邻的矩形,topleft 参数被设置为提供的位置。使用 pygame.mask.from_surface 创建遮罩属性,为玩家定义像素级碰撞检测。
播放器类还为播放器的移动和状态定义了各种属性。direction 属性是一个 pygame.math.Vector2 对象,代表玩家的移动方向。speed 属性设置为 5,表示玩家的移动速度。jump_move 属性设置为 -16,表示玩家跳跃时的垂直移动。life 属性设置为 5,表示玩家的剩余生命值或健康值。game_over 属性初始设置为 “false”,表示游戏是否结束。win 属性初始也设置为 “false”,表示玩家是否取得了胜利。状态属性设置为 “空闲”,表示玩家当前的动画状态。facing_right 属性初始设置为 True,表示玩家朝右。on_ground、on_ceiling、on_left 和 on_right 属性初始设置为 False,分别代表玩家与地面、天花板、左侧和右侧的接触。
玩家动画
对于玩家正在进行的每项活动,无论是行走、跳跃还是简单的站立,我们都会为玩家的每项活动或动作制作大量动画。就像我们在陷阱类中为刀片制作动画一样,我们也将使用类似的逻辑为游戏中的玩家角色制作动画:
# player.py# animates the player actionsdef _animate(self):animation = self.animations[self.status]# loop over frame indexself.frame_index += self.animation_speedif self.frame_index >= len(animation):self.frame_index = 0image = animation[int(self.frame_index)]image = pygame.transform.scale(image, (35, 50))if self.facing_right:self.image = imageelse:flipped_image = pygame.transform.flip(image, True, False)self.image = flipped_image# set the rectif self.on_ground and self.on_right:self.rect = self.image.get_rect(bottomright = self.rect.bottomright)elif self.on_ground and self.on_left:self.rect = self.image.get_rect(bottomleft = self.rect.bottomleft)elif self.on_ground:self.rect = self.image.get_rect(midbottom = self.rect.midbottom)elif self.on_ceiling and self.on_right:self.rect = self.image.get_rect(topright = self.rect.topright)elif self.on_ceiling and self.on_left:self.rect = self.image.get_rect(bottomleft = self.rect.topleft)elif self.on_ceiling:self.rect = self.image.get_rect(midtop = self.rect.midtop)
_animate()方法负责通过循环播放当前动画的帧数来制作玩家角色的动画。首先,该方法使用 self.status 从动画字典中获取与玩家当前状态相对应的动画。接下来,frame_index 会以 self.animation_speed 递增,self.animation_speed 表示动画帧的变化速度。如果 frame_index 超过了动画帧的长度,它就会被重置为 0,从而循环回到第一帧。然后,根据 self.frame_index 的整数值从动画中获取当前帧图像。使用 pygame.transform.scale() 将图像缩放为 (35, 50) 大小。根据播放器的 facing_right 属性,如果播放器朝右,则直接将图像分配给 self.image。否则,如果玩家朝左,则使用 pygame.transform.flip() 水平翻转图像,并将其赋值给 self.image。
随后的代码块会根据玩家的当前状态设置其 rect 属性。玩家矩形(self.rect)的位置和方向会根据玩家是在地面还是天花板上,是在左侧还是右侧进行调整。具体的矩形分配会根据玩家的 on_ground、on_ceiling、on_left 和 on_right 属性组合而变化。
增加玩家能力
在游戏中,玩家应该会向左或向右行走和跳跃。让我们为移动和跳跃添加另外两个函数,命名为 _get_input() 和 _jump()。
# player.py# checks if the player is moving towards left or right or not movingdef _get_input(self, player_event):if player_event != False:if player_event == "right":self.direction.x = 1self.facing_right = Trueelif player_event == "left":self.direction.x = -1self.facing_right = Falseelse:self.direction.x = 0def _jump(self):self.direction.y = self.jump_move
_get_input()方法以 player_event 为参数,它代表来自玩家的输入事件或动作。如果 player_event 不是 False(意味着发生了输入事件),那么如果 player_event 等于 “right”,它就会将玩家方向向量的 x 分量设为 1,表示向右移动。此外,它还会将 facing_right 属性设置为 True,以表示玩家正朝右移动。
如果 player_event 等于 “left”(左),则会将玩家方向向量的 x 分量设置为-1,表示向左移动。它还会将 facing_right 属性设置为 “False”(假),以表示玩家朝左移动。如果 player_event 为 “false”(无输入事件),那么播放器方向向量的 x 分量将被设置为 0,表示没有水平移动。
_jump()方法负责启动玩家的跳跃。它将播放器方向向量的 y 分量设置为 self.jump_move 的值,表示播放器跳跃时的垂直运动。
识别玩家活动
# player.py# identifies player actiondef _get_status(self):if self.direction.y < 0:self.status = "jump"elif self.direction.y > 1:self.status = "fall"elif self.direction.x != 0:self.status = "walk"else:self.status = "idle"
_get_status()函数通过查看玩家精灵在方向属性中的矢量,查看其 x 坐标和 y 坐标的变化,从而为不同的玩家动作确定合适的动画。如果 self.direction.y 小于 0,说明玩家正在跳跃,因此我们会将 self.status 设置为 “跳跃”。反之亦然,如果 self.direction.y 大于 1,意味着玩家正在下落。如果 self.direction.x 不为 0,意味着玩家正在移动,因此我们会将 self.status 设置为 “walk”。最后,如果上述条件都不为真,我们将把 self.status 设置为 “空闲”,作为没有检测到玩家动作时的默认动画。
更新玩家
# player.py# update the player's statedef update(self, player_event):self._get_status()if self.life > 0 and not self.game_over:if player_event == "space" and self.on_ground:self._jump()else:self._get_input(player_event)elif self.game_over and self.win:self.direction.x = 0self.status = "win"else:self.direction.x = 0self.status = "lose"self._animate()
update() 方法首先调用 _get_status() 方法,根据播放器的当前状态更新播放器的状态。
如果玩家的生命值大于 0 且游戏尚未结束,则会根据 player_event 输入评估进一步的操作。如果 player_event 为 “space”(表示跳跃输入),且玩家当前在地面上,则会调用 _jump() 方法启动跳跃。如果 player_event 不是 “space”,或者玩家不在地面上,则会调用 _get_input() 方法来处理输入,并相应地更新玩家的移动。
如果游戏结束且玩家获胜(game_over 为 True,win 为 True),玩家的水平移动会被设置为 0,玩家的状态会被设置为 “win”。如果游戏结束且玩家输了(game_over 为 True,win 为 False),则玩家的水平移动将设为 0,状态设为 “输”。调用 _animate() 方法可更新玩家的动画。
应用游戏目标
对于我们需要的最后一个游戏类,请在 game.py 中创建另一个类并命名为 Game。它负责跟踪游戏的当前状态、游戏是否结束以及玩家是赢了还是输了:
# game.py
import pygame
from settings import HEIGHT, WIDTHpygame.font.init()class Game:def __init__(self, screen):self.screen = screenself.font = pygame.font.SysFont("impact", 70)self.message_color = pygame.Color("darkorange")
添加游戏指示器
我们将在 Game 类中添加几个函数,它们可以帮助用户识别游戏的最新状态。
让我们先创建一个简单的函数,用于在游戏屏幕上显示玩家的剩余生命或健康状况:
# game.pydef show_life(self, player):life_size = 30img_path = "assets/life/life.png"life_image = pygame.image.load(img_path)life_image = pygame.transform.scale(life_image, (life_size, life_size))# life_rect = life_image.get_rect(topleft = pos)indent = 0for life in range(player.life):indent += life_sizeself.screen.blit(life_image, (indent, life_size))
在 show_life() 方法中,life_size 变量被设置为 30,表示生命指示图像的大小。生命图像(“assets/life/life.png”)使用 pygame.image.load() 加载,并按 life_size 缩放,然后存储在 life_image 变量中。
名为 indent 的变量初始化为 0,该变量代表屏幕上显示每个生命指示器的水平位置。根据存储在 player.life 中的剩余生命值执行循环。
在循环的每次迭代中,缩进值都会按 life_size 递增,以确保每个生命值都显示在前一个生命值的旁边。使用 screen.blit() 方法将 life_image 显示在屏幕上,其中 life_image 是要显示的图像,而 (indent, life_size) 坐标代表生命指示器的位置。
我们还要添加一些函数,用于根据游戏状态返回信息。在 Game 类中创建 _game_lose() 和 _game_win() 方法:
# game.py# if player ran out of life or fell below the platformdef _game_lose(self, player):player.game_over = Truemessage = self.font.render('You Lose...', True, self.message_color)self.screen.blit(message,(WIDTH // 3 + 70, 70))# if player reach the goaldef _game_win(self, player):player.game_over = Trueplayer.win = Truemessage = self.font.render('You Win!!', True, self.message_color)self.screen.blit(message,(WIDTH // 3, 70))
游戏检查
让我们再创建一个分析当前游戏状态的函数。在 Game 类中创建另一个方法,并将其命名为 game_state() 方法:
# game.py# checks if the game is over or not, and if win or losedef game_state(self, player, goal):if player.life <= 0 or player.rect.y >= HEIGHT:self._game_lose(player)elif player.rect.colliderect(goal.rect):self._game_win(player)
game_state() 方法有两个参数:player 代表玩家对象,goal 代表游戏中的目标对象。
如果玩家的剩余生命值(player.life)小于或等于 0,或者玩家的垂直位置(player.rect.y)大于或等于屏幕高度(HEIGHT)(当玩家跌落到障碍物下方时,此声明为真),则表示游戏结束,玩家输了。在这种情况下,将调用 _game_lose() 方法来处理游戏结束状态。
如果玩家的矩形(player.rect)与目标的矩形(goal.rect)相撞,则表示玩家已到达目标并赢得了游戏。在这种情况下,将调用 _game_win() 方法来处理游戏结束状态。如果上述条件都不满足,表明游戏仍在进行中,game_state() 方法不会做任何操作。
现在,我们已经完成了平台游戏的编码工作。要测试我们的游戏,请打开终端,进入 "Platformer-Game "文件夹。进入目录后,输入 python main.py 运行游戏。下面是游戏的一些快照:
结论
最后,本教程介绍了如何使用 Python 的 Pygame 库创建一款平台游戏。我们探索了游戏的不同组成部分,包括玩家角色、世界对象、动画、碰撞和游戏状态。通过本教程,您已经学会了如何从零开始开发一个基本的平台游戏,理解了一些关键概念,如精灵处理、输入处理、重力、滚动和输赢条件。
利用从本教程中学到的知识,您可以通过添加新功能、新关卡或新机制来进一步增强和定制您的平台游戏。Pygame 为游戏开发提供了一个多功能框架,让您尽情释放创造力,打造引人入胜的游戏体验。您可以随意探索 Pygame 的其他功能,如音效、关卡设计或敌人 AI,从而将您的游戏提升到一个新的水平。希望本教程对您使用 Pygame 创建平台游戏的旅程有所帮助。