在你的 Python 平台類遊戲中放一些獎勵
這是正在進行的關於使用 Python 3 的 Pygame 模塊創建視頻遊戲的系列文章的第十部分。以前的文章有:
- 通過構建一個簡單的擲骰子遊戲去學習怎麼用 Python 編程
- 使用 Python 和 Pygame 模塊構建一個遊戲框架
- 如何在你的 Python 遊戲中添加一個玩家
- 用 Pygame 使你的遊戲角色移動起來
- 如何向你的 Python 遊戲中添加一個敵人
- 在 Pygame 遊戲中放置平台
- 在你的 Python 遊戲中模擬引力
- 為你的 Python 平台類遊戲添加跳躍功能
- 使你的 Python 遊戲玩家能夠向前和向後跑
如果你已經閱讀了本系列的前幾篇文章,那麼你已經了解了編寫遊戲的所有基礎知識。現在你可以在這些基礎上,創造一個全功能的遊戲。當你第一次學習時,遵循本系列代碼示例,這樣的「用例」是有幫助的,但是,用例也會約束你。現在是時候運用你學到的知識,以新的方式應用它們了。
如果說,說起來容易做起來難,這篇文章展示了一個如何將你已經了解的內容用於新目的的例子中。具體來說,就是它涵蓋了如何使用你以前的課程中已經了解到的來實現獎勵系統。
在大多數電子遊戲中,你有機會在遊戲世界中獲得「獎勵」或收集到寶物和其他物品。獎勵通常會增加你的分數或者你的生命值,或者為你的下一次任務提供信息。
遊戲中包含的獎勵類似於編程平台。像平台一樣,獎勵沒有用戶控制,隨著遊戲世界的滾動進行,並且必須檢查與玩家的碰撞。
創建獎勵函數
獎勵和平台非常相似,你甚至不需要一個獎勵的類。你可以重用 Platform
類,並將結果稱為「獎勵」。
由於獎勵類型和位置可能因關卡不同而不同,如果你還沒有,請在你的 Level
中創建一個名為 loot
的新函數。因為獎勵物品不是平台,你也必須創建一個新的 loot_list
組,然後添加獎勵物品。與平台、地面和敵人一樣,該組用於檢查玩家碰撞:
def loot(lvl,lloc):
if lvl == 1:
loot_list = pygame.sprite.Group()
loot = Platform(300,ty*7,tx,ty, 'loot_1.png')
loot_list.add(loot)
if lvl == 2:
print(lvl)
return loot_list
你可以隨意添加任意數量的獎勵對象;記住把每一個都加到你的獎勵清單上。Platform
類的參數是獎勵圖標的 X 位置、Y 位置、寬度和高度(通常讓你的獎勵精靈保持和所有其他方塊一樣的大小最為簡單),以及你想要用作的獎勵的圖片。獎勵的放置可以和貼圖平台一樣複雜,所以使用創建關卡時需要的關卡設計文檔。
在腳本的設置部分調用新的獎勵函數。在下面的代碼中,前三行是上下文,所以只需添加第四行:
enemy_list = Level.bad( 1, eloc )
ground_list = Level.ground( 1,gloc,tx,ty )
plat_list = Level.platform( 1,tx,ty )
loot_list = Level.loot(1,tx,ty)
正如你現在所知道的,除非你把它包含在你的主循環中,否則獎勵不會被顯示到屏幕上。將下面代碼示例的最後一行添加到循環中:
enemy_list.draw(world)
ground_list.draw(world)
plat_list.draw(world)
loot_list.draw(world)
啟動你的遊戲看看會發生什麼。
![Loot in Python platformer](/data/attachment/album/202001/29/131252hahl80ckmu2p0hk4.jpg "Loot in Python platformer")
你的獎勵將會顯示出來,但是當你的玩家碰到它們時,它們不會做任何事情,當你的玩家經過它們時,它們也不會滾動。接下來解決這些問題。
滾動獎勵
像平台一樣,當玩家在遊戲世界中移動時,獎勵必須滾動。邏輯與平台滾動相同。要向前滾動獎勵物品,添加最後兩行:
for e in enemy_list:
e.rect.x -= scroll
for l in loot_list:
l.rect.x -= scroll
要向後滾動,請添加最後兩行:
for e in enemy_list:
e.rect.x += scroll
for l in loot_list:
l.rect.x += scroll
再次啟動你的遊戲,看看你的獎勵物品現在表現得像在遊戲世界裡一樣了,而不是僅僅畫在上面。
檢測碰撞
就像平台和敵人一樣,你可以檢查獎勵物品和玩家之間的碰撞。邏輯與其他碰撞相同,除了撞擊不會(必然)影響重力或生命值。取而代之的是,命中會導致獎勵物品會消失並增加玩家的分數。
當你的玩家觸摸到一個獎勵對象時,你可以從 loot_list
中移除該對象。這意味著當你的主循環在 loot_list
中重繪所有獎勵物品時,它不會重繪那個特定的對象,所以看起來玩家已經獲得了獎勵物品。
在 Player
類的 update
函數中的平台碰撞檢測之上添加以下代碼(最後一行僅用於上下文):
loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False)
for loot in loot_hit_list:
loot_list.remove(loot)
self.score += 1
print(self.score)
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
當碰撞發生時,你不僅要把獎勵從它的組中移除,還要給你的玩家一個分數提升。你還沒有創建分數變數,所以請將它添加到你的玩家屬性中,該屬性是在 Player
類的 __init__
函數中創建的。在下面的代碼中,前兩行是上下文,所以只需添加分數變數:
self.frame = 0
self.health = 10
self.score = 0
當在主循環中調用 update
函數時,需要包括 loot_list
:
player.gravity()
player.update()
如你所見,你已經掌握了所有的基本知識。你現在要做的就是用新的方式使用你所知道的。
在下一篇文章中還有一些提示,但是與此同時,用你學到的知識來製作一些簡單的單關卡遊戲。限制你試圖創造的東西的範圍是很重要的,這樣你就不會埋沒自己。這也使得最終的成品看起來和感覺上更容易完成。
以下是迄今為止你為這個 Python 平台編寫的所有代碼:
#!/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
# 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)
loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False)
for loot in loot_hit_list:
loot_list.remove(loot)
self.score += 1
print(self.score)
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,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_list
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((20,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
forwardx = 600
backwardx = 230
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 )
loot_list = Level.loot(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
# scroll the world forward
if player.rect.x >= forwardx:
scroll = player.rect.x - forwardx
player.rect.x = forwardx
for p in plat_list:
p.rect.x -= scroll
for e in enemy_list:
e.rect.x -= scroll
for l in loot_list:
l.rect.x -= scroll
# scroll the world backward
if player.rect.x <= backwardx:
scroll = backwardx - player.rect.x
player.rect.x = backwardx
for p in plat_list:
p.rect.x += scroll
for e in enemy_list:
e.rect.x += scroll
for l in loot_list:
l.rect.x += scroll
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
loot_list.draw(world) # refresh loot
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
via: https://opensource.com/article/20/1/loot-python-platformer-game
作者:Seth Kenlon 選題:lujun9972 譯者:heguangzhi 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive