Python程式設計:從入門到實踐 專案《外星人入侵》完整程式碼
阿新 • • 發佈:2021-01-03
學習《Python程式設計:從入門到實踐》有段時間了,跟著書本把所有程式碼都敲了一遍,感悟很深,現在完成了《外星人入侵》專案,對於庫、類、函式、方法都有一定的理解,現在將該專案完整程式碼分享出來,以供學習。
1.首先當然是主執行檔案alien_invasion.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import pygame
from pygame.sprite import Group
from settings import Settings
from game_stats import GameStats
from scoreboard import Scoreboard
from button import Button
from ship import Ship
import game_functions as gf
def run_game():
# 初始化pygame、設定和螢幕物件
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption( "Alien Invasion" )
# 建立Play按鈕
play_button = Button(ai_settings, screen, "Play" )
# 建立儲存遊戲統計資訊的例項,並建立記分牌
stats = GameStats(ai_settings)
sb = Scoreboard(ai_settings, screen, stats)
#建立一艘飛船、一個子彈編組和一個外星人編組
ship = Ship(ai_settings,screen)
bullets = Group()
aliens = Group()
# 建立外星人群
gf.create_fleet(ai_settings,screen,ship,aliens)
# 開始遊戲的主迴圈
while True :
gf.check_events(ai_settings,screen,stats,sb,play_button,ship,
aliens,bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings,screen,stats,sb,ship,aliens,
bullets)
gf.update_aliens(ai_settings,stats,screen,sb,ship,aliens,bullets)
gf.update_screen(ai_settings,screen,stats,sb,ship,aliens,
bullets,play_button)
run_game()
|
2.遊戲相關引數設定settings.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
class Settings():
"""儲存《外星人入侵》的所有設定的類"""
def __init__( self ):
"""初始化遊戲的靜態設定"""
# 螢幕設定
self .screen_width = 800
self .screen_height = 600
self .bg_color = ( 230 , 230 , 230 )
# 飛船的設定
self .ship_speed_factor = 0.3
#子彈設定
self .bullet_speed_factor = 1
self .ship_limit = 3
self .bullet_width = 3
self .bullet_height = 15
self .bullet_color = 60 , 60 , 60
self .bullets_allowed = 3
# 外星人設定
self .alien_speed_factor = 0.1
self .fleet_drop_speed = 10
# 以什麼樣的速度加快遊戲節奏
self .speedup_scale = 1.01
# 外星人點數的提高速度
self .score_scale = 1.1
self .initialize_dynamic_settings()
# fleet_direction為1表示向右移,為-1表示向左移
self .fleet_direction = 1
def initialize_dynamic_settings( self ):
"""初始化隨遊戲進行而變化的設定"""
self .ship_speed_factor = 0.3
self .bullet_speed_factor = 1
self .alien_speed_factor = 0.1
# fleet_direction為1表示向右;為-1表示向左
self .fleet_direction = 1
# 記分
self .alien_points = 50
def increase_speed( self ):
"""提高速度設定和外星人點數"""
self .ship_speed_factor * = self .speedup_scale
self .bullet_speed_factor * = self .speedup_scale
self .alien_speed_factor * = self .speedup_scale
self .alien_points = int ( self .alien_points * self .score_scale)
|
3.按鍵及事件邏輯game_functions.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
import sys
from time import sleep
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_UP:
ship.moving_up = True
elif event.key = = pygame.K_DOWN:
ship.moving_down = True
elif event.key = = pygame.K_SPACE:
fire_bullet(ai_settings,screen,ship,bullets)
elif event.key = = pygame.K_q:
sys.exit()
def fire_bullet(ai_settings,screen,ship,bullets):
"""如果還沒有到達限制,就發射一顆子彈"""
# 建立一顆子彈,並將其加入到編組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
elif event.key = = pygame.K_UP:
ship.moving_up = False
elif event.key = = pygame.K_DOWN:
ship.moving_down = False
def check_events(ai_settings,screen,stats,sb,play_button,ship,aliens,
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)
elif event. type = = pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
check_play_button(ai_settings, screen, stats, sb, play_button, ship,
aliens, bullets, mouse_x, mouse_y)
def check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens,
bullets, mouse_x, mouse_y):
"""在玩家單擊Play按鈕時開始新遊戲"""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:
# 重置遊戲設定
ai_settings.initialize_dynamic_settings()
# 隱藏游標
pygame.mouse.set_visible( False )
# 重置遊戲統計資訊
stats.reset_stats()
stats.game_active = True
# 重置記分牌影象
sb.prep_score()
sb.prep_high_score()
sb.prep_level()
sb.prep_ships()
# 清空外星人列表和子彈列表
aliens.empty()
bullets.empty()
# 建立一群新的外星人,並讓飛船居中
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
def update_bullets(ai_settings,screen,stats,sb,ship,aliens,bullets):
"""更新子彈的位置,並刪除已消失的子彈"""
# 更新子彈的位置
bullets.update()
# 刪除已消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom < = 0 :
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets)
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets):
"""響應子彈和外星人的碰撞"""
# 刪除發生碰撞的子彈和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True , True )
if collisions:
for aliens in collisions.values():
stats.score + = ai_settings.alien_points * len (aliens)
sb.prep_score()
check_high_score(stats, sb)
if len (aliens) = = 0 :
# 如果整群外星人都被消滅,就提高一個等級
bullets.empty()
ai_settings.increase_speed()
# 提高等級
stats.level + = 1
sb.prep_level()
create_fleet(ai_settings, screen, ship, aliens)
def update_screen(ai_settings,screen,stats,sb,ship,aliens,bullets,
play_button):
"""更新螢幕上的影象,並切換到新螢幕"""
#每次迴圈時都重繪螢幕
screen.fill(ai_settings.bg_color)
# 在飛船和外星人後面重繪所有子彈
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# 顯示得分
sb.show_score()
# 如果遊戲處於非活動狀態,就繪製Play按鈕
if not stats.game_active:
play_button.draw_button()
# 讓最近繪製的螢幕可見
pygame.display.flip()
def get_number_aliens_x(ai_settings, alien_width):
"""計算每行可容納多少個外星人"""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int (available_space_x / ( 2 * alien_width))
return number_aliens_x
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 / ( 3 * alien_height))
return number_rows
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.y = alien.rect.height + 2 * alien.rect.height * row_number
alien.rect.x = alien.x
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 check_fleet_edges(ai_settings, aliens):
"""有外星人到達邊緣時採取相應的措施"""
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""將整群外星人下移,並改變它們的方向"""
for alien in aliens.sprites():
alien.rect.y + = ai_settings.fleet_drop_speed
ai_settings.fleet_direction * = - 1
def ship_hit(ai_settings, stats, screen, sb, ship, aliens, bullets):
"""響應被外星人撞到的飛船"""
if stats.ships_left > 0 :
# 將ships_left減1
stats.ships_left - = 1
# 更新記分牌
sb.prep_ships()
# 清空外星人列表和子彈列表
aliens.empty()
bullets.empty()
# 建立一群新的外星人,並將飛船放到螢幕底端中央
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
# 暫停
sleep( 0.5 )
else :
stats.game_active = False
pygame.mouse.set_visible( True )
def check_aliens_bottom(ai_settings, stats, screen, sb, ship, aliens, bullets):
"""檢查是否有外星人到達了螢幕底端"""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom > = screen_rect.bottom:
# 像飛船被撞到一樣進行處理
ship_hit(ai_settings, stats, screen, sb, ship, aliens, bullets)
break
def check_high_score(stats, sb):
"""檢查是否誕生了新的最高得分"""
if stats.score > stats.high_score:
stats.high_score = stats.score
sb.prep_high_score()
def update_aliens(ai_settings, stats, screen, sb, ship, aliens, bullets):
"""
檢查是否有外星人位於螢幕邊緣,並更新整群外星人的位置
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
# 檢測外星人和飛船之間的碰撞
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, stats, screen, sb, ship, aliens, bullets)
# 檢查是否有外星人到達螢幕底端
check_aliens_bottom(ai_settings, stats, screen, sb, ship, aliens, bullets)
|
4.計分相關設定scoreboard.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import pygame.font
from pygame.sprite import Group
from ship import Ship
class Scoreboard():
"""顯示得分資訊的類"""
def __init__( self , ai_settings, screen, stats):
"""初始化顯示得分涉及的屬性"""
self .screen = screen
self .screen_rect = screen.get_rect()
self .ai_settings = ai_settings
self .stats = stats
# 顯示得分資訊時使用的字型設定
self .text_color = ( 30 , 30 , 30 )
self .font = pygame.font.SysFont( None , 48 )
# 準備包含最高得分和當前得分的影象
self .prep_score()
self .prep_high_score()
self .prep_level()
self .prep_ships()
def prep_score( self ):
"""將得分轉換為一幅渲染的影象"""
rounded_score = int ( round ( self .stats.score, - 1 ))
score_str = "{:,}" . format (rounded_score)
self .score_image = self .font.render(score_str, True , self .text_color,
self .ai_settings.bg_color)
# 將得分放在螢幕右上角
self .score_rect = self .score_image.get_rect()
self .score_rect.right = self .screen_rect.right - 20
self .score_rect.top = 20
def prep_high_score( self ):
"""將最高得分轉換為渲染的影象"""
high_score = int ( round ( self .stats.high_score, - 1 ))
high_score_str = "{:,}" . format (high_score)
self .high_score_image = self .font.render(high_score_str, True ,
self .text_color, self .ai_settings.bg_color)
#將最高得分放在螢幕頂部中央
self .high_score_rect = self .high_score_image.get_rect()
self .high_score_rect.centerx = self .screen_rect.centerx
self .high_score_rect.top = self .score_rect.top
def prep_level( self ):
"""將等級轉換為渲染的影象"""
self .level_image = self .font.render( str ( self .stats.level), True ,
self .text_color, self .ai_settings.bg_color)
# 將等級放在得分下方
self .level_rect = self .level_image.get_rect()
self .level_rect.right = self .score_rect.right
self .level_rect.top = self .score_rect.bottom + 10
def prep_ships( self ):
"""顯示還餘下多少艘飛船"""
self .ships = Group()
for ship_number in range ( self .stats.ships_left):
ship = Ship( self .ai_settings, self .screen)
ship.rect.x = 10 + ship_number * ship.rect.width
ship.rect.y = 10
self .ships.add(ship)
def show_score( self ):
"""在螢幕上顯示飛船和得分"""
self .screen.blit( self .score_image, self .score_rect)
self .screen.blit( self .high_score_image, self .high_score_rect)
self .screen.blit( self .level_image, self .level_rect)
# 繪製飛船
self .ships.draw( self .screen)
|
5.跟蹤遊戲統計資訊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class GameStats():
"""跟蹤遊戲的統計資訊"""
def __init__( self , ai_settings):
"""初始化統計資訊"""
self .ai_settings = ai_settings
self .reset_stats()
# 遊戲剛啟動時處於活動狀態
self .game_active = False
# 在任何情況下都不應重置最高得分
self .high_score = 0
def reset_stats( self ):
"""初始化在遊戲執行期間可能變化的統計資訊"""
self .ships_left = self .ai_settings.ship_limit
self .score = 0
self .level = 1
|
6.“遊戲開始”按鍵設定button.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import pygame.font
class Button():
def __init__( self , ai_settings, screen, msg):
"""初始化按鈕的屬性"""
self .screen = screen
self .screen_rect = screen.get_rect()
# 設定按鈕的尺寸和其他屬性
self .width, self .height = 200 , 50
self .button_color = ( 0 , 255 , 0 )
self .text_color = ( 255 , 255 , 255 )
self .font = pygame.font.SysFont( 'arial' , 48 )
# 建立按鈕的rect物件,並使其居中
self .rect = pygame.Rect( 0 , 0 , self .width, self .height)
self .rect.center = self .screen_rect.center
# 按鈕的標籤只需建立一次
self .prep_msg(msg)
def prep_msg( self , msg):
"""將msg渲染為影象,並使其在按鈕上居中"""
self .msg_image = self .font.render(msg, True , self .text_color,
self .button_color)
self .msg_image_rect = self .msg_image.get_rect()
self .msg_image_rect.center = self .rect.center
def draw_button( self ):
# 繪製一個用顏色填充的按鈕,再繪製文字
self .screen.fill( self .button_color, self .rect)
self .screen.blit( self .msg_image, self .msg_image_rect)
|
7.飛船相關設定ship.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
def __init__( self , ai_settings,screen):
"""初始化飛船並設定其初始位置"""
super (Ship, self ).__init__()
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 .centery = float ( self .rect.centery)
# 移動標誌
self .moving_right = False
self .moving_left = False
self .moving_up = False
self .moving_down = False
def update( self ):
"""根據移動標誌調整飛船的位置"""
# 更新飛船的center值,而不是rect
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 > 0 :
self .center - = self .ai_settings.ship_speed_factor
if self .moving_up and self .rect.top > 0 :
self .centery - = self .ai_settings.ship_speed_factor
if self .moving_down and self .rect.bottom < self .screen_rect.bottom:
self .centery + = self .ai_settings.ship_speed_factor
# 根據self.center更新rect物件
self .rect.centerx = self .center
self .rect.centery = self .centery
def center_ship( self ):
"""讓飛船在螢幕上居中"""
self .center = self .screen_rect.centerx
self .centery = self .screen_rect.bottom
def blitme( self ):
"""在指定位置繪製飛船"""
self .screen.blit( self .image, self .rect)
|
8.外星人相關設定alien.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""表示單個外星人的類"""
def __init__( self , ai_settings, screen):
"""初始化外星人並設定其起始位置"""
super (Alien, self ).__init__()
self .screen = screen
self .ai_settings = ai_settings
# 載入外星人影象,並設定其rect屬性
self .image = pygame.image.load( 'images/alien.bmp' )
self .rect = self .image.get_rect()
# 每個外星人最初都在螢幕左上角附近
self .rect.x = self .rect.width
self .rect.y = self .rect.height
# 儲存外星人的準確位置
self .x = float ( self .rect.x)
def blitme( self ):
"""在指定位置繪製外星人"""
self .screen.blit( self .image, self .rect)
def check_edges( self ):
"""如果外星人位於螢幕邊緣,就返回True"""
screen_rect = self .screen.get_rect()
if self .rect.right > = screen_rect.right:
return True
elif self .rect.left < = 0 :
return True
def update( self ):
"""向左或右移動外星人"""
self .x + = ( self .ai_settings.alien_speed_factor *
self .ai_settings.fleet_direction)
self .rect.x = self .x
|
9.子彈相關設定bullet.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一個對飛船發射的子彈進行管理的類"""
def __init__( self , ai_settings, screen, ship):
"""在飛船所處的位置建立一個子彈物件"""
super (Bullet, self ).__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)
|
關於ship.py檔案中
self.image = pygame.image.load(‘images/ship.bmp’)
和alien.py檔案中
self.image = pygame.image.load(‘images/alien.bmp’)
程式碼裡的圖片,需要各位自行在根目錄下新建資料夾和影象
另外,自己在除錯專案的過程中,常常有報錯,其中主要錯誤的反而是忘了下劃線、冒號還有程式碼縮排,程式碼縮排要麼完全使用空格,要麼全都使用tab進行縮排,python在識別縮排上非常嚴格。
寫給自己,也分享給大家。