新聞中心
這是仍在進(jìn)行中的關(guān)于使用 Pygame 模塊來在 Python 3 在創(chuàng)建電腦游戲的第十一部分。先前的文章是:

成都創(chuàng)新互聯(lián)公司專注于企業(yè)成都全網(wǎng)營銷、網(wǎng)站重做改版、溧陽網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5技術(shù)、成都商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營銷網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為溧陽等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
- 通過構(gòu)建一個(gè)簡單的擲骰子游戲去學(xué)習(xí)怎么用 Python 編程
- 使用 Python 和 Pygame 模塊構(gòu)建一個(gè)游戲框架
- 如何在你的 Python 游戲中添加一個(gè)玩家
- 用 Pygame 使你的游戲角色移動(dòng)起來
- 如何向你的 Python 游戲中添加一個(gè)敵人
- 在 Pygame 游戲中放置平臺(tái)
- 在你的 Python 游戲中模擬引力
- 為你的 Python 平臺(tái)類游戲添加跳躍功能
- 使你的 Python 游戲玩家能夠向前和向后跑
- 在你的 Python 平臺(tái)類游戲中放一些獎(jiǎng)勵(lì)
如果你已經(jīng)跟隨這一系列很久,那么已經(jīng)學(xué)習(xí)了使用 Python 創(chuàng)建一個(gè)視頻游戲所需的所有基本語法和模式。然而,它仍然缺少一個(gè)至關(guān)重要的組成部分。這一組成部分不僅僅對(duì)用 Python 編程游戲重要;不管你探究哪個(gè)計(jì)算機(jī)分支,你都必需精通:作為一個(gè)程序員,通過閱讀一種語言的或庫的文檔來學(xué)習(xí)新的技巧。
幸運(yùn)的是,你正在閱讀本文的事實(shí)表明你熟悉文檔。為了使你的平臺(tái)類游戲更加美觀,在這篇文章中,你將在游戲屏幕上添加得分和生命值顯示。不過,教你如何找到一個(gè)庫的功能以及如何使用這些新的功能的這節(jié)課程并沒有多神秘。
在 Pygame 中顯示得分
現(xiàn)在,既然你有了可以被玩家收集的獎(jiǎng)勵(lì),那就有充分的理由來記錄分?jǐn)?shù),以便你的玩家看到他們收集了多少獎(jiǎng)勵(lì)。你也可以跟蹤玩家的生命值,以便當(dāng)他們被敵人擊中時(shí)會(huì)有相應(yīng)結(jié)果。
你已經(jīng)有了跟蹤分?jǐn)?shù)和生命值的變量,但是這一切都發(fā)生在后臺(tái)。這篇文章教你在游戲期間在游戲屏幕上以你選擇的一種字體來顯示這些統(tǒng)計(jì)數(shù)字。
閱讀文檔
大多數(shù) Python 模塊都有文檔,即使那些沒有文檔的模塊,也能通過 Python 的幫助功能來進(jìn)行最小的文檔化。Pygame 的主頁面 鏈接了它的文檔。不過,Pygame 是一個(gè)帶有很多文檔的大模塊,并且它的文檔不像在 Opensource.com 上的文章一樣,以同樣易理解的(和友好的、易解釋的、有用的)敘述風(fēng)格來撰寫的。它們是技術(shù)文檔,并且列出在模塊中可用的每個(gè)類和函數(shù),各自要求的輸入類型等等。如果你不適應(yīng)參考代碼組件描述,這可能會(huì)令人不知所措。
在煩惱于庫的文檔前,第一件要做的事,就是來想想你正在嘗試達(dá)到的目標(biāo)。在這種情況下,你想在屏幕上顯示玩家的得分和生命值。
在你確定你需要的結(jié)果后,想想它需要什么的組件。你可以從變量和函數(shù)的方面考慮這一點(diǎn),或者,如果你還沒有自然地想到這一點(diǎn),你可以進(jìn)行一般性思考。你可能意識(shí)到需要一些文本來顯示一個(gè)分?jǐn)?shù),你希望 Pygame 在屏幕上繪制這些文本。如果你仔細(xì)思考,你可能會(huì)意識(shí)到它與在屏幕上渲染一個(gè)玩家、獎(jiǎng)勵(lì)或一個(gè)平臺(tái)并多么大的不同。
從技術(shù)上講,你可以使用數(shù)字圖形,并讓 Pygame 顯示這些數(shù)字圖形。它不是達(dá)到你目標(biāo)的最容易的方法,但是如果它是你唯一知道的方法,那么它是一個(gè)有效的方法。不過,如果你參考 Pygame 的文檔,你看到列出的模塊之一是 font,這是 Pygame 使得在屏幕上來使打印文本像輸入文字一樣容易的方法。
解密技術(shù)文檔
font 文檔頁面以 pygame.font.init() 開始,它列出了用于初始化字體模塊的函數(shù)。它由 pygame.init() 自動(dòng)地調(diào)用,你已經(jīng)在代碼中調(diào)用了它。再強(qiáng)調(diào)一次,從技術(shù)上講,你已經(jīng)到達(dá)一個(gè)足夠好的點(diǎn)。雖然你尚不知道如何做,你知道你能夠使用 pygame.font 函數(shù)來在屏幕上打印文本。
然而,如果你閱讀更多一些,你會(huì)找到這里還有一種更好的方法來打印字體。pygame.freetype 模塊在文檔中的描述方式如下:
pygame.freetype模塊是pygame.fontpygame模塊的一個(gè)替代品,用于加載和渲染字體。它有原函數(shù)的所有功能,外加很多新的功能。
在 pygame.freetype 文檔頁面的下方,有一些示例代碼:
import pygameimport pygame.freetype
你的代碼應(yīng)該已經(jīng)導(dǎo)入了 Pygame,不過,請(qǐng)修改你的 import 語句以包含 Freetype 模塊:
import pygameimport sysimport osimport pygame.freetype
在 Pygame 中使用字體
從 font 模塊的描述中可以看出,顯然 Pygame 使用一種字體(不管它的你提供的或內(nèi)置到 Pygame 的默認(rèn)字體)在屏幕上渲染字體。滾動(dòng)瀏覽 pygame.freetype 文檔來找到 pygame.freetype.Font 函數(shù):
pygame.freetype.Font從支持的字體文件中創(chuàng)建一個(gè)新的字體實(shí)例。Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Fontpygame.freetype.Font.name符合規(guī)則的字體名稱。pygame.freetype.Font.path字體文件路徑。pygame.freetype.Font.size在渲染中使用的默認(rèn)點(diǎn)大小
這描述了如何在 Pygame 中構(gòu)建一個(gè)字體“對(duì)象”。把屏幕上的一個(gè)簡單對(duì)象視為一些代碼屬性的組合對(duì)你來說可能不太自然,但是這與你構(gòu)建英雄和敵人精靈的方式非常類似。你需要一個(gè)字體文件,而不是一個(gè)圖像文件。在你有一個(gè)字體文件后,你可以在你的代碼中使用 pygame.freetype.Font 函數(shù)來創(chuàng)建一個(gè)字體對(duì)象,然后使用該對(duì)象來在屏幕上渲染文本。
因?yàn)椴⒉皇鞘澜缟系拿總€(gè)人的電腦上都有完全一樣的字體,因此將你選擇的字體與你的游戲捆綁在一起是很重要的。要捆綁字體,首先在你的游戲文件夾中創(chuàng)建一個(gè)新的目錄,放在你為圖像而創(chuàng)建的文件目錄旁邊。稱其為 fonts 。
即使你的計(jì)算機(jī)操作系統(tǒng)隨附了幾種字體,但是將這些字體給予其他人是非法的。這看起來很奇怪,但法律就是這樣運(yùn)作的。如果想與你的游戲一起隨附一種字體,你必需找到一種開源或知識(shí)共享的字體,以允許你隨游戲一起提供該字體。
專門提供自由和合法字體的網(wǎng)站包括:
- Font Library
- Font Squirrel
- League of Moveable Type
當(dāng)你找到你喜歡的字體后,下載下來。解壓縮 ZIP 或 TAR 文件,并移動(dòng) .ttf 或 .otf 文件到你的項(xiàng)目目錄下的 fonts 文件夾中。
你沒有安裝字體到你的計(jì)算機(jī)上。你只是放置字體到你游戲的 fonts 文件夾中,以便 Pygame 可以使用它。如果你想,你可以在你的計(jì)算機(jī)上安裝該字體,但是沒有必要。重要的是將字體放在你的游戲目錄中,這樣 Pygame 可以“描繪”字體到屏幕上。
如果字體文件的名稱復(fù)雜且?guī)в锌崭窕蛱厥庾址恍枰匦旅纯?。文件名稱是完全任意的,并且對(duì)你來說,文件名稱越簡單,越容易將其鍵入你的代碼中。
現(xiàn)在告訴 Pygame 你的字體。從文檔中你知道,當(dāng)你至少提供了字體文件路徑給 pygame.freetype.Font 時(shí)(文檔明確指出所有其余屬性都是可選的),你將在返回中獲得一個(gè)字體對(duì)象:
Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font
創(chuàng)建一個(gè)稱為 myfont 的新變量來充當(dāng)你在游戲中字體,并放置 Font 函數(shù)的結(jié)果到這個(gè)變量中。這個(gè)示例中使用 amazdoom.ttf 字體,但是你可以使用任何你想使用的字體。在你的設(shè)置部分放置這些代碼:
font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"fonts","amazdoom.ttf")font_size = txmyfont = pygame.freetype.Font(font_path, font_size)
在 Pygame 中顯示文本
現(xiàn)在你已經(jīng)創(chuàng)建一個(gè)字體對(duì)象,你需要一個(gè)函數(shù)來繪制你想繪制到屏幕上的文本。這和你在你的游戲中繪制背景和平臺(tái)是相同的原理。
首先,創(chuàng)建一個(gè)函數(shù),并使用 myfont 對(duì)象來創(chuàng)建一些文本,設(shè)置顏色為某些 RGB 值。這必須是一個(gè)全局函數(shù);它不屬于任何具體的類:
def stats(score,health):myfont.render_to(world, (4, 4), "Score:"+str(score), WHITE, None, size=64)myfont.render_to(world, (4, 72), "Health:"+str(health), WHITE, None, size=64)
當(dāng)然,你此刻已經(jīng)知道,如果它不在主循環(huán)中,你的游戲?qū)⒉粫?huì)發(fā)生任何事,所以在文件的底部添加一個(gè)對(duì)你的 stats 函數(shù)的調(diào)用:
for e in enemy_list:e.move()stats(player.score,player.health) # draw textpygame.display.flip()
嘗試你的游戲。
當(dāng)玩家收集獎(jiǎng)勵(lì)品時(shí),得分會(huì)上升。當(dāng)玩家被敵人擊中時(shí),生命值下降。成功!
Keeping score in Pygame
不過,這里有一個(gè)問題。當(dāng)一個(gè)玩家被敵人擊中時(shí),健康度會(huì)一路下降,這是不公平的。你剛剛發(fā)現(xiàn)一個(gè)非致命的錯(cuò)誤。非致命的錯(cuò)誤是這些在應(yīng)用程序中小問題,(通常)不會(huì)阻止應(yīng)用程序啟動(dòng)或甚至導(dǎo)致停止工作,但是它們要么沒有意義,要么會(huì)惹惱用戶。這里是如何解決這個(gè)問題的方法。
修復(fù)生命值計(jì)數(shù)
當(dāng)前生命值系統(tǒng)的問題是,敵人接觸玩家時(shí),Pygame 時(shí)鐘的每一次滴答,健康度都會(huì)減少。這意味著一個(gè)緩慢移動(dòng)的敵人可能在一次遭遇中將一個(gè)玩家降低健康度至 -200 ,這不公平。當(dāng)然,你可以給你的玩家一個(gè) 10000 的起始健康度得分,而不用擔(dān)心它;這可以工作,并且可能沒有人會(huì)注意。但是這里有一個(gè)更好的方法。
當(dāng)前,你的代碼偵查出一個(gè)玩家和一個(gè)敵人發(fā)生碰撞的時(shí)候。生命值問題的修復(fù)是檢測(cè)兩個(gè)獨(dú)立的事件:什么時(shí)候玩家和敵人碰撞,并且,在它們碰撞后,什么時(shí)候它們停止碰撞。
首先,在你的玩家類中,創(chuàng)建一個(gè)變量來代表玩家和敵人碰撞在一起:
self.frame = 0self.health = 10self.damage = 0
在你的 Player 類的 update 函數(shù)中,移除這塊代碼塊:
for enemy in enemy_hit_list:self.health -= 1#print(self.health)
并且在它的位置,只要玩家當(dāng)前沒有被擊中,檢查碰撞:
if self.damage == 0:for enemy in enemy_hit_list:if not self.rect.contains(enemy):self.damage = self.rect.colliderect(enemy)
你可能會(huì)在你刪除的語句塊和你剛剛添加的語句塊之間看到相似之處。它們都在做相同的工作,但是新的代碼更復(fù)雜。最重要的是,只有當(dāng)玩家當(dāng)前沒有被擊中時(shí),新的代碼才運(yùn)行。這意味著,當(dāng)一個(gè)玩家和敵人碰撞時(shí),這些代碼運(yùn)行一次,而不是像以前那樣一直發(fā)生碰撞。
新的代碼使用兩個(gè)新的 Pygame 函數(shù)。self.rect.contains 函數(shù)檢查一個(gè)敵人當(dāng)前是否在玩家的邊界框內(nèi),并且當(dāng)它是 true 時(shí), self.rect.colliderect 設(shè)置你的新的 self.damage 變量為 1,而不管它多少次是 true 。
現(xiàn)在,即使被一個(gè)敵人擊中 3 秒,對(duì) Pygame 來說仍然看作一次擊中。
我通過通讀 Pygame 的文檔而發(fā)現(xiàn)了這些函數(shù)。你沒有必要一次閱讀完全部的文檔,并且你也沒有必要閱讀每個(gè)函數(shù)的每個(gè)單詞。不過,花費(fèi)時(shí)間在你正在使用的新的庫或模塊的文檔上是很重要的;否則,你極有可能在重新發(fā)明輪子。不要花費(fèi)一個(gè)下午的時(shí)間來嘗試修改拼接一個(gè)解決方案到一些東西,而這些東西已經(jīng)被你正在使用的框架的所解決。閱讀文檔,知悉函數(shù),并從別人的工作中獲益!
最后,添加另一個(gè)代碼語句塊來偵查出什么時(shí)候玩家和敵人不再接觸。然后直到那時(shí),才從玩家減少一個(gè)生命值。
if self.damage == 1:idx = self.rect.collidelist(enemy_hit_list)if idx == -1:self.damage = 0 # set damage back to 0self.health -= 1 # subtract 1 hp
注意,只有當(dāng)玩家被擊中時(shí),這個(gè)新的代碼才會(huì)被觸發(fā)。這意味著,在你的玩家在你的游戲世界正在探索或收集獎(jiǎng)勵(lì)時(shí),這個(gè)代碼不會(huì)運(yùn)行。它僅當(dāng) self.damage 變量被激活時(shí)運(yùn)行。
當(dāng)代碼運(yùn)行時(shí),它使用 self.rect.collidelist 來查看玩家是否仍然接觸在你敵人列表中的敵人(當(dāng)其未偵查到碰撞時(shí),collidelist 返回 -1)。在它沒有接觸敵人時(shí),是該處理 self.damage 的時(shí)機(jī):通過設(shè)置 self.damage 變量回到 0 來使其無效,并減少一點(diǎn)生命值。
現(xiàn)在嘗試你的游戲。
得分反應(yīng)
現(xiàn)在,你有一個(gè)來讓你的玩家知道它們分?jǐn)?shù)和生命值的方法,當(dāng)你的玩家達(dá)到某些里程碑時(shí),你可以確保某些事件發(fā)生。例如,也許這里有一個(gè)特殊的恢復(fù)一些生命值的獎(jiǎng)勵(lì)項(xiàng)目。也許一個(gè)到達(dá) 0 生命值的玩家不得不從一個(gè)關(guān)卡的起始位置重新開始。
你可以在你的代碼中檢查這些事件,并且相應(yīng)地操縱你的游戲世界。你已經(jīng)知道該怎么做,所以請(qǐng)瀏覽文檔來尋找新的技巧,并且獨(dú)立地嘗試這些技巧。
這里是到目前為止所有的代碼:
#!/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# add scrolling# add loot# add score# 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 pygameimport sysimport osimport pygame.freetype'''Objects'''class Platform(pygame.sprite.Sprite):# x location, y location, img width, img height, img filedef __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 = ylocself.rect.x = xlocclass Player(pygame.sprite.Sprite):'''Spawn a player'''def __init__(self):pygame.sprite.Sprite.__init__(self)self.movex = 0self.movey = 0self.frame = 0self.health = 10self.damage = 0self.collide_delta = 0self.jump_delta = 6self.score = 1self.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 = 0def gravity(self):self.movey += 3.2 # how fast player fallsif self.rect.y > worldy and self.movey >= 0:self.movey = 0self.rect.y = worldy-tydef control(self,x,y):'''control player movement'''self.movex += xself.movey += ydef update(self):'''Update sprite position'''self.rect.x = self.rect.x + self.movexself.rect.y = self.rect.y + self.movey# moving leftif self.movex < 0:self.frame += 1if self.frame > ani*3:self.frame = 0self.image = self.images[self.frame//ani]# moving rightif self.movex > 0:self.frame += 1if self.frame > ani*3:self.frame = 0self.image = self.images[(self.frame//ani)+4]# collisionsenemy_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 # set damage back to 0self.health -= 1 # subtract 1 hploot_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)for p in plat_hit_list:self.collide_delta = 0 # stop jumpingself.movey = 0if self.rect.y > p.rect.y:self.rect.y = p.rect.y+tyelse:self.rect.y = p.rect.y-tyground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)for g in ground_hit_list:self.movey = 0self.rect.y = worldy-ty-tyself.collide_delta = 0 # stop jumpingif self.rect.y > g.rect.y:self.health -=1print(self.health)if self.collide_delta < 6 and self.jump_delta < 6:self.jump_delta = 6*2self.movey -= 33 # how high to jumpself.collide_delta += 6self.jump_delta += 6class 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 = xself.rect.y = yself.counter = 0def move(self):'''enemy movement'''distance = 80speed = 8self.movey += 3.2if 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 += 1if not self.rect.y >= worldy-ty-ty:self.rect.y += self.moveyplat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)for p in plat_hit_list:self.movey = 0if self.rect.y > p.rect.y:self.rect.y = p.rect.y+tyelse:self.rect.y = p.rect.y-tyground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)for g in ground_hit_list:self.rect.y = worldy-ty-tyclass Level():def bad(lvl,eloc):if lvl == 1:enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemyenemy_list = pygame.sprite.Group() # create enemy groupenemy_list.add(enemy) # add enemy to groupif lvl == 2:print("Level " + str(lvl) )return enemy_listdef loot(lvl,tx,ty):if lvl == 1:loot_list = pygame.sprite.Group()loot = Platform(200,ty*7,tx,ty, 'loot_1.png')loot_list.add(loot)if lvl == 2:print(lvl)return loot_listdef 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,'ground.png')ground_list.add(ground)i=i+1if lvl == 2:print("Level " + str(lvl) )return ground_listdef platform(lvl,tx,ty):plat_list = pygame.sprite.Group()ploc = []i=0if lvl == 1:ploc.append((20,worldy-ty-128,3))ploc.append((300,worldy-ty-256,3))ploc.append((500,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,'ground.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 stats(score,health):myfont.render_to(world, (4, 4), "Score:"+str(score), SNOWGRAY, None, size=64)myfont.render_to(world, (4, 72), "Health:"+str(health), SNOWGRAY, None, size=64)'''Setup'''worldx = 960worldy = 720fps = 40 # frame rateani = 4 # animation cyclesclock = pygame.time.Clock()pygame.init()main = TrueBLUE = (25,25,200)BLACK = (23,23,23 )WHITE = (254,254,254)SNOWGRAY = (137,164,166)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 playerplayer.rect.x = 0player.rect.y = 0player_list = pygame.sprite.Group()player_list.add(player)steps = 10forwardx = 600backwardx = 230eloc = []eloc = [200,20]gloc = []tx = 64 #tile sizety = 64 #tile sizefont_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"fonts","amazdoom.ttf")font_size = txmyfont = pygame.freetype.Font(font_path, font_size)i=0while i <= (worldx/tx)+tx:gloc.append(i*tx)i=i+1enemy_list = Level.bad(<
當(dāng)前文章:添加計(jì)分到你的Python游戲
鏈接分享:http://m.5511xx.com/article/dppidip.html


咨詢
建站咨詢
