溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python 實(shí)現(xiàn)平臺類游戲添加跳躍功能

發(fā)布時(shí)間:2020-10-02 13:02:30 來源:腳本之家 閱讀:161 作者:Seth Kenlon 欄目:開發(fā)技術(shù)

 在本期使用 Python Pygame 模塊編寫視頻游戲中,學(xué)會如何使用跳躍來對抗重力。

在本系列的前一篇文章 中,你已經(jīng)模擬了重力。但現(xiàn)在,你需要賦予你的角色跳躍的能力來對抗重力。

跳躍是對重力作用的暫時(shí)延緩。在這一小段時(shí)間里,你是向上跳,而不是被重力拉著向下落。但你一旦到達(dá)了跳躍的最高點(diǎn),重力就會重新發(fā)揮作用,將你拉回地面。

在代碼中,這種變化被表示為變量。首先,你需要為玩家精靈建立一個(gè)變量,使得 Python 能夠跟蹤該精靈是否正在跳躍中。一旦玩家精靈開始跳躍,他就會再次受到重力的作用,并被拉回最近的物體。

設(shè)置跳躍狀態(tài)變量

你需要為你的 Player 類添加兩個(gè)新變量:

  • 一個(gè)是為了跟蹤你的角色是否正在跳躍中,可通過你的玩家精靈是否站在堅(jiān)實(shí)的地面來確定
  • 一個(gè)是為了將玩家?guī)Щ氐孛?br />

將如下兩個(gè)變量添加到你的 Player 類中。在下方的代碼中,注釋前的部分用于提示上下文,因此只需要添加最后兩行:           

 self.movex = 0
  self.movey = 0
  self.frame = 0
  self.health = 10
  # 此處是重力相關(guān)變量
  self.collide_delta = 0
  self.jump_delta = 6

第一個(gè)變量 collide_delta 被設(shè)為 0 是因?yàn)樵谡顟B(tài)下,玩家精靈沒有處在跳躍中的狀態(tài)。另一個(gè)變量 jump_delta 被設(shè)為 6,是為了防止精靈在第一次進(jìn)入游戲世界時(shí)就發(fā)生反彈(實(shí)際上就是跳躍)。當(dāng)你完成了本篇文章的示例,嘗試把該變量設(shè)為 0 看看會發(fā)生什么。

跳躍中的碰撞

如果你是跳到一個(gè)蹦床上,那你的跳躍一定非常優(yōu)美。但是如果你是跳向一面墻會發(fā)生什么呢?(千萬不要去嘗試!)不管你的起跳多么令人印象深刻,當(dāng)你撞到比你更大更硬的物體時(shí),你都會立馬停下。(LCTT 譯注:原理參考動量守恒定律)

為了在你的視頻游戲中模擬這一點(diǎn),你需要在你的玩家精靈與地面等東西發(fā)生碰撞時(shí),將 self.collide_delta 變量設(shè)為 0。如果你的 self.collide_delta 不是 0 而是其它的什么值,那么你的玩家就會發(fā)生跳躍,并且當(dāng)你的玩家與墻或者地面發(fā)生碰撞時(shí)無法跳躍。

在你的 Player 類的 update 方法中,將地面碰撞相關(guān)代碼塊修改為如下所示:

 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 # 停止跳躍
  if self.rect.y > g.rect.y:
  self.health -=1
  print(self.health)

這段代碼塊檢查了地面精靈和玩家精靈之間發(fā)生的碰撞。當(dāng)發(fā)生碰撞時(shí),它會將玩家 Y 方向的坐標(biāo)值設(shè)置為游戲窗口的高度減去一個(gè)瓷磚的高度再減去另一個(gè)瓷磚的高度。以此保證了玩家精靈是站在地面上,而不是嵌在地面里。同時(shí)它也將 self.collide_delta 設(shè)為 0,使得程序能夠知道玩家未處在跳躍中。除此之外,它將 self.movey 設(shè)為 0,使得程序能夠知道玩家當(dāng)前未受到重力的牽引作用(這是游戲物理引擎的奇怪之處,一旦玩家落地,也就沒有必要繼續(xù)將玩家拉向地面)。

此處 if 語句用來檢測玩家是否已經(jīng)落到地面之下,如果是,那就扣除一點(diǎn)生命值作為懲罰。此處假定了你希望當(dāng)你的玩家落到地圖之外時(shí)失去生命值。這個(gè)設(shè)定不是必需的,它只是平臺類游戲的一種慣例。更有可能的是,你希望這個(gè)事件能夠觸發(fā)另一些事件,或者說是一種能夠讓你的現(xiàn)實(shí)世界玩家沉迷于讓精靈掉到屏幕之外的東西。一種簡單的恢復(fù)方式是在玩家精靈掉落到地圖之外時(shí),將 self.rect.y 重新設(shè)置為 0,這樣它就會在地圖上方重新生成,并落到堅(jiān)實(shí)的地面上。

撞向地面

模擬的重力使你玩家的 Y 坐標(biāo)不斷增大(LCTT 譯注:此處原文中為 0,但在 Pygame 中越靠下方 Y 坐標(biāo)應(yīng)越大)。要實(shí)現(xiàn)跳躍,完成如下代碼使你的玩家精靈離開地面,飛向空中。

在你的 Player 類的 update 方法中,添加如下代碼來暫時(shí)延緩重力的作用:

 if self.collide_delta < 6 and self.jump_delta < 6:
  self.jump_delta = 6*2
  self.movey -= 33 # 跳躍的高度
  self.collide_delta += 6
  self.jump_delta += 6

根據(jù)此代碼所示,跳躍使玩家精靈向空中移動了 33 個(gè)像素。此處是負(fù) 33 是因?yàn)樵?Pygame 中,越小的數(shù)代表距離屏幕頂端越近。

不過此事件視條件而定,只有當(dāng) self.collide_delta 小于 6(缺省值定義在你 Player 類的 init 方法中)并且 self.jump_delta 也于 6 的時(shí)候才會發(fā)生。此條件能夠保證直到玩家碰到一個(gè)平臺,才能觸發(fā)另一次跳躍。換言之,它能夠阻止空中二段跳。

在某些特殊條件下,你可能不想阻止空中二段跳,或者說你允許玩家進(jìn)行空中二段跳。舉個(gè)栗子,如果玩家獲得了某個(gè)戰(zhàn)利品,那么在他被敵人攻擊到之前,都能夠擁有空中二段跳的能力。

當(dāng)你完成本篇文章中的示例,嘗試將 self.collide_deltaself.jump_delta 設(shè)置為 0,從而獲得百分之百的幾率觸發(fā)空中二段跳。

在平臺上著陸

目前你已經(jīng)定義了在玩家精靈摔落地面時(shí)的抵抗重力條件,但此時(shí)你的游戲代碼仍保持平臺與地面置于不同的列表中(就像本文中做的很多其他選擇一樣,這個(gè)設(shè)定并不是必需的,你可以嘗試將地面作為另一種平臺)。為了允許玩家精靈站在平臺之上,你必須像檢測地面碰撞一樣,檢測玩家精靈與平臺精靈之間的碰撞。將如下代碼放于你的 update 方法中:

 plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
 for p in plat_hit_list:
  self.collide_delta = 0 # 跳躍結(jié)束
  self.movey = 0

但此處還有一點(diǎn)需要考慮:平臺懸在空中,也就意味著玩家可以通過從上面或者從下面接觸平臺來與之互動。

確定平臺如何與玩家互動取決于你,阻止玩家從下方到達(dá)平臺也并不稀奇。將如下代碼加到上方的代碼塊中,使得平臺表現(xiàn)得像天花板或者說是藤架。只有在玩家精靈跳得比平臺上沿更高時(shí)才能跳到平臺上,但會阻止玩家從平臺下方跳上來:        

 if self.rect.y > p.rect.y:
  self.rect.y = p.rect.y+ty
  else:
  self.rect.y = p.rect.y-ty

此處 if 語句代碼塊的第一個(gè)子句阻止玩家精靈從平臺正下方跳到平臺上。如果它檢測到玩家精靈的坐標(biāo)比平臺更大(在 Pygame 中,坐標(biāo)更大意味著在屏幕的更下方),那么將玩家精靈新的 Y 坐標(biāo)設(shè)置為當(dāng)前平臺的 Y 坐標(biāo)加上一個(gè)瓷磚的高度。實(shí)際效果就是保證玩家精靈距離平臺一個(gè)瓷磚的高度,防止其從下方穿過平臺。

else 子句做了相反的事情。當(dāng)程序運(yùn)行到此處時(shí),如果玩家精靈的 Y 坐標(biāo)不比平臺的更大,意味著玩家精靈是從空中落下(不論是由于玩家剛剛從此處生成,或者是玩家執(zhí)行了跳躍)。在這種情況下,玩家精靈的 Y 坐標(biāo)被設(shè)為平臺的 Y 坐標(biāo)減去一個(gè)瓷磚的高度(切記,在 Pygame 中更小的 Y 坐標(biāo)代表在屏幕上的更高處)。這樣就能保證玩家在平臺上,除非他從平臺上跳下來或者走下來。

你也可以嘗試其他的方式來處理玩家與平臺之間的互動。舉個(gè)栗子,也許玩家精靈被設(shè)定為處在平臺的“前面”,他能夠無障礙地跳躍穿過平臺并站在上面?;蛘吣憧梢栽O(shè)計(jì)一種平臺會減緩而又不完全阻止玩家的跳躍過程。甚至你可以通過將不同平臺分到不同列表中來混合搭配使用。

觸發(fā)一次跳躍

目前為此,你的代碼已經(jīng)模擬了所有必需的跳躍條件,但仍缺少一個(gè)跳躍觸發(fā)器。你的玩家精靈的 self.jump_delta 初始值被設(shè)置為 6,只有當(dāng)它比 6 小的時(shí)候才會觸發(fā)更新跳躍的代碼。

為跳躍變量設(shè)置一個(gè)新的設(shè)置方法,在你的 Player 類中創(chuàng)建一個(gè) jump 方法,并將 self.jump_delta 設(shè)為小于 6 的值。通過使玩家精靈向空中移動 33 個(gè)像素,來暫時(shí)減緩重力的作用。

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

不管你相信與否,這就是 jump 方法的全部。剩余的部分在 update 方法中,你已經(jīng)在前面實(shí)現(xiàn)了相關(guān)代碼。

要使你游戲中的跳躍功能生效,還有最后一件事情要做。如果你想不起來是什么,運(yùn)行游戲并觀察跳躍是如何生效的。

問題就在于你的主循環(huán)中沒有調(diào)用 jump 方法。先前你已經(jīng)為該方法創(chuàng)建了一個(gè)按鍵占位符,現(xiàn)在,跳躍鍵所做的就是將 jump 打印到終端。

調(diào)用 jump 方法

在你的主循環(huán)中,將上方向鍵的效果從打印一條調(diào)試語句,改為調(diào)用 jump 方法。

注意此處,與 update 方法類似,jump 方法也需要檢測碰撞,因此你需要告訴它使用哪個(gè) plat_list。           

if event.key == pygame.K_UP or event.key == ord('w'):
  player.jump(plat_list)

如果你傾向于使用空格鍵作為跳躍鍵,使用 pygame.K_SPACE 替代 pygame.K_UP 作為按鍵。另一種選擇,你可以同時(shí)使用兩種方式(使用單獨(dú)的 if 語句),給玩家多一種選擇。

現(xiàn)在來嘗試你的游戲吧!在下一篇文章中,你將讓你的游戲卷動起來。

Python 實(shí)現(xiàn)平臺類游戲添加跳躍功能

以下是目前為止的所有代碼:

#!/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 坐標(biāo),y 坐標(biāo),圖像寬度,圖像高度,圖像文件
 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):
 '''
 生成一個(gè)玩家
 '''
 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):
 '''
 控制玩家移動
 '''
 self.movex += x
 self.movey += y
 
 def update(self):
 '''
 更新精靈位置
 '''
 
 self.rect.x = self.rect.x + self.movex
 self.rect.y = self.rect.y + self.movey
 # 向左移動
 if self.movex < 0:
  self.frame += 1
  if self.frame > ani*3:
  self.frame = 0
  self.image = self.images[self.frame//ani]
 # 向右移動
 if self.movex > 0:
  self.frame += 1
  if self.frame > ani*3:
  self.frame = 0
  self.image = self.images[(self.frame//ani)+4]
 # 碰撞
 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):
 '''
 生成一個(gè)敵人
 '''
 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):
 '''
 敵人移動
 '''
 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') # 生成敵人
  enemy_list = pygame.sprite.Group() # 創(chuàng)建敵人組
  enemy_list.add(enemy)  # 將敵人添加到敵人組
  
 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 # 幀率
ani = 4 # 動畫循環(huán)
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() # 生成玩家
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 # 瓷磚尺寸
ty = 64 # 瓷磚尺寸
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 )
'''
主循環(huán)
'''
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() # 檢查重力
 player.update()
 player_list.draw(world) # 刷新玩家位置
 enemy_list.draw(world) # 刷新敵人
 ground_list.draw(world) # 刷新地面
 plat_list.draw(world) # 刷新平臺
 for e in enemy_list:
 e.move()
 pygame.display.flip()
 clock.tick(fps)

總結(jié)

到此這篇關(guān)于Python 實(shí)現(xiàn)平臺類游戲添加跳躍功能的文章就介紹到這了,更多相關(guān)python 平臺類游戲 跳躍內(nèi)容請搜索億速云以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持億速云!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI