前言
這一期我們會帶大家進一步復現我們的魔塔小游戲,主要內容為進一步完成和其他更為復雜的地圖元素接觸時可以觸發的事件,
廢話不多說,讓我們愉快地開始吧~
開發工具
Python版本: 3.7.4
相關模塊:
pygame模塊;
以及一些python自帶的模塊,
環境搭建
安裝Python并添加到環境變數,pip安裝需要的相關模塊即可,
原理簡介
上一期,我們實作了一些簡單的勇士和地圖元素接觸時會觸發的事件,就像下圖這樣:

顯然,這個效果圖是不完美的,比如左側面板的時間顯示和當前層的顯示沒有添加,這里我們可以先寫幾行代碼添加一下:
# --左側面板欄
font = pygame.font.Font(self.cfg.FONTPATH_CN, 20)
font_renders = [
self.hero.font.render(str(self.map_level_pointer), True, (255, 255, 255)),
font.render('游戲時間: ' + str(pygame.time.get_ticks() // 60000) + ' 分 ' + str(pygame.time.get_ticks() // 1000 % 60) + ' 秒', True, (255, 255, 255)),
]
rects = [fr.get_rect() for fr in font_renders]
rects[0].topleft = (150, 530)
rects[1].topleft = (75, 630)
for fr, rect in zip(font_renders, rects):
screen.blit(fr, rect)
添加了這部分代碼之后的效果是這樣子的:

接著,就是上一期我們說的,在原版的游戲中,勇士和這個仙女碰撞的時候,會出現對話框,類似這樣:

這部分內容該如何實作呢?首先,可以肯定的是對話框是由四個部分組成的,即矩形、矩形內填充的底色、左上角的人物圖示以及文字內容,他們的實作思路分別應該是:
矩形: 呼叫pygame畫矩形的函式pygame.draw.rect;
底色填充: 匯入背景圖中黑色的地磚來填充對話框;
左上角人物圖示: pygame.image.load匯入后畫到對應的位置即可;
文字: 主要呼叫pygame.font.Font實作.
具體而言,我們的代碼實作如下:
'''仙女和勇士對話'''
def showconversationheroandfairy(self, screen, scenes):
# 對話框指標
conversation_pointer = 0
# 定義所有對話
conversations = [
['......'],
['你醒了!'],
['......', '你是誰? 我在哪里?'],
['我是這里的仙子, 剛才你被這里的', '小怪打暈了.'],
['......', '劍, 劍, 我的劍呢?'],
['你的劍被他們搶走了, 我只來得及', '將你救出來.'],
['那, 公主呢? 我是來救公主的.'],
['公主還在里面, 你這樣進去是打不', '過里面的小怪的.'],
['那我怎么辦, 我答應了國王一定要', '把公主救出來的,那我現在應該怎', '么辦呢?'],
['放心吧, 我把我的力量借給你, 你', '就可以打贏那些小怪了. 不過, 你', '得先去幫我去找一樣東西,找到', '了再來這里找我.'],
['找東西? 找什么東西?'],
['是一個十字架, 中間有一顆紅色的', '寶石.'],
['那個東西有什么用嗎?'],
['我本是這座塔守護者, 可不久前, ', '從北方來了一批惡魔, 他們占領了', '這座塔,并將我的魔力封在了這', '個十字架里面, 如果你能將它帶出', '塔來, 那我的魔力便會慢慢地恢復, ', '到那時我便可以把力量借給你去', '救公主了. 要記住, 只有用我的魔力', '才可以打開二十一層的門.'],
['......', '好吧,我試試看'],
['剛才我去看過了, 你的劍被放在三', '樓, 你的盾在五樓上, 而那個十字', '架被放在七樓. 要到七樓, 你得', '先取回你的劍和盾. 另外在塔里的', '其他樓層上, 還有一些存放了好幾百', '年的寶劍和寶物,如果得到它們,', '對于你對付這里面的怪物將有很大', '的幫助.'],
['可是, 我怎么進去呢?'],
['我這里有三把鑰匙, 你先拿去, 在', '塔里面還有很多這樣的鑰匙, 你一', '定要珍惜使用. 勇敢的去吧,勇士!']
]
# 主回圈
clock = pygame.time.Clock()
while True:
screen.fill((0, 0, 0))
screen.blit(self.background_images['gamebg'], (0, 0))
self.map_parser.draw(screen)
for scene in scenes:
screen.blit(scene[0], scene[1])
self.hero.draw(screen)
# --按鍵檢測
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
conversation_pointer += 1
if conversation_pointer >= len(conversations): return
# --畫對話框
conversation = conversations[conversation_pointer]
font = pygame.font.Font(self.cfg.FONTPATH_CN, 20)
# ----勇士
if conversation_pointer % 2 == 0:
left, top, width, height = 510, 430, 7, 2
pygame.draw.rect(screen, (199, 97, 20), (left - 4, top - 4, self.cfg.BLOCKSIZE * width + 8, self.cfg.BLOCKSIZE * height + 8), 7)
id_image = self.hero.images['down']
# ----仙子
else:
left, top, width, height = 300, 250, 7, 2
if len(conversation) > 3: height = 3
if len(conversation) > 5: height = 4
if len(conversation) > 7: height = 5
pygame.draw.rect(screen, (199, 97, 20), (left - 4, top - 4, self.cfg.BLOCKSIZE * width + 8, self.cfg.BLOCKSIZE * height + 8), 7)
id_image = pygame.image.load(self.cfg.MAPELEMENTSPATHS['24'][0])
# ----底色
filepath = self.cfg.MAPELEMENTSPATHS['0'][0]
for col in range(width):
for row in range(height):
image = pygame.image.load(filepath)
image = pygame.transform.scale(image, (self.cfg.BLOCKSIZE, self.cfg.BLOCKSIZE))
screen.blit(image, (left + col * self.cfg.BLOCKSIZE, top + row * self.cfg.BLOCKSIZE))
# ----左上角圖示
screen.blit(id_image, (left + 10, top + 10))
# ----對話框中的文字
for idx, text in enumerate(conversation):
font_render = font.render(text, True, (255, 255, 255))
rect = font_render.get_rect()
rect.left, rect.top = left + self.cfg.BLOCKSIZE + 20, top + 10 + idx * 30
screen.blit(font_render, rect)
# --重繪
pygame.display.flip()
clock.tick(self.cfg.FPS)
具體的效果圖如下:

接下來,我們需要實作的就是和怪物戰斗的場景啦,按照原版游戲的設定,當勇士接觸到地圖上的怪物時,如果勇士可以擊敗該怪物,則自動觸發戰斗模式:

要實作這個功能,我們需要先定義地圖上所有怪物的生命值,攻擊力和防御力數值,具體而言,代碼定義如下:
# 地圖上所有怪物的屬性: 名字, 生命值, 攻擊力, 防御力
self.monsters_dict = {
'40': ('綠頭怪', 50, 20, 1),
'41': ('紅頭怪', 70, 15, 2),
'42': ('小蝙蝠', 100, 20, 5),
'43': ('青頭怪', 200, 35, 10),
'44': ('骷髏人', 110, 25, 5),
'45': ('骷髏士兵', 150, 40, 20),
'46': ('獸面人', 300, 75, 45),
'47': ('初級衛兵', 450, 150, 90),
'48': ('大蝙蝠', 150, 65, 30),
'49': ('紅蝙蝠', 550, 160, 90),
'50': ('白衣武士', 1300, 300, 150),
'51': ('怪王', 700, 250, 125),
'52': ('紅衣法師', 500, 400, 260),
'53': ('紅衣魔王', 15000, 1000, 1000),
'54': ('金甲衛士', 850, 350, 200),
'55': ('金甲隊長', 900, 750, 650),
'56': ('骷髏隊長', 400, 90, 50),
'57': ('靈法師', 1500, 830, 730),
'58': ('靈武士', 1200, 980, 900),
'59': ('冥靈魔王', 30000, 1700, 1500),
'60': ('麻衣法師', 250, 120, 70),
'61': ('冥戰士', 2000, 680, 590),
'62': ('冥隊長', 2500, 900, 850),
'63': ('初級法師', 125, 50, 25),
'64': ('高級法師', 100, 200, 110),
'65': ('石頭怪人', 500, 115, 65),
'66': ('獸面戰士', 900, 450, 330),
'67': ('雙手劍士', 1200, 620, 520),
'68': ('冥衛兵', 1250, 500, 400),
'69': ('高級衛兵', 1500, 560, 460),
'70': ('影子戰士', 3100, 1150, 1050),
'188': ('血影', 99999, 5000, 4000),
'198': ('魔龍', 99999, 9999, 5000),
}
其中,字典的鍵值和第一期童年經典回憶 | 從零開始帶大家擼一個魔塔小游戲呀(1)中定義地圖的地圖檔案里的數字對應的地圖元素的含義是一致的,接下來,為了實作和原版一樣的功能,我們需要寫個函式判斷一下勇士當前的狀態是否可以擊敗怪物:
'''判斷勇士是否可以打贏怪物'''
def winmonster(self, monster):
# 如果攻擊力低于怪物防御力, monster: [名字, 生命值, 攻擊力, 防御力]
if self.attack_power <= monster[3]: return False
# 如果防御力高于怪物攻擊力
if self.defense_power >= monster[2]: return True
# 我方打怪物一次扣多少血
diff_our = self.attack_power - monster[3]
# 怪物打我方一次扣多少血
diff_monster = monster[2] - self.defense_power
# 計算誰可以win
if round(monster[1] / diff_our) <= round(self.life_value / diff_monster):
return True
return False
如果可以,則觸發戰斗場景,具體而言,我們已經做好了戰斗的基礎面板,每次只需要把對應的怪物、勇士以及他們各自的狀態畫上去即可,原理類似下圖:

主要的代碼實作如下:
'''戰斗畫面'''
def battle(self, monster, monster_image, map_parser, screen):
monster = list(monster).copy()
# 我方打怪物一次扣多少血
diff_our = self.attack_power - monster[3]
# 怪物打我方一次扣多少血
diff_monster = monster[2] - self.defense_power
# 更新戰斗面板的頻率
update_count, update_interval, update_hero = 0, 10, False
# 主回圈
clock = pygame.time.Clock()
font = pygame.font.Font(self.cfg.FONTPATH_CN, 40)
while True:
screen.fill((0, 0, 0))
screen.blit(self.background_images['gamebg'], (0, 0))
map_parser.draw(screen)
for scene in self.cur_scenes:
screen.blit(scene[0], scene[1])
self.draw(screen)
# --按鍵檢測
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# --更新戰斗面板
update_count += 1
if update_count > update_interval:
update_count = 0
if update_hero:
self.life_value = self.life_value - (monster[2] - self.defense_power)
else:
monster[1] = max(monster[1] - (self.attack_power - monster[3]), 0)
update_hero = not update_hero
if monster[1] <= 0: return
screen.blit(self.background_images['battlebg'], (20, 40))
screen.blit(monster_image, (90, 140))
font_renders = [
font.render(str(monster[1]), True, (255, 255, 255)),
font.render(str(monster[2]), True, (255, 255, 255)),
font.render(str(monster[3]), True, (255, 255, 255)),
font.render(str(self.life_value), True, (255, 255, 255)),
font.render(str(self.attack_power), True, (255, 255, 255)),
font.render(str(self.defense_power), True, (255, 255, 255)),
]
rects = [fr.get_rect() for fr in font_renders]
for idx in range(3):
rects[idx].top, rects[idx].left = 78 + idx * 95, 320
for idx in range(3, 6):
rects[idx].top, rects[idx].right = 78 + (idx - 3) * 95, 655
for fr, rect in zip(font_renders, rects):
screen.blit(fr, rect)
# --重繪
pygame.display.flip()
clock.tick(self.cfg.FPS)
原理其實很簡單,由我方的勇士先發起一次攻擊,然后再由怪物發起一次攻擊,以此類推,該程序中戰斗面板實時更新當前勇士和怪物的狀態,最終的效果如下圖所示:

本期完整源代碼可以私信獲取~
文章到這里就結束了,感謝你的觀看,Python29個小游戲系列,下篇文章分享魔塔小游戲呀(4)
為了感謝讀者們,我想把我最近收藏的一些編程干貨分享給大家,回饋每一個讀者,希望能幫到你們,
往期回顧
Python從零開始帶大家實作一個魔塔小游戲(1)
Python從零開始帶大家實作一個魔塔小游戲(2)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/337832.html
標籤:其他
上一篇:二十一天——打卡第四天
下一篇:【100個 Unity小知識點】?? | Unity 可以在編輯器中讀取Excel,打包成exe后就無法讀取的問題
