Python 項目實踐一(外星人入侵小游戲)第三篇


今天是聖誕節,公司放假一天,趁着有空,學習了一下午,多寫一篇博客吧!

接着上節的繼續學習,

一 重構:模塊game_functions

在大型項目中,經常需要在添加新代碼前重構既有代碼。重構旨在簡化既有代碼的結構,使其更容易擴展。在本節中,我們將創建一個名為game_functions的新模塊,它將存儲大量讓游戲《外星人入侵》運行的函數。通過創建模塊game_functions,可避免alien_invasion.py太長,並使其邏輯更容易理解。

1 函數check_events()

將check_events()放在一個名為game_functions的模塊中,在該函數主要是管理事件的功能,通過隔離事件循環,可將事件管理與游戲的其他方面(如更新屏幕)分離。通

#game_functions.py
import sys
import pygame
def check_events():
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
        sys.exit()            

2 函數update_screen()

為進一步簡化run_game(),下面將更新屏幕的代碼移到一個名為update_screen()的函數中,並將這個函數放在模塊game_functions.py中:

#game_functions.py
    --snip--
def check_events():
    --snip--
def update_screen(ai_settings, screen, ship):
    """更新屏幕上的圖像,並切換到新屏幕"""
    # 每次循環時都重繪屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # 讓最近繪制的屏幕可見
    pygame.display.flip()

二 駕駛飛船

下面來讓玩家能夠左右移動飛船:

1 相應按鍵

每當用戶按鍵時,都將在Pygame中注冊一個事件。事件都是通過方法pygame.event.get()獲取的,因此在函數check_events()中,我們需要指定要檢查哪些類型的事件。每次按鍵都被注冊為一個KEYDOWN事件。

檢測到KEYDOWN事件時,我們需要檢查按下的是否是特定的鍵。例如,如果按下的是右箭頭鍵,我們就增大飛船的rect.centerx值,將飛船向右移動:

 #game_ functions.py
def check_events(ship):
    """響應按鍵和鼠標事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
     elif event.type == pygame.KEYDOWN:
         if event.key == pygame.K_RIGHT:
                #向右移動飛船
             ship.rect.centerx += 1        

2 允許不斷移動

玩家按住右箭頭鍵不放時,我們希望飛船不斷地向右移動,直到玩家松開為止。檢測pygame.KEYUP事件,以便玩家松開右箭頭鍵時我們能夠知道這一點;然后,我們將結合使用KEYDOWN和KEYUP事件,以及一個名為moving_right的標志來實現持續移動。

飛船不動時,標志moving_right將為False。玩家按下右箭頭鍵時,我們將這個標志設置為True;而玩家松開時,我們將這個標志重新設置為False。代碼見下面:

3 調整飛船的速度

當前,每次執行while循環時,飛船最多移動1像素,但我們可以在Settings類中添加屬性ship_speed_factor,用於控制飛船的速度。我們將根據這個屬性決定飛船在每次循環時最多移動多少距離。下面演示了如何在settings.py中添加這個新屬性:

4 限制飛船的活動范圍

當前,如果玩家按住箭頭鍵的時間足夠長,飛船將移到屏幕外面,消失得無影無蹤。下面來修復這種問題,讓飛船到達屏幕邊緣后停止移動。為此,我們將修改Ship類的方法update():

import pygame

class Ship():
    def __init__(self, ai_settings,screen):
        """初始化飛船並設置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings
        # 加載飛船圖像並獲取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        # 將每艘新飛船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

        #將飛船的屬性center中存儲小數值
        self.center=float(self.rect.centerx)

        #移動標志
        self.moving_right = False
        self.moving_left = False
    def update(self) :
        #根據移動標志調整飛船的位置
        if self.moving_right and self.rect.right < self.screen_rect.right :
            self.center +=self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > self.screen_rect.left :
            self.center -=self.ai_settings.ship_speed_factor
        self.rect.centerx = self.center
    def blitme(self):
        """在指定位置繪制飛船"""
        self.screen.blit(self.image, self.rect)

三 簡單回顧

1 alien_invasion.py

主文件alien_invasion.py創建一系列整個游戲都要用到的對象:存儲在ai_settings中的設置、存儲在screen中的主顯示surface以及一個飛船實例。文件alien_invasion.py還包含游戲的主循環,這是一個調用check_events()、ship.update()和update_screen()的while循環。

2 settings.py

文件settings.py包含Settings類,這個類只包含方法__init__(),它初始化控制游戲外觀和飛船速度的屬性。

3 game_functions.py

文件game_functions.py包含一系列函數,游戲的大部分工作都是由它們完成的。函數check_events()檢測相關的事件,如按鍵和松開,並使用輔助函數check_keydown_events()和check_keyup_events() 來處理這些事件。就目前而言, 這些函數管理飛船的移動。模塊game_functions還包含函數update_screen(),它用於在每次執行主循環時都重繪屏幕。

4 ship.py

文件ship.py包含Ship類,這個類包含方法__init__()、管理飛船位置的方法update()以及在屏幕上繪制飛船的方法blitme()。表示飛船的圖像存儲在文件夾images下的文件ship.bmp中。

四 射擊子彈

下面來添加射擊功能。我們將編寫玩家按空格鍵時發射子彈(小矩形)的代碼。子彈將在屏幕中向上穿行,抵達屏幕上邊緣后消失。

1 添加子彈設置

首先,更新settings.py,在其方法__init__()末尾存儲新類Bullet所需的值:

class Settings():
    '''存儲《外星人入侵》的所有設置的類'''

    def __init__(self):
        '''初始化游戲的設置'''
        self.screen_width=1200
        self.screen_height=800
        self.bg_color = (230,230,230)
        self.ship_speed_factor =1.5
        #子彈的設置
        self.bullet_speed_factor = 1
        self.bullet_width =10
        self.bullet_height =15
        self.bullet_color =60,60,60

2 創建Bullet類

下面來創建存儲Bullet類的文件bullet.py,Bullet類繼承了我們從模塊pygame.sprite中導入的Sprite類。通過使用精靈,可將游戲中相關的元素編組,進而同時操作編組中的所有元素。為創建子彈實例,需要向__init__()傳遞i_settings、screen和ship實例,還調用了super()來繼承Sprite。我們創建了子彈的屬性rect。子彈並非基於圖像的,因此我們必須使用pygame.Rect()類從空白開始創建一個矩形。創建這個類的實例時,必須提供矩形左上角的x坐標和y坐標,還有矩形的寬度和高度。方法update()管理子彈的位置。發射出去后,子彈在屏幕中向上移動,這意味着y坐標將不斷減小,因此為更新子彈的位置,子彈發射后,其x坐標始終不變,因此子彈將沿直線垂直地往上穿行。需要繪制子彈時,我們調用draw_bullet()。函數draw.rect()使用存儲在self.color中的顏色填充表示子彈的rect占據的屏幕部分。代碼如下:

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite) :
    def __init__(self,ai_settings,screen,ship) :
        # 在飛船所處的位置創建一個子彈對象
        super().__init__()
        self.screen=screen

        #在(0,0)處創建一個表示子彈的矩形,再設置正確的位置
        self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)
        self.rect.centerx=ship.rect.centerx
        self.rect.top = ship.rect.top

        #存儲用小數表示子彈的位置
        self.y=float(self.rect.y)
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

    def update(self) :
        #向上移動子彈,更新表示子彈位置的小數值
        self.y -= self.speed_factor
        #更新表示子彈的rect的位置
        self.rect.y = self.y

    def draw_bullet(self) :
        #在屏幕上繪制子彈
        pygame.draw.rect(self.screen,self.color,self.rect)

3 將子彈存儲到編組中

定義Bullet類和必要的設置后,就可以編寫代碼了,在玩家每次按空格鍵時都射出一發子彈。首先,我們將在alien_invasion.py中創建一個編組(group),用於存儲所有有效的子彈,以便能夠管理發射出去的所有子彈。這個編組將是pygame.sprite.Group類的一個實例;pygame.sprite.Group類類似於列表,但提供了有助於開發游戲的額外功能。在主循環中,我們將使用這個編組在屏幕上繪制子彈,以及更新每顆子彈的位置:

import sys
from settings import Settings
from ship import Ship
import game_functions as gf
import pygame
from pygame.sprite import Group

def run_game():
    # 初始化游戲並建立一個屏幕對象
    pygame.init()
    # screen = pygame.display.set_mode((1200,800))
    ai_settings=Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    # 創建一艘飛船
    ship = Ship(ai_settings,screen)

    #創建一個用於存儲子彈的group
    bullets =Group()
    
    #開始游戲的主循環
    while True:
        # 監視鍵盤和鼠標事件
        gf.check_events(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()
        bullets.update()
        gf.update_screen(ai_settings,screen,ship,bullets)
run_game()

  

4 開火

在game_functions.py中,我們需要修改check_keydown_events(),以便在玩家按空格鍵時發射一顆子彈。我們無需修改check_keyup_events(),因為玩家松開空格鍵時什么都不會發生。我們還需修改update_screen(),確保在調用flip()前在屏幕上重繪每顆子彈。下面是對game_functions.py所做的相關修改:

import sys
from bullet import Bullet
import pygame

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 :
        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)
            #check_keydown_events(event,ship)
        elif event.type == pygame.KEYUP :
            check_keyup_events(event,ship)

def update_screen(ai_settings,screen,ship,bullets) :
    """更新屏幕上的圖像,並切換到新屏幕"""
    # 每次循環時都重繪屏幕
    screen.fill(ai_settings.bg_color)
    #在飛船和外星人后面重新繪制所有子彈
    for bullet in bullets.sprites() :
        bullet.draw_bullet()
    ship.blitme()
    # 讓最近繪制的屏幕可見
    pygame.display.flip()

 先寫到這里吧,不知不覺天已經黑了,太耗時了,要去做飯了,對了最后的效果如下所示(為了截圖我把子彈的速度調的非常慢,所以看起來有點怪):

昨天寫的了,忘了發布了,今天發布下!

 

 

 

 

  

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM