在 Pygame 遊戲中放置平臺
在這個從零構建一個 Python 遊戲系列的第六部分中,為你的角色建立一些平臺來旅行。
這是仍在進行中的關於使用 Pygame 模組來在 Python 3 中建立電腦遊戲的系列文章的第六部分。先前的文章是:
- 通過構建一個簡單的擲骰子游戲去學習怎麼用 Python 程式設計
- 使用 Python 和 Pygame 模組構建一個遊戲框架
- 如何在你的 Python 遊戲中新增一個玩家
- 用 Pygame 使你的遊戲角色移動起來
- 如何向你的 Python 遊戲中新增一個敵人
一個平臺類遊戲需要平臺。
在 Pygame 中,平臺本身也是個妖精,正像你那個可玩的妖精。這一點是重要的,因為有個是物件的平臺,可以使你的玩家妖精更容易與之互動。
建立平臺有兩個主要步驟。首先,你必須給該物件編寫程式碼,然後,你必須對映出你希望該物件出現的位置。
編碼平臺物件
要構建一個平臺物件,你要建立一個名為 Platform
的類。它是一個妖精,正像你的 Player
妖精 一樣,帶有很多相同的屬性。
你的 Platform
類需要知道很多平臺型別的資訊,它應該出現在遊戲世界的哪裡、它應該包含的什麼圖片等等。這其中很多資訊可能還尚不存在,這要看你為你的遊戲計劃了多少,但是沒有關係。正如直到移動你的遊戲角色那篇文章結束時,你都沒有告訴你的玩家妖精移動速度有多快,你不必事先告訴 Platform
每一件事。
在這系列中你所寫的指令碼的開頭附近,建立一個新的類。在這個程式碼示例中前三行是用於說明上下文,因此在註釋的下面新增程式碼:
import pygame
import sys
import os
## 新程式碼如下:
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.image.set_colorkey(ALPHA)
self.rect = self.image.get_rect()
self.rect.y = yloc
self.rect.x = xloc
當被呼叫時,這個類在某個 X 和 Y 位置上建立一個屏上物件,具有某種寬度和高度,並使用某種影象作為紋理。這與如何在屏上繪製出玩家或敵人非常類似。
平臺的型別
下一步是繪製出你的平臺需要出現的地方。
瓷磚方式
實現平臺類遊戲世界有幾種不同的方法。在最初的橫向滾軸遊戲中,例如,馬里奧超級兄弟和刺蝟索尼克,這個技巧是使用“瓷磚”方式,也就是說有幾個代表地面和各種平臺的塊,並且這些塊被重複使用來製作一個關卡。你只能有 8 或 12 種不同的塊,你可以將它們排列在螢幕上來建立地面、浮動的平臺,以及你遊戲中需要的一切其它的事物。有人發現這是製作遊戲最容易的方法了,因為你只需要製作(或下載)一小組關卡素材就能建立很多不同的關卡。然而,這裡的程式碼需要一點數學知識。
SuperTux ,一個基於瓷磚的電腦遊戲。
手工繪製方式
另一種方法是將每個素材作為一個整體影象。如果你喜歡為遊戲世界建立素材,那你會在用圖形應用程式構建遊戲世界的每個部分上花費很多時間。這種方法不需要太多的數學知識,因為所有的平臺都是整體的、完整的物件,你只需要告訴 Python 將它們放在螢幕上的什麼位置。
每種方法都有優勢和劣勢,並且根據於你選擇使用的方式,程式碼稍有不同。我將覆蓋這兩方面,所以你可以在你的工程中使用一種或另一種,甚至兩者的混合。
關卡繪製
總的來說,繪製你的遊戲世界是關卡設計和遊戲程式設計中的一個重要的部分。這需要數學知識,但是沒有什麼太難的,而且 Python 擅長數學,它會有所幫助。
你也許發現先在紙張上設計是有用的。拿一張表格紙,並繪製一個方框來代表你的遊戲窗體。在方框中繪製平臺,並標記其每一個平臺的 X 和 Y 座標,以及它的寬度和高度。在方框中的實際位置沒有必要是精確的,你只要保持數字合理即可。譬如,假設你的螢幕是 720 畫素寬,那麼你不能在一個螢幕上放 8 塊 100 畫素的平臺。
當然,不是你遊戲中的所有平臺都必須容納在一個螢幕大小的方框裡,因為你的遊戲將隨著你的玩家行走而滾動。所以,可以繼續繪製你的遊戲世界到第一螢幕的右側,直到關卡結束。
如果你更喜歡精確一點,你可以使用方格紙。當設計一個瓷磚類的遊戲時,這是特別有用的,因為每個方格可以代表一個瓷磚。
一個關卡地圖示例。
座標系
你可能已經在學校中學習過笛卡爾座標系。你學習的東西也適用於 Pygame,除了在 Pygame 中你的遊戲世界的座標系的原點 0,0
是放置在你的螢幕的左上角而不是在中間,是你在地理課上用過的座標是在中間的。
在 Pygame 中的座標示例。
X 軸起始於最左邊的 0,向右無限增加。Y 軸起始於螢幕頂部的 0,向下延伸。
圖片大小
如果你不知道你的玩家、敵人、平臺是多大的,繪製出一個遊戲世界是毫無意義的。你可以在圖形程式中找到你的平臺或瓷磚的尺寸。例如在 Krita 中,單擊“影象”選單,並選擇“屬性”。你可以在“屬性”視窗的最頂部處找到它的尺寸。
另外,你也可以建立一個簡單的 Python 指令碼來告訴你的一個影象的尺寸。開啟一個新的文字檔案,並輸入這些程式碼到其中:
#!/usr/bin/env python3
from PIL import Image
import os.path
import sys
if len(sys.argv) > 1:
print(sys.argv[1])
else:
sys.exit('Syntax: identify.py [filename]')
pic = sys.argv[1]
dim = Image.open(pic)
X = dim.size[0]
Y = dim.size[1]
print(X,Y)
儲存該文字檔案為 identify.py
。
要使用這個指令碼,你必須安裝一些額外的 Python 模組,它們包含了這個指令碼中新使用的關鍵字:
$ pip3 install Pillow --user
一旦安裝好,在你遊戲工程目錄中執行這個指令碼:
$ python3 ./identify.py images/ground.png
(1080, 97)
在這個示例中,地面平臺的圖形的大小是 1080 畫素寬和 97 畫素高。
平臺塊
如果你選擇單獨地繪製每個素材,你必須建立想要插入到你的遊戲世界中的幾個平臺和其它元素,每個素材都放在它自己的檔案中。換句話說,你應該讓每個素材都有一個檔案,像這樣:
每個物件一個圖形檔案。
你可以按照你希望的次數重複使用每個平臺,只要確保每個檔案僅包含一個平臺。你不能使用一個檔案包含全部素材,像這樣:
你的關卡不能是一個圖形檔案。
當你完成時,你可能希望你的遊戲看起來像這樣,但是如果你在一個大檔案中建立你的關卡,你就沒有方法從背景中區分出一個平臺,因此,要麼把物件繪製在它們自己的檔案中,要麼從一個更大的檔案中裁剪出它們,並儲存為單獨的副本。
注意: 如同你的其它素材,你可以使用 GIMP、Krita、MyPaint,或 Inkscape 來建立你的遊戲素材。
平臺出現在每個關卡開始的螢幕上,因此你必須在你的 Level
類中新增一個 platform
函式。在這裡特例是地面平臺,它重要到應該擁有它自己的一個組。通過把地面看作一組特殊型別的平臺,你可以選擇它是否滾動,或它上面是否可以站立,而其它平臺可以漂浮在它上面。這取決於你。
新增這兩個函式到你的 Level
類:
def ground(lvl,x,y,w,h):
ground_list = pygame.sprite.Group()
if lvl == 1:
ground = Platform(x,y,w,h,'block-ground.png')
ground_list.add(ground)
if lvl == 2:
print("Level " + str(lvl) )
return ground_list
def platform( lvl ):
plat_list = pygame.sprite.Group()
if lvl == 1:
plat = Platform(200, worldy-97-128, 285,67,'block-big.png')
plat_list.add(plat)
plat = Platform(500, worldy-97-320, 197,54,'block-small.png')
plat_list.add(plat)
if lvl == 2:
print("Level " + str(lvl) )
return plat_list
ground
函式需要一個 X 和 Y 位置,以便 Pygame 知道在哪裡放置地面平臺。它也需要知道平臺的寬度和高度,這樣 Pygame 知道地面延伸到每個方向有多遠。該函式使用你的 Platform
類來生成一個屏上物件,然後將這個物件新增到 ground_list
組。
platform
函式本質上是相同的,除了其有更多的平臺。在這個示例中,僅有兩個平臺,但是你可以想有多少就有多少。在進入一個平臺後,在列出另一個前你必須新增它到 plat_list
中。如果你不新增平臺到組中,那麼它將不出現在你的遊戲中。
提示: 很難想象你的遊戲世界的 0 是在頂部,因為在真實世界中發生的情況是相反的;當估計你有多高時,你不會從上往下測量你自己,而是從腳到頭頂來測量。
如果對你來說從“地面”上來構建你的遊戲世界更容易,將 Y 軸值表示為負數可能有幫助。例如,你知道你的遊戲世界的底部是
worldy
的值。因此worldy
減去地面的高度(在這個示例中是 97)是你的玩家正常站立的位置。如果你的角色是 64 畫素高,那麼地面減去 128 正好是你的玩家的兩倍高。事實上,一個放置在 128 畫素處平臺大約是相對於你的玩家的兩層樓高度。一個平臺在 -320 處比三層樓更高。等等。
正像你現在可能所知的,如果你不使用它們,你的類和函式是沒有價值的。新增這些程式碼到你的設定部分(第一行只是上下文,所以新增最後兩行):
enemy_list = Level.bad( 1, eloc )
ground_list = Level.ground( 1,0,worldy-97,1080,97 )
plat_list = Level.platform( 1 )
並把這些行加到你的主迴圈(再一次,第一行僅用於上下文):
enemy_list.draw(world) # 重新整理敵人
ground_list.draw(world) # 重新整理地面
plat_list.draw(world) # 重新整理平臺
瓷磚平臺
瓷磚類遊戲世界更容易製作,因為你只需要在前面繪製一些塊,就能在遊戲中一再使用它們建立每個平臺。在像 OpenGameArt.org 這樣的網站上甚至有一套瓷磚供你來使用。
Platform
類與在前面部分中的類是相同的。
ground
和 platform
在 Level
類中,然而,必須使用迴圈來計算使用多少塊來建立每個平臺。
如果你打算在你的遊戲世界中有一個堅固的地面,這種地面是很簡單的。你只需要從整個視窗的一邊到另一邊“克隆”你的地面瓷磚。例如,你可以建立一個 X 和 Y 值的列表來規定每個瓷磚應該放置的位置,然後使用一個迴圈來獲取每個值並繪製每一個瓷磚。這僅是一個示例,所以不要新增這到你的程式碼:
# Do not add this to your code
gloc = [0,656,64,656,128,656,192,656,256,656,320,656,384,656]
不過,如果你仔細看,你可以看到所有的 Y 值是相同的,X 值以 64 的增量不斷地增加 —— 這就是瓷磚的大小。這種重複是精確地,是計算機擅長的,因此你可以使用一點數學邏輯來讓計算機為你做所有的計算:
新增這些到你的指令碼的設定部分:
gloc = []
tx = 64
ty = 64
i=0
while i <= (worldx/tx)+tx:
gloc.append(i*tx)
i=i+1
ground_list = Level.ground( 1,gloc,tx,ty )
現在,不管你的視窗的大小,Python 會通過瓷磚的寬度分割遊戲世界的寬度,並建立一個陣列列表列出每個 X 值。這裡不計算 Y 值,因為在平的地面上這個從不會變化。
為了在一個函式中使用陣列,使用一個 while
迴圈,檢視每個條目並在適當的位置新增一個地面瓷磚:
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,'tile-ground.png')
ground_list.add(ground)
i=i+1
if lvl == 2:
print("Level " + str(lvl) )
return ground_list
除了 while
迴圈,這幾乎與在上面一部分中提供的瓷磚類平臺的 ground
函式的程式碼相同。
對於移動的平臺,原理是相似的,但是這裡有一些技巧可以使它簡單。
你可以通過它的起始畫素(它的 X 值)、距地面的高度(它的 Y 值)、繪製多少瓷磚來定義一個平臺,而不是通過畫素繪製每個平臺。這樣,你不必操心每個平臺的寬度和高度。
這個技巧的邏輯有一點複雜,因此請仔細複製這些程式碼。有一個 while
迴圈巢狀在另一個 while
迴圈的內部,因為這個函式必須考慮每個陣列項的三個值來成功地建造一個完整的平臺。在這個示例中,這裡僅有三個平臺以 ploc.append
語句定義,但是你的遊戲可能需要更多,因此你需要多少就定義多少。當然,有一些不會出現,因為它們遠在螢幕外,但是一旦當你進行滾動時,它們將呈現在眼前。
def platform(lvl,tx,ty):
plat_list = pygame.sprite.Group()
ploc = []
i=0
if lvl == 1:
ploc.append((200,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,'tile.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
要讓這些平臺出現在你的遊戲世界,它們必須出現在你的主迴圈中。如果你還沒有這樣做,新增這些行到你的主迴圈(再一次,第一行僅被用於上下文)中:
enemy_list.draw(world) # 重新整理敵人
ground_list.draw(world) # 重新整理地面
plat_list.draw(world) # 重新整理平臺
啟動你的遊戲,根據需要調整你的平臺的放置位置。如果你看不見螢幕外產生的平臺,不要擔心;你不久後就可以修復它。
到目前為止,這是遊戲的圖片和程式碼:
到目前為止,我們的 Pygame 平臺。
#!/usr/bin/env python3
# draw a world
# add a player and player control
# add player movement
# add enemy and basic collision
# add platform
# 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.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 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)
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
for g in ground_hit_list:
self.health -= 1
print(self.health)
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.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
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
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,lloc):
print(lvl)
def ground(lvl,gloc,tx,ty):
ground_list = pygame.sprite.Group()
i=0
if lvl == 1:
while i < len(gloc):
print("blockgen:" + str(i))
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
'''
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 # how fast to move
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
print("block: " + str(i))
enemy_list = Level.bad( 1, eloc )
ground_list = Level.ground( 1,gloc,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'):
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'):
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 == ord('q'):
pygame.quit()
sys.exit()
main = False
# world.fill(BLACK)
world.blit(backdrop, backdropbox)
player.update()
player_list.draw(world) #refresh player position
enemy_list.draw(world) # refresh enemies
ground_list.draw(world) # refresh enemies
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
(LCTT 譯註:到本文翻譯完為止,該系列已經近一年沒有繼續更新了~)
via: https://opensource.com/article/18/7/put-platforms-python-game
作者:Seth Kenlon 選題:lujun9972 譯者:robsean 校對:wxy
訂閱“Linux 中國”官方小程式來檢視
相關文章
- TAPTAP憑什麼在眾多遊戲平臺中脫穎而出APT遊戲
- python中pygame遊戲打包為exe檔案PythonGAM遊戲
- “反遊戲“的遊戲——放置遊戲研究遊戲
- pygame開發小遊戲GAM遊戲
- 平臺遊戲中走與跳的實現遊戲
- bEnable判斷點卡充值平臺是否在遊戲中,遮蔽任務點卡充值斷點遊戲
- 放置遊戲大成之作 — Realm Grinder遊戲
- 《Lunark》開發者Johan Vinet:在畫素平臺遊戲中融入電影鏡頭遊戲
- 印度遊戲平臺Dream11如何伸縮擴充套件他們的遊戲中臺?遊戲套件
- 中通大資料平臺在大促中的進化大資料
- GOG打造PC玩家遊戲聚合平臺 囊括所有平臺及主機遊戲Go遊戲
- 從零開始實現放置遊戲(三):後臺管理系統搭建遊戲
- 探索.NET平臺中的SIMD內在函式Vector函式
- 在Solaris平臺中ASM所需磁碟的Format過程ASMORM
- Valve:在Steam平臺發行遊戲的五個建議遊戲
- Python遊戲程式設計之旅(2):pygame中的IO、資料Python遊戲程式設計GAM
- 廣告在移動介面的放置
- 手遊折扣平臺 遊戲打折扣的平臺推薦遊戲
- 《火柴人覺醒》今日全平臺公測,放置卡牌拯救火柴人!
- 在我們的努力下,MOEGARN遊戲工作室在平臺著陸了。遊戲
- 大豬網網頁遊戲平臺網頁遊戲
- 遊戲平臺採集資料遊戲
- 從平臺到中臺 | Elasticsearch 在螞蟻金服的實踐經驗Elasticsearch
- 輕策略放置手遊《小小軍團2》國服雙平臺預約今日開啟!
- golang在windows平臺使用zmqGolangWindowsMQ
- 在AWS中部署OpenShift平臺
- 全鏈路壓測平臺(Quake)在美團中的實踐
- 十大變態遊戲平臺排行榜 玩變態遊戲哪個平臺好遊戲
- 放置等於棄置?放置類遊戲是否是市場下一個風口?遊戲
- “放置+IP”苗頭初現,它會是放置類遊戲的新方向嗎?遊戲
- 放置遊戲如何成爆款?我們在《最強蝸牛》中找到了答案遊戲
- 在開放平臺建立第三方平臺應用
- 如何搭建遊戲資料分析平臺遊戲
- 186wan網頁遊戲平臺網頁遊戲
- 遊戲:下一個超級平臺遊戲
- 七大放置遊戲機制遊戲
- 放置遊戲一統天下?AFK arena深度分析遊戲
- Google Play 放置遊戲產品型別分析Go遊戲型別