前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为你的Python游戏添加跳跃系统

为你的Python游戏添加跳跃系统

作者头像
五月Rambo
修改2019-12-09 15:09:49
1.5K0
修改2019-12-09 15:09:49
举报

在本系列的上一篇文章中,你模拟了重力系统, 现在你需要给玩家一种通过跳跃来对抗重力的方法。

跳跃可以被理解为是对重力的暂时缓解。很短暂地,你跳的时候不是掉下来,而是重力在吸引你。但是,一旦你达到了跳跃的顶峰,重力就会重新开始发挥效用并将你拉回地面。

在代码中,这反应为变量。首先,必须为玩家精灵建立变量,以便Python可以跟踪精灵是否在跳跃。玩家精灵跳跃后,将重力再次施加到玩家精灵,将其向下拉至最近的对象t。

设置跳跃状态变量

您必须在Player类中添加两个新变量:

·一个用来跟踪玩家是否在跳跃的状态,取决于玩家精灵是否站立在坚实的地面上。

·一个让玩家重返地面。

将这些变量添加到您的Player类中。在下面的代码中,注释上方的行是针对上下文的,因此只需添加最后两行:

代码语言:javascript
复制
 self.movex = 0
 self.movey = 0
 self.frame = 0
 self.health = 10
 # gravity variables here
 self.collide_delta = 0
 self.jump_delta = 6

第一个变量(collide_delta)设置为0,因为在其自然状态下,玩家精灵不在跳跃中。另一个变量(jump_delta)设置为6,以防止精灵在首次进入游戏世界时弹起(实际上是跳跃)。完成本文的示例后,请尝试将其设置为0以查看会发生什么。

碰撞mid-jump

如果您在蹦床上跳跃,那么您的跳跃会令人非常得劲。但是,如果您跳入墙壁会发生什么?(请勿尝试!)无论您开始跳动地多么嗨皮,当您与比自己大得多且坚固得多的物体碰撞时,跳动都会很快结束。

要在您的游戏中模仿这一点,您必须在玩家精灵与地面等物体碰撞时将self.collide_delta变量设置为0。如果self.collide_delta的值不是0,则说明您的玩家正在跳跃,并且当玩家撞到墙壁或地面时无法再次跳跃。

update函数的Player类中, 像这样修改地面碰撞区块:

代码语言:javascript
复制
        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
 for g in ground_hit_list:
 self.movey = 0
 self.rect.y = worldy-ty-ty
 self.collide_delta = 0 # stop jumping
 if self.rect.y > g.rect.y:
 self.health -=1
 print(self.health)

这段代码块检查地面精灵与玩家精灵之间是否发生冲突。在发生碰撞的情况下,它将玩家的Y位置设置为等于游戏窗口(worldy)高度减去一个图块的高度减去另一个图块的高度的值(这样,玩家精灵会显示为 站在地上而不是中间)。它还将self.collide_delta设置为0,以使程序知道玩家不在跳跃中。此外,它会将self.movey设置为0,以便程序知道玩家没有被重力吸引(这是游戏物理学的特有现象,您不需要在玩家已经被拉过后继续将玩家拉向地球)。

if语句检测玩家是否下降到地面以下; 如果是这样,它将扣除血量作为罚款。 假设您希望您的玩家因摔倒而血条消失,这不是绝对的;这只是游戏中的常见情况。不过,您可能希望此事件触发某些事件,否则您的现实世界玩家将被困在没有玩家角色的游戏中。一个简单的恢复方法是将self.rect.y再次设置为0,这样当玩家精灵掉落到世界上时,它会重生在世界的最顶端,然后退回到坚实的地面上。

击中地面

您的模拟重力希望玩家的Y轴运动为0或更大。要创建跳跃,请编写代码,将您的玩家精灵从坚实的地面发射到空中。

在您的Player类的更新功能中,从重力添加一个临时模块:

代码语言:javascript
复制
 if self.collide_delta < 6 and self.jump_delta < 6:
 self.jump_delta = 6*2
 self.movey -= 33 # how high to jump
 self.collide_delta += 6
 self.jump_delta    += 6

根据此代码,跳跃将玩家精灵向空中发送了33个像素。负33是因为Pygame中的数字越小意味着它离屏幕顶部越近。

但是,此事件是有条件的。仅当self.collide_delta小于6(其默认值在您的Sprite Sprite的init函数中建立)且self.jump_delta小于6时才会发生。此情况可防止玩家触发另一次跳跃,直到与平台碰撞 。 换句话说,它可以防止空中跳跃(海贼王里的月步)。

您不必阻止空中跳跃,也可以在特殊条件下允许空中跳跃。例如,如果玩家获得了特殊的战利品,那么您可以授予其进行空中跳跃的能力,直到下次敌人击中它为止。

完成本文的示例后,请尝试将self.collide_delta和self.jump_delta设置为0,以获得100%的机会跳入空中。

登录某个平台

到目前为止,您已经为玩家精灵击中地面定义了反重力条件,但是游戏代码将平台和地面保留在单独的列表中。(与本文中的许多选择一样,这不是绝对必要的,您可以尝试将地面视为另一个平台。)要使玩家精灵能够站在平台之上,您必须检测到 玩家精灵和平台精灵,然后执行与地面碰撞相同的操作。将此代码放入您的新函数中:

代码语言:javascript
复制
        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
 self.collide_delta = 0 # stop jumping
 self.movey = 0

不过,还有另外一个问题:平台悬空悬挂,这意味着玩家可以通过从顶部或底部接近平台来与平台互动。

由您决定平台如何对玩家做出反应。阻止玩家从下方访问平台并不少见。将此代码添加到上面的代码块中,将平台视为一种天花板或架子,以便玩家精灵可以跳到平台上,只要它跳得比平台的顶部更高,但在试图从平台上跳下来时会阻塞玩家:

代码语言:javascript
复制
 if self.rect.y > p.rect.y:
 self.rect.y = p.rect.y+ty
 else:
 self.rect.y = p.rect.y-ty

如果玩家精灵位于平台正下方,则此if语句的第一条款会阻止玩家访问平台。通过检测玩家精灵的位置较大(在Pygame中,这意味着屏幕上从上到下较低),然后将玩家精灵的新Y位置设置为其当前的Y位置加上图块的高度,来完成此操作,从而有效地使玩家远离其下方穿过平台的高度。

else语句则相反。如果程序正在运行此代码,则玩家精灵的Y位置不比平台大,这意味着玩家精灵正在从天上掉落(要么是因为它是从那里新鲜产生的,要么是因为玩家已经跳了起来)。在这种情况下,将玩家精灵的位置设置为平台位置减去一个图块的高度(因为请记住,在Pygame中,数字越小表示屏幕上的内容越高)。除非玩家跳离平台,否则它将使玩家始终处于平台顶部.

您可以尝试其他处理Sprite和平台交互的方法。例如,假设玩家被假定在平台的“前面”,并且可以无缝地跳过平台以站在平台之上。 或者平台可以减慢玩家的飞跃,但不能完全阻止它。您甚至可以通过将平台分组到不同列表中来进行混合和匹配。

触发跳跃

您的代码现在可以模拟所有必要的跳跃条件,但是仍然缺少跳跃触发器。玩家精灵的self.jump_delta最初设置为6,并且跳转更新代码仅在小于6时触发。

要触发跳跃变量的新设置,请在您的Player类中创建一个跳转功能,将self.jump_delta设置为小于6,从而通过将玩家精灵向空中发送33个像素来暂时取消重力:

代码语言:javascript
复制
 def jump(self,platform_list):
 self.jump_delta = 0

不管您相信与否,这就是跳转功能所需要的。其余的发生在更新函数中,并且您已经编写了该代码.

在游戏中开始跳跃之前,还有最后一件事要做。如果您不知道它是什么,请尝试玩游戏,看看跳跃如何为您工作。

问题是您的主循环中没有任何东西正在调用``跳跃''函数。您很早就为此做了一个占位键,但是现在,所有的跳跃键都只是在终端上打印跳跃。

调用跳跃函数

在您的主循环中,将向上箭头的结果从打印调试语句更改为调用跳跃功能。

请注意,jump函数(如更新函数)需要了解冲突,因此您必须告诉它要使用哪个plat_list.

代码语言:javascript
复制
 if event.key == pygame.K_UP or event.key == ord('w'):
                player.jump(plat_list)

如果您想使用空格键进行跳跃,请将键设置为pygame.K_SPACE而不是pygame.K_UP。或者,您可以同时使用两者(作为单独的if陈述),以便玩家可以选择。

现在开始你的探索了!在下一篇文章中,我将带你开发世界滚动功能。

这是目前为止的code:

代码语言:javascript
复制
#!/usr/bin/env python3
# draw a world
# add a player and player control
# add player movement
# add enemy and basic collision
# add platform
# add gravity
# add jumping

# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

import pygame
import sys
import os

'''
Objects
'''

class Platform(pygame.sprite.Sprite):
 # x location, y location, img width, img height, img file    
 def __init__(self,xloc,yloc,imgw,imgh,img):
        pygame.sprite.Sprite.__init__(self)
 self.image = pygame.image.load(os.path.join('images',img)).convert()
 self.image.convert_alpha()
 self.rect = self.image.get_rect()
 self.rect.y = yloc
 self.rect.x = xloc

class Player(pygame.sprite.Sprite):
 '''
    Spawn a player
    '''
 def __init__(self):
        pygame.sprite.Sprite.__init__(self)
 self.movex = 0
 self.movey = 0
 self.frame = 0
 self.health = 10
 self.collide_delta = 0
 self.jump_delta = 6
 self.score = 1
 self.images = []
 for i in range(1,9):
            img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
            img.convert_alpha()
            img.set_colorkey(ALPHA)
 self.images.append(img)
 self.image = self.images[0]
 self.rect = self.image.get_rect()

 def jump(self,platform_list):
 self.jump_delta = 0

 def gravity(self):
 self.movey += 3.2 # how fast player falls
 
 if self.rect.y > worldy and self.movey >= 0:
 self.movey = 0
 self.rect.y = worldy-ty
 
 def control(self,x,y):
 '''
        control player movement
        '''
 self.movex += x
 self.movey += y
 
 def update(self):
 '''
        Update sprite position
        '''
 
 self.rect.x = self.rect.x + self.movex
 self.rect.y = self.rect.y + self.movey

 # moving left
 if self.movex < 0:
 self.frame += 1
 if self.frame > ani*3:
 self.frame = 0
 self.image = self.images[self.frame//ani]

 # moving right
 if self.movex > 0:
 self.frame += 1
 if self.frame > ani*3:
 self.frame = 0
 self.image = self.images[(self.frame//ani)+4]

 # collisions
        enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
 for enemy in enemy_hit_list:
 self.health -= 1
 #print(self.health)

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
 self.collide_delta = 0 # stop jumping
 self.movey = 0
 if self.rect.y > p.rect.y:
 self.rect.y = p.rect.y+ty
 else:
 self.rect.y = p.rect.y-ty
 
        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
 for g in ground_hit_list:
 self.movey = 0
 self.rect.y = worldy-ty-ty
 self.collide_delta = 0 # stop jumping
 if self.rect.y > g.rect.y:
 self.health -=1
 print(self.health)
 
 if self.collide_delta < 6 and self.jump_delta < 6:
 self.jump_delta = 6*2
 self.movey -= 33 # how high to jump
 self.collide_delta += 6
 self.jump_delta    += 6
 
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.movey = 0
 #self.image.convert_alpha()
 #self.image.set_colorkey(ALPHA)
 self.rect = self.image.get_rect()
 self.rect.x = x
 self.rect.y = y
 self.counter = 0

 
 def move(self):
 '''
        enemy movement
        '''
        distance = 80
        speed = 8

 self.movey += 3.2
 
 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

 if not self.rect.y >= worldy-ty-ty:
 self.rect.y += self.movey

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
 self.movey = 0
 if self.rect.y > p.rect.y:
 self.rect.y = p.rect.y+ty
 else:
 self.rect.y = p.rect.y-ty

        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
 for g in ground_hit_list:
 self.rect.y = worldy-ty-ty

 
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

 def loot(lvl,lloc):
 print(lvl)

 def ground(lvl,gloc,tx,ty):
        ground_list = pygame.sprite.Group()
        i=0
 if lvl == 1:
 while i < len(gloc):
                ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
                ground_list.add(ground)
                i=i+1

 if lvl == 2:
 print("Level " + str(lvl) )

 return ground_list

 def platform(lvl,tx,ty):
        plat_list = pygame.sprite.Group()
        ploc = []
        i=0
 if lvl == 1:
            ploc.append((0,worldy-ty-128,3))
            ploc.append((300,worldy-ty-256,3))
            ploc.append((500,worldy-ty-128,4))

 while i < len(ploc):
                j=0
 while j <= ploc[i][2]:
                    plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png')
                    plat_list.add(plat)
                    j=j+1
 print('run' + str(i) + str(ploc[i]))
                i=i+1

 if lvl == 2:
 print("Level " + str(lvl) )

 return plat_list

'''
Setup
'''
worldx = 960
worldy = 720

fps = 40 # frame rate
ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True

BLUE  = (25,25,200)
BLACK = (23,23,23 )
WHITE = (254,254,254)
ALPHA = (0,255,0)

world = pygame.display.set_mode([worldx,worldy])
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
backdropbox = world.get_rect()
player = Player() # spawn player
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
jump = -24

eloc = []
eloc = [200,20]
gloc = []
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
tx = 64 #tile size
ty = 64 #tile size

i=0
while i <= (worldx/tx)+tx:
    gloc.append(i*tx)
    i=i+1

enemy_list = Level.bad( 1, eloc )
ground_list = Level.ground( 1,gloc,tx,ty )
plat_list = Level.platform( 1,tx,ty )

'''
Main loop
'''
while main == True:
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
            pygame.quit(); sys.exit()
            main = False

 if event.type == pygame.KEYDOWN:
 if event.key == pygame.K_LEFT or event.key == ord('a'):
 print("LEFT")
                player.control(-steps,0)
 if event.key == pygame.K_RIGHT or event.key == ord('d'):
 print("RIGHT")
                player.control(steps,0)
 if event.key == pygame.K_UP or event.key == ord('w'):
 print('jump')

 if event.type == pygame.KEYUP:
 if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps,0)
 if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps,0)
 if event.key == pygame.K_UP or event.key == ord('w'):
                player.jump(plat_list)

 if event.key == ord('q'):
                pygame.quit()
 sys.exit()
                main = False

#    world.fill(BLACK)
    world.blit(backdrop, backdropbox)
    player.gravity() # check gravity
    player.update()
    player_list.draw(world) #refresh player position
    enemy_list.draw(world) # refresh enemies
    ground_list.draw(world) # refresh enemies
    plat_list.draw(world) # refresh platforms
 for e in enemy_list:
        e.move()
    pygame.display.flip()
    clock.tick(fps)

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 设置跳跃状态变量
  • 碰撞mid-jump
  • 击中地面
  • 登录某个平台
  • 触发跳跃
  • 调用跳跃函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档