新聞中心
四處奔跑躲避敵人是一回事,反擊敵人是另一回事。學習如何在這系列的第十二篇文章中在 Pygame 中創(chuàng)建平臺游戲。
這是仍在進行中的關于使用 Pygame 模塊在 Python 3 中創(chuàng)建電腦游戲的第十二部分。先前的文章是:
- 通過構(gòu)建一個簡單的擲骰子游戲去學習怎么用 Python 編程
- 使用 Python 和 Pygame 模塊構(gòu)建一個游戲框架
- 如何在你的 Python 游戲中添加一個玩家
- 用 Pygame 使你的游戲角色移動起來
- 如何向你的 Python 游戲中添加一個敵人
- 在 Pygame 游戲中放置平臺
- 在你的 Python 游戲中模擬引力
- 為你的 Python 平臺類游戲添加跳躍功能
- 使你的 Python 游戲玩家能夠向前和向后跑
- 在你的 Python 平臺類游戲中放一些獎勵
- 添加計分到你的 Python 游戲
我的上一篇文章本來是這一系列文章的最后一篇,它鼓勵你為這個游戲編寫自己的附加程序。你們很多人都這么做了!我收到了一些電子郵件,要求幫助我還沒有涵蓋的常用機制:戰(zhàn)斗。畢竟,跳起來躲避壞人是一回事,但是有時候讓他們走開是一件非常令人滿意的事。在電腦游戲中向你的敵人投擲一些物品是很常見的,不管是一個火球、一支箭、一道閃電,還是其它適合游戲的東西。
與迄今為止你在這個系列中為你的平臺游戲編程的任何東西不同,可投擲物品有一個生存時間。在你投擲一個物品后,它會如期在移動一段距離后消失。如果它是一支箭或其它類似的東西,它可能會在通過屏幕的邊緣時而消失。如果它是一個火球或一道閃電,它可能會在一段時間后熄滅。
這意味著每次生成一個可投擲的物品時,也必須生成一個獨特的衡量其生存時間的標準。為了介紹這個概念,這篇文章演示如何一次只投擲一個物品。(換句話說,每次僅存在一個投擲物品)。 一方面,這是一個游戲的限制條件,但另一方面,它卻是游戲本身的運行機制。你的玩家不能每次同時投擲 50 個火球,因為每次僅允許一個投擲物品,所以當你的玩家釋放一個火球來嘗試擊中一名敵人就成為了一項挑戰(zhàn)。而在幕后,這也使你的代碼保持簡單。
如果你想啟用每次投擲多個項目,在完成這篇教程后,通過學習這篇教程所獲取的知識來挑戰(zhàn)你自己。
創(chuàng)建 Throwable 類
如果你跟隨學習這系列的其它文章,那么你應該熟悉在屏幕上生成一個新的對象基礎的 __init__ 函數(shù)。這和你用來生成你的 玩家 和 敵人 的函數(shù)是一樣的。這里是生成一個 throwable 對象的 __init__ 函數(shù)來:
class Throwable(pygame.sprite.Sprite):"""生成一個 throwable 對象"""def __init__(self, x, y, img, throw):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 = xself.rect.y = yself.firing = throw
同你的 Player 類或 Enemy 類的 __init__ 函數(shù)相比,這個函數(shù)的主要區(qū)別是,它有一個 self.firing 變量。這個變量保持跟蹤一個投擲的物品是否在當前屏幕上活動,因此當一個 throwable 對象創(chuàng)建時,將變量設置為 1 的合乎情理的。
判斷存活時間
接下來,就像使用 Player 和 Enemy 一樣,你需要一個 update 函數(shù),以便投擲的物品在瞄準敵人拋向空中時,它會自己移動。
測定一個投擲的物品存活時間的最簡單方法是偵測它何時離開屏幕。你需要監(jiān)視的屏幕邊緣取決于你投擲的物品的物理特性。
- 如果你的玩家正在投擲的物品是沿著水平軸快速移動的,像一只弩箭或箭或一股非??斓哪Хα?,而你想監(jiān)視你游戲屏幕的水平軸極限。這可以通過
worldx定義。 - 如果你的玩家正在投擲的物品是沿著垂直方向或同時沿著水平方向和垂直方向移動的,那么你必須監(jiān)視你游戲屏幕的垂直軸極限。這可以通過
worldy定義。
這個示例假設你投擲的物品向前移動一點并最終落到地面上。不過,投擲的物品不會從地面上反彈起來,而是繼續(xù)掉落出屏幕。你可以嘗試不同的設置來看看什么最適合你的游戲:
def update(self,worldy):'''投擲物理學'''if self.rect.y < worldy: #垂直軸self.rect.x += 15 #它向前移動的速度有多快self.rect.y += 5 #它掉落的速度有多快else:self.kill() #移除投擲對象self.firing = 0 #解除火力發(fā)射
為使你的投擲物品移動地更快,增加 self.rect 的動量值。
如果投擲物品不在屏幕上,那么該物品將被銷毀,以及釋放其所占用的寄存器。另外,self.firing 將被設置回 0 以允許你的玩家來進行另一次射擊。
設置你的投擲對象
就像使用你的玩家和敵人一樣,你必須在你的設置部分中創(chuàng)建一個精靈組來保持投擲對象。
此外,你必須創(chuàng)建一個非活動的投擲對象來供開始的游戲使用。如果在游戲開始時卻沒有一個投擲對象,那么玩家在第一次嘗試投擲一柄武器時,投擲將失敗。
這個示例假設你的玩家使用一個火球作為開始的武器,因此,每一個投擲實例都是由 fire 變量指派的。在后面的關卡中,當玩家獲取新的技能時,你可以使用相同的 Throwable 類來引入一個新的變量以使用一張不同的圖像。
在這代碼塊中,前兩行已經(jīng)在你的代碼中,因此不要重新鍵入它們:
player_list = pygame.sprite.Group() #上下文player_list.add(player) #上下文fire = Throwable(player.rect.x,player.rect.y,'fire.png',0)firepower = pygame.sprite.Group()
注意,每一個投擲對象的起始位置都是和玩家所在的位置相同。這使得它看起來像是投擲對象來自玩家。在第一個火球生成時,使用 0 來顯示 self.firing 是可用的。
在主循環(huán)中獲取投擲行為
沒有在主循環(huán)中出現(xiàn)的代碼不會在游戲中使用,因此你需要在你的主循環(huán)中添加一些東西,以便能在你的游戲世界中獲取投擲對象。
首先,添加玩家控制。當前,你沒有火力觸發(fā)器。在鍵盤上的按鍵是有兩種狀態(tài)的:釋放的按鍵,按下的按鍵。為了移動,你要使用這兩種狀態(tài):按下按鍵來啟動玩家移動,釋放按鍵來停止玩家移動。開火僅需要一個信號。你使用哪個按鍵事件(按鍵按下或按鍵釋放)來觸發(fā)你的投擲對象取決于你的品味。
在這個代碼語句塊中,前兩行是用于上下文的:
if event.key == pygame.K_UP or event.key == ord('w'):player.jump(platform_list)if event.key == pygame.K_SPACE:if not fire.firing:fire = Throwable(player.rect.x,player.rect.y,'fire.png',1)firepower.add(fire)
與你在設置部分創(chuàng)建的火球不同,你使用一個 1 來設置 self.firing 為不可用。
最后,你必須更新和繪制你的投擲物品。這個順序很重要,因此把這段代碼放置到你現(xiàn)有的 enemy.move 和 player_list.draw 的代碼行之間:
enemy.move() # 上下文if fire.firing:fire.update(worldy)firepower.draw(world)player_list.draw(screen) # 上下文enemy_list.draw(screen) # 上下文
注意,這些更新僅在 self.firing 變量被設置為 1 時執(zhí)行。如果它被設置為 0 ,那么 fire.firing 就不為 true,接下來就跳過更新。如果你嘗試做上述這些更新,不管怎樣,你的游戲都會崩潰,因為在游戲中將不會更新或繪制一個 fire 對象。
啟動你的游戲,嘗試挑戰(zhàn)你的武器。
檢測碰撞
如果你玩使用了新投擲技巧的游戲,你可能會注意到,你可以投擲對象,但是它卻不會對你的敵人有任何影響。
原因是你的敵人沒有被查到碰撞事故。一名敵人可能會被你的投擲物品所擊中,但是敵人卻從來不知道被擊中了。
你已經(jīng)在你的 Player 類中完成了碰撞檢測,這非常類似。在你的 Enemy 類中,添加一個新的 update 函數(shù):
def update(self,firepower, enemy_list):"""檢測火力碰撞"""fire_hit_list = pygame.sprite.spritecollide(self,firepower,False)for fire in fire_hit_list:enemy_list.remove(self)
代碼很簡單。每個敵人對象都檢查并看看它自己是否被 firepower 精靈組的成員所擊中。如果它被擊中,那么敵人就會從敵人組中移除和消失。
為集成這些功能到你的游戲之中,在主循環(huán)中調(diào)用位于新觸發(fā)語句塊中的函數(shù):
if fire.firing: # 上下文fire.update(worldy) # 上下文firepower.draw(screen) # 上下文enemy_list.update(firepower,enemy_list) # 更新敵人
你現(xiàn)在可以嘗試一下你的游戲了,大多數(shù)的事情都如預期般的那樣工作。不過,這里仍然有一個問題,那就是投擲的方向。
更改投擲機制的方向
當前,你英雄的火球只會向右移動。這是因為 Throwable 類的 update 函數(shù)將像素添加到火球的位置,在 Pygame 中,在 X 軸上一個較大的數(shù)字意味著向屏幕的右側(cè)移動。當你的英雄轉(zhuǎn)向另一個方向時,你可能希望它投擲的火球也拋向左側(cè)。
到目前為止,你已經(jīng)知道如何實現(xiàn)這一點,至少在技術上是這樣的。然而,最簡單的解決方案卻是使用一個變量,在一定程度上對你來說可能是一種新的方法。一般來說,你可以“設置一個標記”(有時也被稱為“翻轉(zhuǎn)一個位”)來標明你的英雄所面向的方向。在你做完后,你就可以檢查這個變量來得知火球是向左移動還是向右移動。
首先,在你的 Player 類中創(chuàng)建一個新的變量來代表你的游戲所面向的方向。因為我的游戲天然地面向右側(cè),由此我把面向右側(cè)作為默認值:
self.score = 0self.facing_right = True # 添加這行self.is_jumping = True
當這個變量是 True 時,你的英雄精靈是面向右側(cè)的。當玩家每次更改英雄的方向時,變量也必須重新設置,因此,在你的主循環(huán)中相關的 keyup 事件中這樣做:
if event.type == pygame.KEYUP:if event.key == pygame.K_LEFT or event.key == ord('a'):player.control(steps, 0)player.facing_right = False # 添加這行if event.key == pygame.K_RIGHT or event.key == ord('d'):player.control(-steps, 0)player.facing_right = True # 添加這行
最后,更改你的 Throwable 類的 update 函數(shù),以檢測英雄是否面向右側(cè),并恰當?shù)靥砑踊驕p去來自火球位置的像素:
if self.rect.y < worldy:if player.facing_right:self.rect.x += 15else:self.rect.x -= 15self.rect.y += 5
再次嘗試你的游戲,清除掉你游戲世界中的一些壞人。
Python 平臺類使用投擲能力
作為一項額外的挑戰(zhàn),當徹底打敗敵人時,嘗試增加你玩家的得分。
完整的代碼
#!/usr/bin/env python3# 作者: Seth Kenlon# GPLv3# This program is free software: you can redistribute it and/or# modify it under the terms of the GNU General Public License as# published by the Free Software Foundation, either version 3 of the# License, or (at your option) any later version.## This program is distributed in the hope that it will be useful, but# WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU# General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program. If not, see <[http://www.gnu.org/licenses/>][17].import pygameimport pygame.freetypeimport sysimport os'''變量'''worldx = 960worldy = 720fps = 40ani = 4world = pygame.display.set_mode([worldx, worldy])forwardx = 600backwardx = 120BLUE = (80, 80, 155)BLACK = (23, 23, 23)WHITE = (254, 254, 254)ALPHA = (0, 255, 0)tx = 64ty = 64font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "fonts", "amazdoom.ttf")font_size = txpygame.freetype.init()myfont = pygame.freetype.Font(font_path, font_size)'''對象'''def stats(score, health):myfont.render_to(world, (4, 4), "Score:"+str(score), BLUE, None, size=64)myfont.render_to(world, (4, 72), "Health:"+str(health), BLUE, None, size=64)class Throwable(pygame.sprite.Sprite):"""生成一個投擲的對象"""def __init__(self, x, y, img, throw):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 = xself.rect.y = yself.firing = throwdef update(self, worldy):'''投擲物理學'''if self.rect.y < worldy:if player.facing_right:self.rect.x += 15else:self.rect.x -= 15self.rect.y += 5else:self.kill()self.firing = 0# x 位置, y 位置, img 寬度, img 高度, img 文件class Platform(pygame.sprite.Sprite):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.image.set_colorkey(ALPHA)self.rect = self.image.get_rect()self.rect.y = ylocself.rect.x = xlocclass Player(pygame.sprite.Sprite):"""生成一名玩家"""def __init__(self):pygame.sprite.Sprite.__init__(self)self.movex = 0self.movey = 0self.frame = 0self.health = 10self.damage = 0self.score = 0self.facing_right = Trueself.is_jumping = Trueself.is_falling = Trueself.images = []for i in range(1, 5):img = pygame.image.load(os.path.join('images', 'walk' + 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 gravity(self):if self.is_jumping:self.movey += 3.2def control(self, x, y):"""控制玩家移動"""self.movex += xdef jump(self):if self.is_jumping is False:self.is_falling = Falseself.is_jumping = Truedef update(self):"""更新精靈位置"""# 向左移動if self.movex < 0:self.is_jumping = Trueself.frame += 1if self.frame > 3 * ani:self.frame = 0self.image = pygame.transform.flip(self.images[self.frame // ani], True, False)# 向右移動if self.movex > 0:self.is_jumping = Trueself.frame += 1if self.frame > 3 * ani:self.frame = 0self.image = self.images[self.frame // ani]# 碰撞enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)if self.damage == 0:for enemy in enemy_hit_list:if not self.rect.contains(enemy):self.damage = self.rect.colliderect(enemy)if self.damage == 1:idx = self.rect.collidelist(enemy_hit_list)if idx == -1:self.damage = 0 # 設置傷害回 0self.health -= 1 # 減去 1 單位健康度ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)for g in ground_hit_list:self.movey = 0self.rect.bottom = g.rect.topself.is_jumping = False # 停止跳躍# 掉落世界if self.rect.y > worldy:self.health -=1print(self.health)self.rect.x = txself.rect.y = typlat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)for p in plat_hit_list:self.is_jumping = False # 停止跳躍self.movey = 0if self.rect.bottom <= p.rect.bottom:self.rect.bottom = p.rect.topelse:self.movey += 3.2if self.is_jumping and self.is_falling is False:self.is_falling = Trueself.movey -= 33 # 跳躍多高loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False)for loot in loot_hit_list:loot_list.remove(loot)self.score += 1print(self.score)plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)self.rect.x += self.movexself.rect.y += self.moveyclass Enemy(pygame.sprite.Sprite):"""生成一名敵人"""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 = xself.rect.y = yself.counter = 0def move(self):"""敵人移動"""distance = 80speed = 8if self.counter >= 0 and self.counter <= distance:self.rect.x += speedelif self.counter >= distance and self.counter <= distance * 2:self.rect.x -= speedelse:self.counter = 0self.counter += 1def update(self, firepower, enemy_list):"""檢測火力碰撞"""fire_hit_list = pygame.sprite.spritecollide(self, firepower, False)for fire in fire_hit_list:enemy_list.remove(self)class Level:def ground(lvl, gloc, tx, ty):ground_list = pygame.sprite.Group()i = 0if lvl == 1:while i < len(gloc):ground = Platform(gloc[i], worldy - ty, tx, ty, 'tile-ground.png')ground_list.add(ground)i = i + 1if lvl == 2:print("Level " + str(lvl))return ground_listdef bad(lvl, eloc):if lvl == 1:enemy = Enemy(eloc[0], eloc[1], 'enemy.png')enemy_list = pygame.sprite.Group()enemy_list.add(enemy)if lvl == 2:print("Level " + str(lvl))return enemy_list# x 位置, y 位置, img 寬度, img 高度, img 文件def platform(lvl, tx, ty):plat_list = pygame.sprite.Group()ploc = []i = 0if lvl == 1:ploc.append((200, worldy - ty - 128, 3))ploc.append((300, worldy - ty - 256, 3))ploc.append((550, worldy - ty - 128, 4))while i < len(ploc):j = 0while j <= ploc[i][2]:plat = Platform((ploc[i][0] + (j * tx)), ploc[i][1], tx, ty, 'tile.png')plat_list.add(plat)j = j + 1print('run' + str(i) + str(ploc[i]))i = i + 1if lvl == 2:print("Level " + str(lvl))return plat_listdef loot(lvl):if 當前名稱:在你的Python游戲中添加投擲機制
網(wǎng)站網(wǎng)址:http://www.dlmjj.cn/article/coogosp.html


咨詢
建站咨詢

