前言
??我們上篇文章初步給大家介紹了外星人的需求分析,以及創建了第一個外星人,包括創建Alien類以及Alien實體,最后將我們創建的外星人出現在螢屏上,本文給大家接著介紹多個外星人的實作,
創建一群外星人
??要繪制一群外星人,需要確定一行能容納多少個外星人以及要繪制多少行外星人,我們將先計算外星人之間的水平距離,并創建一行外星人,并創建整群外星人,
1、確定一行可容納外星人的數量
??為確定一行可容納外星人的個數,我們需要看看可用的水平空間有多大,螢屏寬度存盤在ai_settings.screen_width中,但需要在螢屏兩邊都留下一定的邊距,把它設定為外星人的寬度,由于有兩個邊距,因此可用于放置外星人的水平空間為螢屏寬度減去外星人寬度的兩倍,具體計算如下:
avaliable_space_x = ai_settings.screen_width - (2 * alien_width)
??我們還需要在外星人之間流出一定的空間,即外星人寬度,因此,顯示一個外星人所需的水平空間為外星人寬度的兩倍;一個寬度為用于放置外星人,另一個寬度為外星人右邊的空白區域,為確定一行可容納多少個外星人,我們將可用空間除以外星人寬度的兩倍:
numner_aliens_x = avaliable_space_x / (2 * alien_width)
??我們將在創建外星人群時使用這些公式,這里需要我們注意的是:其實我們在程式開始執行的時候無需確定公式的正確性,而可以嘗試直接運行程式,看看結果是否符合預期,即便是在最糟糕的情況下,也只是螢屏上顯示的外星人太多或太少,我們可以根據在螢屏上看到的情況對公式進行相應的調整,
2、創建多行外星人
??為創建一行外星人,首先在alien_invasion.py中創建一個名為alines的空編組,用于存盤全部的外星人,再呼叫game_funcattion.py中創建外星人群的函式:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
from alien import Alien
def run_game():
# 初始化游戲并創建一個螢屏物件
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alian Invasion")
# 創建一艘飛船、一個子彈編組和一個外星人編組
ship = Ship(ai_settings, screen)
# 創建一組一個用于存盤子彈的編組
bullets = Group()
aliens = Group()
# 創建外q星人群
# alien = Alien(ai_settings, screen)
gf.create_fleet(ai_settings, screen, aliens)
# 開始游戲的主回圈
while True:
# 監視鍵盤和滑鼠事件
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
??由于我們不再在alien_invasion.py中直接創建外星人,因此無需在這個檔案中匯入Alien類,
??首先我們創建了一個空編組,用于存盤所有的外星人,接下里,呼叫稍后將撰寫的函式create_fleet(),并將ai_settings、物件screen和空編組aliens傳遞給它,然后,修改對update_screen()的呼叫,讓它能夠訪問外星人編組,
??當然我們還需要修改ganme_function.py中的update_screen(),并且我們還需要實作新函式create_fleet(),這個時候,我們就需要匯入Alien類:
import sys
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""回應按鍵"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
# 創建一顆子彈,并將其加入到編組bullets中
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
"""如果還沒有到達限制,就發射一顆子彈"""
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""回應松開"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""回應按鍵和滑鼠事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""更新螢屏上的影像,并切換到新螢屏"""
# 每次回圈時都重繪螢屏
screen.fill(ai_settings.bg_color)
# 在飛船和外星人后面重繪所有子彈
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# 讓最近繪制的螢屏可見
pygame.display.flip()
def update_bullets(bullets):
"""更新子彈的位置,并洗掉已消失的子彈"""
# 更新子彈的位置
bullets.update()
# 洗掉已消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def create_fleet(ai_settings, screen, aliens):
"""創建外星人群"""
# 創建一個外星人,并計算一行可容納多少個外星人
# 外星人間距為外星人寬度
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
avaliable_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(avaliable_space_x / (2 * alien_width))
# 創建第一行外星人
for alien_number in range(number_aliens_x):
# 創建一個外星人并將其加入當前行
alien = Alien(ai_settings, screen)
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
aliens.add(alien)
??這些代碼在前面其實我們都給大家詳細介紹過,我們對編組呼叫draw()時,pygame自動繪制了編組的每個元素,繪制位置由元素的屬性rect決定,在這里,aliens.draw(screen)在螢屏上繪制編組中的每個外星人,
??我們為了放置外星人,需要知道外星人的寬度和高度,因此在執行計算前,我們先創建一個外星人,這個外星人不是外星人群的成員,因此,沒有將它加入到編組aliens中,另外,我們從外星人的rect屬性中獲取外星人的寬度,并將這個值存盤到alien_width中,以免反復訪問屬性rect,最后,我們計算可用于放置外星人的水平空間,以及其中可容納多少個外星人,
??相比于前面介紹的作業,這里唯一的不同是使用了int來確保計算得到的外星人數量為整數,因為我們不希望某個外星人只顯示一部分,而且函式range()也需要一個整數,函式int()將小數部分丟棄,相當于向下取整,其實這樣做最大的好處就是可以流出一些空間,而不是擠滿了外星人,看起來很少不好看,
??最后,我們撰寫一個回圈,它從零數到要創建的外星人數,在這個回圈的主題中,我們創建一個新的外星人,并通過設定x坐標將其加入當前行,將每個外星人都往右推出一個外星人的寬度,接下來,我們將外星人寬度乘以2,得到每個外星人占據的空間,這里當然也包含其空白的區域,再據此計算當前外星人在當前的位置,最后,我們將每個新創建的外星人添加到編組aliens中,
??我們將目前代碼運行,其效果如下:

??這一行外星人在螢屏上稍微偏向了左邊,這其實是比較好的,因為我們將讓外星人群往右移,觸及螢屏邊緣后稍微往下移,然后往左移,以此類推,我們將讓外星人群不斷這樣移動,直到所有外星人都被擊落或有外星人撞上飛船或抵達螢屏底端,這里還需要我們特別注意的是:根據讀者自己選擇的螢屏寬度以及在你的系統中,第一行外星人的位置以及外星人個數可能不一樣,
3、重構create_fleet()
??倘若我們創建了外星人群,也許應讓create_fleet()保持原樣,但鑒于創建外星人的作業還未完成,我們稍微整理一下這個函式,下面是create_fleet()和兩個新函式:get_number_aliens_x()和create_alien():
import sys
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""回應按鍵"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
# 創建一顆子彈,并將其加入到編組bullets中
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
"""如果還沒有到達限制,就發射一顆子彈"""
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""回應松開"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""回應按鍵和滑鼠事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""更新螢屏上的影像,并切換到新螢屏"""
# 每次回圈時都重繪螢屏
screen.fill(ai_settings.bg_color)
# 在飛船和外星人后面重繪所有子彈
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# 讓最近繪制的螢屏可見
pygame.display.flip()
def update_bullets(bullets):
"""更新子彈的位置,并洗掉已消失的子彈"""
# 更新子彈的位置
bullets.update()
# 洗掉已消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def get_number_aliens_x(ai_settings, alien_width):
avaliable_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(avaliable_space_x / (2 * alien_width))
return number_aliens_x
def create_alien(ai_settings, screen, aliens, alien_number):
# 創建一個外星人,并計算一行可容納多少個外星人
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
aliens.add(alien)
def create_fleet(ai_settings, screen, aliens):
"""創建外星人群"""
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
# 創建第一行外星人
for alien_number in range(number_aliens_x):
# 創建一個外星人并將其加入當前行
create_alien(ai_settings, screen, aliens, alien_number)
??函式get_number_aliens_x()的代碼都來自create_fleet(),且未做任何修改,函式create_alien()的代碼也都是來自create_fleet(),且未做任何修改,只是使用剛創建的外星人來獲取外星人的寬度,我們將計算可用水平空間的代碼替換為對get_number_aliens_x()的呼叫,并洗掉了參考alien_width的代碼行,因為現在這是在create_alien()中處理的,最后,我們呼叫create_alien(),通過這樣的重構,將添加新行進而創建整群外星人就更容易了,
4、添加行
??要創建外星人群,需要設計螢屏可容納多少行,并對創建一行外星人的回圈重復相應的次數,為計算可容納的行數,我們這樣計算可用垂直空間:將螢屏高度減去第一行外星人的上邊距(外星人的高度)、飛船的高度以及最初外星人高度加上外星人間距(外星人高度的兩倍):
avaliable_space_y = ai_settings.screen_height - 3 * alien_height - ship_height
??這將在飛船上方留出一定空白的區域,給玩家留出射殺外星人的時間,
??每行下方都要留出一定的空白區域,并將其設定為外星人的高度,為計算可容納的行數,我們將可用垂直空間除以外星人高度的兩倍(同樣,如果這樣計算不對,我們馬上就能發現,繼而將間距調整為合理的值),
number_rows = available_space_y / (2 * alien_height)
??知道可容納的行數后,便可重復執行創建一行外星人的代碼:
import sys
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""回應按鍵"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
# 創建一顆子彈,并將其加入到編組bullets中
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
"""如果還沒有到達限制,就發射一顆子彈"""
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""回應松開"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
"""回應按鍵和滑鼠事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""更新螢屏上的影像,并切換到新螢屏"""
# 每次回圈時都重繪螢屏
screen.fill(ai_settings.bg_color)
# 在飛船和外星人后面重繪所有子彈
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# 讓最近繪制的螢屏可見
pygame.display.flip()
def update_bullets(bullets):
"""更新子彈的位置,并洗掉已消失的子彈"""
# 更新子彈的位置
bullets.update()
# 洗掉已消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def get_number_aliens_x(ai_settings, alien_width):
avaliable_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(avaliable_space_x / (2 * alien_width))
return number_aliens_x
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
# 創建一個外星人,并計算一行可容納多少個外星人
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def create_fleet(ai_settings, screen, ship, aliens):
"""創建外星人群"""
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
# 創建第一行外星人
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
# 創建一個外星人并將其加入當前行
create_alien(ai_settings, screen, aliens, alien_number, row_number)
def get_number_rows(ai_settings, ship_height, alien_height):
"""計算螢屏可容納多少行外星人"""
available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
??為計算螢屏可容納多少行外星人,我們在函式get_number_rows()實作了前面計算available_space_y和number_rows的公式,這個函式與get_number_aliens_x()類似,計算公式要用括號括起來,這樣可將代碼分成兩行,以遵循每行不超過79字符的建議,這里使用了int(),因為我們不想創建不完整的外星人行,
??為創建多行,我們使用兩個嵌套在一起的回圈:一個外部回圈一個內部回圈,其中的內部回圈創建一行外星人,而外部回圈從零數到要創建的外星人行數,Python將重復執行創建單行外星人代碼,重復次數為number_rows,
??為嵌套回圈,我們撰寫了一個新的for回圈,并縮進了要重復執行的代碼,另外又呼叫create_alien()時,傳遞了一個表示行號的實參,將每行都沿螢屏依次向下放置,
??create_alien()的定義需要一個用于存盤行號的實參,在create_alien()中,我們修改外星人的y坐標,并在第一行外星人上方留出與外星人等高的空白區域,相鄰外星人的y坐標差外星人高度的兩倍,因此,我們將外星人高度乘以2,再乘以行號,第一行的行號為0,因此,第一行的垂直位置不變,而其他行都沿螢屏依次向下放置,
??在create_fleet()的定義中,還新增了一個用于存盤ship物件的形參,因此,在alien_invasion.py中呼叫create_fleet()時,需要傳遞實參ship:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
from alien import Alien
def run_game():
# 初始化游戲并創建一個螢屏物件
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alian Invasion")
# 創建一艘飛船、一個子彈編組和一個外星人編組
ship = Ship(ai_settings, screen)
# 創建一組一個用于存盤子彈的編組
bullets = Group()
aliens = Group()
# 創建外星人群
# alien = Alien(ai_settings, screen)
gf.create_fleet(ai_settings, screen, ship, aliens)
# 開始游戲的主回圈
while True:
# 監視鍵盤和滑鼠事件
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
??具體運行的效果如下:

總結
??我們上篇文章初步給大家介紹了外星人的需求分析,以及創建了第一個外星人,包括創建Alien類以及Alien實體,最后將我們創建的外星人出現在螢屏上,本文給大家介紹多個外星人的實作,包括確定一行可容納外星人的數量、創建多行外星人以及代碼的重構和添加多行外星人,為了讓大家更好的吸收專案所用到的知識點,我們每一篇文章只給大家實作《外星人入侵》的一個功能,所以,希望大家能夠仔細閱讀,認真跟著寫代碼,理解其中的深入含義,吧這個專案的價值發揮到最大,其實這個專案已經很典型,代碼到處都是,但是,如果你只是簡單的粘貼復制,對你知識的學習沒有任何的價值,你還是得跟著過一遍,然后要知道每行代碼的含義或者是用到了前面我們介紹的哪一塊知識點,只有這樣,這個專案才會發揮不一樣的價值,希望大家認真學習,把基礎知識打扎實一點,Python是一門注重實際操作的語言,它是眾多編程語言中最簡單,也是最好入門的,當你把這門語言學會了,再去學習java、go以及C語言就比較簡單了,當然,Python也是一門熱門語言,對于人工智能的實作有著很大的幫助,因此,值得大家花時間去學習,生命不息,奮斗不止,我們每天努力,好好學習,不斷提高自己的能力,相信自己一定會學有所獲,加油!!!臨近中秋,提前祝大家中秋快樂!闔家歡樂!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301612.html
標籤:其他
上一篇:深度強化學習入門介紹
