打开网易新闻 查看更多图片

导读:四处奔跑躲避敌人是一回事,反击敌人是另一回事。学习如何在这系列的第十二篇文章中在 Pygame 中创建平台游戏。

本文字数:18629,阅读时长大约: 21分钟

https://linux.cn/article-12872-1.html
作者:Seth Kenlon
译者:郑

这是仍在进行中的关于使用 模块在 中创建电脑游戏的第十二部分。先前的文章是:

我的上一篇文章本来是这一系列文章的最后一篇,它鼓励你为这个游戏编写自己的附加程序。你们很多人都这么做了!我收到了一些电子邮件,要求帮助我还没有涵盖的常用机制:战斗。毕竟,跳起来躲避坏人是一回事,但是有时候让他们走开是一件非常令人满意的事。在电脑游戏中向你的敌人投掷一些物品是很常见的,不管是一个火球、一支箭、一道闪电,还是其它适合游戏的东西。

与迄今为止你在这个系列中为你的平台游戏编程的任何东西不同,可投掷物品有一个生存时间。在你投掷一个物品后,它会如期在移动一段距离后消失。如果它是一支箭或其它类似的东西,它可能会在通过屏幕的边缘时而消失。如果它是一个火球或一道闪电,它可能会在一段时间后熄灭。

这意味着每次生成一个可投掷的物品时,也必须生成一个独特的衡量其生存时间的标准。为了介绍这个概念,这篇文章演示如何一次只投掷一个物品。(换句话说,每次仅存在一个投掷物品)。一方面,这是一个游戏的限制条件,但另一方面,它却是游戏本身的运行机制。你的玩家不能每次同时投掷 50 个火球,因为每次仅允许一个投掷物品,所以当你的玩家释放一个火球来尝试击中一名敌人就成为了一项挑战。而在幕后,这也使你的代码保持简单。

如果你想启用每次投掷多个项目,在完成这篇教程后,通过学习这篇教程所获取的知识来挑战你自己。

创建 Throwable 类

如果你跟随学习这系列的其它文章,那么你应该熟悉在屏幕上生成一个新的对象基础的__init__函数。这和你用来生成你的 和 的函数是一样的。这里是生成一个throwable对象的__init__函数来:

  1. class Throwable(pygame.sprite.Sprite):

  2. """

  3. 生成一个 throwable 对象

  4. """

  5. def __init__(self, x, y, img, throw):

  6. pygame.sprite.Sprite.__init__(self)

  7. self.image = pygame.image.load(os.path.join('images',img))

  8. self.image.convert_alpha()

  9. self.image.set_colorkey(ALPHA)

  10. self.rect = self.image.get_rect()

  11. self.rect.x = x

  12. self.rect.y = y

  13. self.firing = throw

同你的Player类或Enemy类的__init__函数相比,这个函数的主要区别是,它有一个self.firing变量。这个变量保持跟踪一个投掷的物品是否在当前屏幕上活动,因此当一个throwable对象创建时,将变量设置为1的合乎情理的。

判断存活时间

接下来,就像使用PlayerEnemy一样,你需要一个update函数,以便投掷的物品在瞄准敌人抛向空中时,它会自己移动。

测定一个投掷的物品存活时间的最简单方法是侦测它何时离开屏幕。你需要监视的屏幕边缘取决于你投掷的物品的物理特性。

如果你的玩家正在投掷的物品是沿着水平轴快速移动的,像一只弩箭或箭或一股非常快的魔法力量,而你想监视你游戏屏幕的水平轴极限。这可以通过worldx定义。

如果你的玩家正在投掷的物品是沿着垂直方向或同时沿着水平方向和垂直方向移动的,那么你必须监视你游戏屏幕的垂直轴极限。这可以通过worldy定义。

这个示例假设你投掷的物品向前移动一点并最终落到地面上。不过,投掷的物品不会从地面上反弹起来,而是继续掉落出屏幕。你可以尝试不同的设置来看看什么最适合你的游戏:

  1. def update(self,worldy):

  2. '''

  3. 投掷物理学

  4. '''

  5. if self.rect.y < worldy: #垂直轴

  6. self.rect.x += 15 #它向前移动的速度有多快

  7. self.rect.y += 5 #它掉落的速度有多快

  8. else:

  9. self.kill() #移除投掷对象

  10. self.firing = 0 #解除火力发射

为使你的投掷物品移动地更快,增加self.rect的动量值。

如果投掷物品不在屏幕上,那么该物品将被销毁,以及释放其所占用的寄存器。另外,self.firing将被设置回0以允许你的玩家来进行另一次射击。

设置你的投掷对象

就像使用你的玩家和敌人一样,你必须在你的设置部分中创建一个精灵组来保持投掷对象。

此外,你必须创建一个非活动的投掷对象来供开始的游戏使用。如果在游戏开始时却没有一个投掷对象,那么玩家在第一次尝试投掷一柄武器时,投掷将失败。

这个示例假设你的玩家使用一个火球作为开始的武器,因此,每一个投掷实例都是由fire变量指派的。在后面的关卡中,当玩家获取新的技能时,你可以使用相同的Throwable类来引入一个新的变量以使用一张不同的图像。

在这代码块中,前两行已经在你的代码中,因此不要重新键入它们:

  1. player_list = pygame.sprite.Group() #上下文

  2. player_list.add(player) #上下文

  3. fire = Throwable(player.rect.x,player.rect.y,'fire.png',0)

  4. firepower = pygame.sprite.Group()

注意,每一个投掷对象的起始位置都是和玩家所在的位置相同。这使得它看起来像是投掷对象来自玩家。在第一个火球生成时,使用0来显示self.firing是可用的。

在主循环中获取投掷行为

没有在主循环中出现的代码不会在游戏中使用,因此你需要在你的主循环中添加一些东西,以便能在你的游戏世界中获取投掷对象。

首先,添加玩家控制。当前,你没有火力触发器。在键盘上的按键是有两种状态的:释放的按键,按下的按键。为了移动,你要使用这两种状态:按下按键来启动玩家移动,释放按键来停止玩家移动。开火仅需要一个信号。你使用哪个按键事件(按键按下或按键释放)来触发你的投掷对象取决于你的品味。

在这个代码语句块中,前两行是用于上下文的:

  1. if event.key == pygame.K_UP or event.key == ord('w'):

  2. player.jump(platform_list)

  3. if event.key == pygame.K_SPACE:

  4. if not fire.firing:

  5. fire = Throwable(player.rect.x,player.rect.y,'fire.png',1)

  6. firepower.add(fire)

与你在设置部分创建的火球不同,你使用一个1来设置self.firing为不可用。

最后,你必须更新和绘制你的投掷物品。这个顺序很重要,因此把这段代码放置到你现有的enemy.moveplayer_list.draw的代码行之间:

  1. enemy.move() # 上下文

  2. if fire.firing:

  3. fire.update(worldy)

  4. firepower.draw(world)

  5. player_list.draw(screen) # 上下文

  6. enemy_list.draw(screen) # 上下文

注意,这些更新仅在self.firing变量被设置为 1 时执行。如果它被设置为 0 ,那么fire.firing就不为true,接下来就跳过更新。如果你尝试做上述这些更新,不管怎样,你的游戏都会崩溃,因为在游戏中将不会更新或绘制一个fire对象。

启动你的游戏,尝试挑战你的武器。

检测碰撞

如果你玩使用了新投掷技巧的游戏,你可能会注意到,你可以投掷对象,但是它却不会对你的敌人有任何影响。

原因是你的敌人没有被查到碰撞事故。一名敌人可能会被你的投掷物品所击中,但是敌人却从来不知道被击中了。

你已经在你的Player类中完成了碰撞检测,这非常类似。在你的Enemy类中,添加一个新的update函数:

  1. def update(self,firepower, enemy_list):

  2. """

  3. 检测火力碰撞

  4. """

  5. fire_hit_list = pygame.sprite.spritecollide(self,firepower,False)

  6. for fire in fire_hit_list:

  7. enemy_list.remove(self)

代码很简单。每个敌人对象都检查并看看它自己是否被firepower精灵组的成员所击中。如果它被击中,那么敌人就会从敌人组中移除和消失。

为集成这些功能到你的游戏之中,在主循环中调用位于新触发语句块中的函数:

  1. if fire.firing: # 上下文

  2. fire.update(worldy) # 上下文

  3. firepower.draw(screen) # 上下文

  4. enemy_list.update(firepower,enemy_list) # 更新敌人

你现在可以尝试一下你的游戏了,大多数的事情都如预期般的那样工作。不过,这里仍然有一个问题,那就是投掷的方向。

更改投掷机制的方向

当前,你英雄的火球只会向右移动。这是因为Throwable类的update函数将像素添加到火球的位置,在 Pygame 中,在 X 轴上一个较大的数字意味着向屏幕的右侧移动。当你的英雄转向另一个方向时,你可能希望它投掷的火球也抛向左侧。

到目前为止,你已经知道如何实现这一点,至少在技术上是这样的。然而,最简单的解决方案却是使用一个变量,在一定程度上对你来说可能是一种新的方法。一般来说,你可以“设置一个标记”(有时也被称为“翻转一个位”)来标明你的英雄所面向的方向。在你做完后,你就可以检查这个变量来得知火球是向左移动还是向右移动。

首先,在你的Player类中创建一个新的变量来代表你的游戏所面向的方向。因为我的游戏天然地面向右侧,由此我把面向右侧作为默认值:

  1. self.score = 0

  2. self.facing_right = True # 添加这行

  3. self.is_jumping = True

当这个变量是True时,你的英雄精灵是面向右侧的。当玩家每次更改英雄的方向时,变量也必须重新设置,因此,在你的主循环中相关的keyup事件中这样做:

  1. if event.type == pygame.KEYUP:

  2. if event.key == pygame.K_LEFT or event.key == ord('a'):

  3. player.control(steps, 0)

  4. player.facing_right = False # 添加这行

  5. if event.key == pygame.K_RIGHT or event.key == ord('d'):

  6. player.control(-steps, 0)

  7. player.facing_right = True # 添加这行

最后,更改你的Throwable类的update函数,以检测英雄是否面向右侧,并恰当地添加或减去来自火球位置的像素:

  1. if self.rect.y < worldy:

  2. if player.facing_right:

  3. self.rect.x += 15

  4. else:

  5. self.rect.x -= 15

  6. self.rect.y += 5

再次尝试你的游戏,清除掉你游戏世界中的一些坏人。

Python 平台类使用投掷能力

作为一项额外的挑战,当彻底打败敌人时,尝试增加你玩家的得分。

via:

作者: 选题: 译者: 校对:

本文由 原创编译, 荣誉推出