在本系列的第5部分,从零开始的Python游戏中,添加一个坏人让你的英雄去战斗!
在本系列的前几篇文章(请参阅第1部分,第2部分,第3部分和第4部分)中,您学习了如何使用Pygame和Python在尚未出现的空白游戏世界中构建可玩角色。但是,没有恶人可打的英雄岂不是很难受?
如果没有敌人,这将是一个非常无聊的游戏,因此在本文中,您将为游戏添加一个敌人并加入用于构建关卡的框架。
当还需要做更多工作以使玩家精灵充分发挥作用时,跳到敌人身上似乎显得很奇怪,但是你已经从前面的文章学到了很多,创建敌人与创建玩家精灵非常相似。因此,放轻松,利用已经掌握的知识,看看我们还有那些问题需要解决。
对于本次练习,您可以从Open Game Art下载一些预设的资料。 这是我使用的一些资料:
·Sprites, characters, objects, 和effects
是的,无论你是否意识到,你基本上已经知道如何去处理一个敌人目标了。该过程与创建玩家对象是非常相似的:
1.创建一个class让敌人能够生成。
2.创建一个 update 函数这样敌人就可以检测到碰撞。
3.创建一个move函数这样你的敌人就可以到处移动。
咱们先从class开始。从概念上讲,它与Player的class基本相同。 设置一个图像或一系列图像,然后设置对象的起始位置.
在开始之前,请确保您有敌人的图像文件,即使这只是临时图像也可以。 把图像文件放在你工程中的images目录里 (跟放置Player图片的目录是一样的).
如果画面生动活泼,那么游戏看起来会好很多。对敌方对象进行动画处理的方法与为玩家对象进行动画处理的方法相同。不过目前,先暂时保持简单,并使用非动画对象。
在你代码的objects部分的顶部, 使用此代码创建一个名为Enemy的类:
class Enemy(pygame.sprite.Sprite):
'''
Spawn an enemy
'''
def __init__(self,x,y,img):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('images',img))
self.image.convert_alpha()
self.image.set_colorkey(ALPHA)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
如果要为敌人设置动画,请执行与player动画设置相同的操作。
你可以通过告诉class使用哪个图像以及精灵应该在地图的哪个位置,来使class有效地产生多个敌人。这意味着你可以使用同一敌人class在游戏世界中的任何地方生成任意数量的敌人精灵。你所要做的就是调用该class,并告诉它要使用的图像以及所需的生成点的X和Y坐标。
同样,从原理上讲,这类似于生成Player精灵。在脚本的设置部分中,添加以下代码:
enemy = Enemy(20,200,'yeti.png')# spawn enemy
enemy_list = pygame.sprite.Group() # create enemy group
enemy_list.add(enemy) # add enemy to group
在该示例代码中,X位置为20,Y位置为200。您可能需要调整这些数字,具体取决于敌人精灵的大小,但请尝试使其在某个地方生成,以便您可以与Player精灵接触。其中Yeti.png是用于敌人的图像.
接下来,将敌人组中的所有敌人绘制到屏幕上。现在,你只有一个敌人,但是如果需要,你可以在后面添加更多敌人。只要将敌人添加到敌人组,它就会在主循环中绘制到屏幕上。中线是您需要添加的新行:
player_list.draw(world)
enemy_list.draw(world) # refresh enemies
pygame.display.flip()
启动你的游戏。无论选择哪种X和Y坐标,敌人都会出现在游戏世界中。
你的游戏尚处于起步阶段,但你可能需要添加另一个级别。进行编程时,必须提前规划好才行,以便你的游戏可以随着你对编程的更多了解而发展,这一点很重要。即使你还没有一个完整level规划,您也应该编写代码,就像您打算拥有多个level一样.
考虑一下什么是“level”吧。你怎么知道自己在游戏中处于某哪一个level嘞?
您可以将关卡视为项目的集合。 在一个平台游戏中(例如你在此处构建的平台游戏),关卡由平台的特定布置,敌人和战利品的放置等组成。 你可以建立一个围绕你的玩家建立一个关卡的class。最终,当你创建多个关卡时,您可以使用该class在玩家达到特定目标时生成下一个关卡。
将你编写的用于创建敌人及其组的代码移动到新函数中,该函数将与每个新level一起被调用。它需要进行一些修改,以便每次创建新关卡时都可以创建多个敌人:
class Level():
def bad(lvl,eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
enemy_list = pygame.sprite.Group() # create enemy group
enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return enemy_list
其中的 return 语句确保你在使用Level.bad函数时,留下一个包含你定义的每个敌人的敌人列表。
由于你现在正在将敌人创建为每个关卡的一部分,因此你的setup部分也需要更改。不是创建敌人就可以了,你必须定义敌人将在何处生成以及它属于哪个关卡。.
eloc = []
eloc = [200,20]
enemy_list = Level.bad( 1, eloc )
再次运行游戏以确认你的关卡正确生成。你应该像往常一样看到你的玩家,以及在本关中添加的敌人。
如果对玩家没有影响,那么敌人就算不上是敌人。当玩家与敌人碰撞时,通常会造成伤害。
你可能想跟踪玩家的血条情况,因此碰撞检查发生在Player类而不是Enemy类中。如果需要,你也可以跟踪敌人的血条。逻辑和代码几乎相同,但是到目前为止,我们只跟踪玩家的血条。
要跟踪玩家的血条,你必须首先为玩家的血条建立一个变量。此代码示例中的第一行用于上下文,因此将第二行添加到Player类:
self.frame = 0
self.health = 10
在Player class的 update 函数中, 添加此代码块:
hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in hit_list:
self.health -= 1
print(self.health)
这段代码通过Pygamr函数中的sprite.spritecollide创建了一个碰撞检测器, 名为enemy_hit。 每当其父子画面(创建该探测器的Player子画面)的点击框碰到enemy_list中任何子画面的点击框时,此碰撞检测器都会发出信号。当收到这样的信号时触发for循环,并从玩家的血条中扣除一点。
由于此代码出现在Player类的更新函数中,并且在主循环中调用了更新,因此Pygame每隔一个时钟滴答检查一次此冲突。
如果你想要的话,静止不动的敌人也会很有用,例如可能伤害玩家的尖刺或陷阱,但是如果敌人可以四处走动,游戏将更具挑战。
与玩家精灵不同,敌方精灵不受用户控制。所以它的动作必须是自动化的。
最终,你的游戏世界将会滚动,因此当游戏世界本身在移动时,如何让敌人在游戏世界中来回移动?
例如,你告诉敌人的精灵向右走10步,然后向左走10步。敌人的子画面无法计数,因此你必须创建一个变量来跟踪敌人移动了多少步伐,并根据计数变量的值对敌人进行编程以使其向右或向左移动.
首先,在您的Enemy类中创建计数器变量。在此代码示例中添加最后一行:
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.counter = 0 # counter variable
然后,在你的Enemy类中创建一个move函数。使用if-else去做一个所谓的infinite loop(无限循环):
·如果计数器的数字在0到100之间,则向右移动。
·如果计数器的数字从100到200,则向左移动。
·如果计数器大于200,则将计数器重置为0。
无限循环是没有止境的。它永远循环,因为循环中的任何事物都不是不真实的。在这种情况下,计数器始终位于0到100或100到200之间,因此敌人的精灵会永远从右向左走,从右向左走。敌人将沿任一方向移动多远的实际数字取决于你的屏幕大小,以及最终可能取决于敌人正在行走的平台的大小。从细小开始,逐步适应结果。首先尝试:
def move(self):
'''
enemy movement
'''
distance = 80
speed = 8
if self.counter >= 0 and self.counter <= distance:
self.rect.x += speed
elif self.counter >= distance and self.counter <= distance*2:
self.rect.x -= speed
else:
self.counter = 0
self.counter += 1
你可以根据需要调整距离和速度。
如果你现在启动游戏,此代码会起作用吗?
当然不会,你应该知道原因。你必须在主循环中调用move函数。此示例代码中的第一行是上下文,因此添加最后两行:
enemy_list.draw(world) #refresh enemy
for e in enemy_list:
e.move()
启动你的游戏,看看当你击中敌人时会发生什么。您可能需要调整Sprite的生成位置,以便你的玩家和敌人Sprite发生碰撞。当它们发生碰撞时,请查看IDLE或Ninja-IDE的控制台以查看要扣除的血条。
你可能会注意到,玩家和敌人接触的每一刻都会掉血。这是一个问题,但是在使用Python进行更多练习之后,你将在以后解决这个问题。
现在,尝试添加更多敌人。 记住将每个敌人添加到enemy_list中。 作为练习,你还可以尝试改变不同敌人精灵移动的距离。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。