偷學Python之最後的項目二:飛機大戰小遊戲(純代碼)
古之立大事者,不惟有超世之才,亦必有堅忍不拔之志。——蘇軾
甜甜先說
這次用Python中的pygame
模塊來完成一個飛機大戰的小遊戲;基本思路是通過方向鍵來控制飛機的左右移動射擊飛船。
成品效果
此貼爲這個小遊戲的完整代碼,帶有完整的註釋,另外一個貼中有完整的流程。傳送門
安裝pygame
要完成這個項目肯定要安裝pygame
第三方庫,安裝流程如下
-
首先通過命令行工具檢測系統是否安裝的
pip
工具python -m pip --version
小甜是Windows系統,這裏只提供Windows系統的檢測方法
-
如果未安裝則安裝
pip
工具,安裝則請跳過這一步python get-pip.py
安裝完畢以後退回第一步重新檢測
-
安裝
pygame
python -m pip install pygame --user
或者通過
pycharm
安裝第三個庫,流程如下 -
通過
import
關鍵字導入第三方庫import pygame
小飛機類
文件名爲plane.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""
class GameStats:
"""跟蹤遊戲統計信息"""
def __init__(self, setting):
self.setting = setting
self.reset_stats()
# 遊戲默認爲不活動狀態
self.game_active = False
# 最高得分
self.high_score = 0
def reset_stats(self):
# 初始化在遊戲運行期間可能變化的統計信息
self.planes_left = self.setting.plane_limit
# 統計得分
self.score = 0
子彈類
文件名爲bullet.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite): # 繼承pygame.sprite中的Sprite類
"""子彈的管理"""
def __init__(self, setting, screen, plane):
super().__init__()
self.screen = screen
# 在(0,0)處創建一個表示子彈的矩形
# pygame.Rect
# 用於存儲直角座標的pygame對象
self.rect = pygame.Rect(0,0, setting.bullet_width, setting.bullet_height)
# 設置顯示的位置
self.rect.centerx = plane.rect.centerx
self.rect.top = plane.rect.top
# 讓子彈的位置跟小飛機重疊,當子彈飛出了以後,就顯得跟從小飛機裏面射出來一樣
# 將子彈的座標轉換爲浮點數
self.y = float(self.rect.y)
# 子彈的顏色
self.color = setting.bullet_color
# 子彈的速度
self.speed = setting.bullet_speed
def update(self):
# 向上移動子彈
self.y -= self.speed
# 根據self.y的值更新self.rect.y
self.rect.y = self.y
def draw_bullet(self):
"""繪製子彈"""
# pygame.draw.rect()畫一個矩形的形狀
pygame.draw.rect(self.screen, self.color, self.rect)
敵人類(飛船)
文件名爲spaceship.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""
import pygame
from pygame.sprite import Sprite
class Spaceship(Sprite):
'''表示飛船的類'''
def __init__(self, setting, screen):
super().__init__()
self.screen = screen
self.setting = setting
# 添加飛船圖像
self.img = pygame.image.load("./imgs/enemy.png")
# 獲取rect屬性
self.rect = self.img.get_rect()
# 每個飛船最初都在屏幕左上角附近
self.rect.x = self.rect.width # 飛船圖像的左邊距等於圖像的寬度
self.rect.y = self.rect.height # 飛船圖書的上邊距等於圖像的高度
self.rect.w = self.rect.width
self.rect.h = int(self.rect.height / 2) # 將高度設置爲一半
self.x = float(self.rect.x)
def blitme(self):
# 繪製飛船圖像
self.screen.blit(self.img, 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.setting.spaceship_speed * self.setting.fleet_direction)
self.rect.x = self.x
按鈕類
文件名爲button.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/5
"""
import pygame.font
class Button:
def __init__(self, setting, screen, msg):
"""初始化按鈕的屬性"""
self.screen = screen
self.screen_rect = screen.get_rect()
# 設置按鈕的大小
self.width = 180
self.height = 50
# 設置按鈕的顏色
self.button_color = (0, 255, 0)
# 設置文本的顏色
self.text_color = (200, 200, 200)
# 設置字體的大小
self.font = pygame.font.SysFont("SimHei", 50) # 字體爲黑體大小爲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渲染爲圖像
"""
font.render方法是將msg中的文本轉換爲圖像
* 參數True是開啓抗鋸齒模式
* self.text_color是文本的顏色
* self.button_color是背景顏色
"""
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) # 繪製文本
記錄遊戲信息
文件名爲game_stats.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""
class GameStats:
"""跟蹤遊戲統計信息"""
def __init__(self, setting):
self.setting = setting
self.reset_stats()
# 遊戲默認爲不活動狀態
self.game_active = False
# 最高得分
self.high_score = 0
def reset_stats(self):
# 初始化在遊戲運行期間可能變化的統計信息
self.planes_left = self.setting.plane_limit
# 統計得分
self.score = 0
記分類
文件名爲scoreboard.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/5
"""
import pygame.font
from pygame.sprite import Group
from plane import Plane
class Scoreboard:
"""顯示得分信息的類"""
def __init__(self, setting, screen, stats):
"""初始化得分涉及到的屬性"""
self.screen = screen
self.screen_rect = screen.get_rect()
self.stats = stats
self.setting = setting
# 顯示得分的字體設置
# 設置文本的顏色
self.text_color = (250, 250, 250)
# 設置字體的大小
self.font = pygame.font.SysFont("SimHei", 40) # 字體爲黑體大小爲40像素
# 初始化得分圖像
self.prep_score()
# 初始化最高分圖像
self.prep_high_score()
def prep_score(self):
"""將得分轉換爲圖像"""
score_str = str(self.stats.score)
self.score_image = self.font.render(score_str, True, self.text_color)
# 將得分放到屏幕的右上角
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20 # 與右邊差20 像素
self.score_rect.top = 20 # 與頂部差20像素
def prep_high_score(self):
"""將得分轉換爲圖像"""
high_score_str = str(self.stats.high_score)
self.high_score_image = self.font.render(high_score_str, True, self.text_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 = 10
def show_score(self):
"""在屏幕上顯示得分"""
self.screen.blit(self.score_image, self.score_rect)
# 顯示最高分
self.screen.blit(self.high_score_image, self.high_score_rect)
遊戲設置類
文件名爲settings.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
import random
class Settings:
"""存儲飛機大戰的所有設置"""
def __init__(self):
# 屏幕設置
self.screen_width = 1000
self.screen_height = 600
self.bg_img = pygame.image.load("./imgs/bg_img.png")
#小飛機設置
# 小飛機的生命限制
self.plane_limit = 3
# 子彈的設置
self.bullet_width = 3 # 子彈的寬
self.bullet_height = 15 # 子彈的高
self.bullet_color = 190, 190, 190 # 子彈的顏色
# 限制子彈的數量
self.bullet_allowed = 5
# 飛船下降的速度
self.fleet_drop_speed = 10
# 以什麼樣的速度加快遊戲節奏
self.speedup_scale = 1.2
# 提高分數的速度
self.score_scale = 1.5
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
#小飛機設置
# 小飛機的移動速度
self.plane_speed = 2.5
# 子彈的設置
self.bullet_speed = 3 # 速度
# 飛船移動的速度
self.spaceship_speed = 2
# 標誌位,1表示右移 -1表示左移
self.fleet_direction = 1 # 默認右移動
self.spaceship_points = 15
def increase_speed(self):
"""提高遊戲節奏"""
self.plane_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.spaceship_speed *= self.speedup_scale
# 提高飛船的分數
self.spaceship_points = int(self.spaceship_points * self.score_scale)
遊戲的所有函數
文件名爲game_func.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import sys
from bullet import Bullet
from spaceship import Spaceship
import pygame
from time import sleep
def check_keydown_events(event, plane, setting, screen, bullets):
# 捕捉用戶按下
if event.key == pygame.K_RIGHT:
# 當用戶按下鍵位時標誌位爲True
plane.mv_right = True
elif event.key == pygame.K_LEFT:
plane.mv_left = True
elif event.key == pygame.K_SPACE:
if len(bullets) <= setting.bullet_allowed:
# 創建一個子彈,並將其加入到編組bullets中
new_bullet = Bullet(setting, screen, plane)
bullets.add(new_bullet)
def check_keyup_events(event, plane):
# 捕捉用戶鬆開
if event.key == pygame.K_RIGHT:
# 當用戶鬆開鍵位爲false
plane.mv_right = False
elif event.key == pygame.K_LEFT:
plane.mv_left = False
def check_events(plane, setting, screen, bullets, stats, play_button, spaceships, score_board):
# 爲了防止遊戲窗口啓動會立馬關閉,在其中增加一個遊戲循環(無限循環),
for event in pygame.event.get():
if event.type == pygame.QUIT: # QUIT用戶請求程序關閉
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, plane, setting, screen, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, plane)
elif event.type == pygame.MOUSEBUTTONDOWN: # 檢測MOUSEBUTTONDOWN事件
mouse_x, mouse_y = pygame.mouse.get_pos() # 返回一個元組,包含鼠標單擊時的座標
check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships, score_board)
def check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships, score_board):
# 玩家單機play按鈕時開始遊戲
# collidepoint檢測單擊的位置是否在按鈕的rect內
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active: # 當stats.game_active的值爲False時,取反纔會執行
# 重置記分的所有圖像
score_board.prep_score()
score_board.prep_high_score()
# 重置遊戲設置
setting.initialize_dynamic_settings()
# 隱藏光標
pygame.mouse.set_visible(False)
# 遊戲狀態
stats.game_active = True
# 重置遊戲統計信息
stats.reset_stats()
# 清空飛船列表和子彈列表
spaceships.empty()
bullets.empty()
# 讓飛機居中
plane.center_plane()
def update_screen(screen, bg_img, plane, bullets, spaceships, stats, play_button, score_board):
# 更新屏幕的圖像
# 每次循環都會重新繪製屏幕
screen.blit(bg_img, [0, 0]) # 繪製圖像
# 繪製子彈
for bullet in bullets.sprites():
bullet.draw_bullet() # 繪製子彈
# 繪製飛機
plane.blitme()
for spaceship in spaceships.sprites():
spaceship.blitme()
# 顯示得分
score_board.show_score()
# 如果遊戲處於非活動狀態,繪製Play按鈕
if not stats.game_active:
play_button.draw_button()
# 將完整顯示Surface更新到屏幕
pygame.display.flip()
def update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board):
# 將編組中的每個子彈調用bullet.update()
bullets.update()
# 刪除已經消失的子彈
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
collisions = pygame.sprite.groupcollide(bullets, spaceships, True, True) # 兩個實參true則是爲了將其刪除
"""這個字典中的鍵是打中小飛船的子彈,值則爲一個列表,其中包含了所有被打中的值"""
if collisions: # 當發生碰撞,會有有返回值,纔會爲True
for spaceship in collisions.values():
"""遍歷這個字典,確保每個外星人的點數都計入得分"""
stats.score += setting.spaceship_points * len(spaceship) # 發生碰撞加分
"""因爲子彈的值是一個列表,所有這裏每次計算一下列表的長度來進行加分"""
score_board.prep_score() # 並繪製在屏幕上
check_high_score(stats, score_board)
if len(spaceships) == 0:
bullets.empty() # 刪除編組中的所有精靈(子彈)
# 加快遊戲節奏
setting.increase_speed()
create_fleet(setting, screen, spaceships, plane) # 重新調用生成飛船
def get_number_spaceship_x(setting, spaceship_width):
# 計算每行可以容納多少個外星人的函數
# 計算可以容納多個飛船的寬度
available_space_x = setting.screen_width - (2 * spaceship_width)
number_spaceship_x = int(available_space_x / (2 * spaceship_width)) # 將其轉換爲整數
return number_spaceship_x
def get_number_rows(setting, plane_height, spaceship_height):
# 計算可以容納多少行
available_space_y = setting.screen_height - 7 * spaceship_height - plane_height
number_rows = int(available_space_y / spaceship_height)
return number_rows
def create_spaceship(setting, screen, spaceships, spaceship_number, number_rows):
# 創建一個飛船並加入當前行
spaceship = Spaceship(setting, screen)
spaceship_width = spaceship.rect.width # 飛船的寬度
spaceship.x = spaceship_width + 2 * spaceship_width * spaceship_number
spaceship.rect.x = spaceship.x
spaceship.rect.y = spaceship.rect.height + 2 * spaceship.rect.height * number_rows
spaceships.add(spaceship)
def create_fleet(setting, screen, spaceships, plane):
# 創建一個飛船
spaceship = Spaceship(setting, screen)
number_spaceship_x = get_number_spaceship_x(setting, spaceship.rect.width)
number_rows = get_number_rows(setting, plane.rect.height, spaceship.rect.height)
for row_number in range(number_rows):
for spaceship_number in range(number_spaceship_x):
# if stats.game_active: # 爲真纔會創建飛船
create_spaceship(setting, screen, spaceships, spaceship_number, row_number)
def change_fleet_direction(setting, spaceships):
"""將所有飛船下移,並改變方向"""
for spaceship in spaceships.sprites():
spaceship.rect.y += setting.fleet_drop_speed
setting.fleet_direction *= -1 # 如果爲1則相乘爲-1,如果爲-1相乘爲1
def check_fleet_edges(setting, spaceships):
"""有飛船到達了邊緣應採取的措施"""
for spaceship in spaceships.sprites():
if spaceship.check_edges(): # 如果爲true,已經到了邊緣,就執行change_fleet_direction
change_fleet_direction(setting, spaceships)
break
def plane_hit(setting, spaceships, plane, stats, screen, bullets):
"""
有飛船撞擊到飛機以後已經數量減1,創建一批新的飛創,並將飛機重新反之到屏幕的原始位置
還將引入time模塊的sleep函數實現暫停的效果
"""
if stats.planes_left > 0:
stats.planes_left -= 1 # 將planes_left減1
# print(stats.planes_left)
# 清空飛船和子彈的編組
spaceships.empty()
bullets.empty()
# 創建新的飛機和飛船
create_fleet(setting, screen, spaceships, plane)
plane.center_plane()
sleep(1) # 暫停1秒
else:
stats.game_active = False
# 將光標設置爲顯示
pygame.mouse.set_visible(True)
def check_spaceship_bottom(setting, spaceships, plane, stats, screen, bullets):
# 檢測是否有飛船觸碰到底部
screen_rect = screen.get_rect()
for spaceship in spaceships.sprites():
if spaceship.rect.bottom >= screen_rect.bottom:
# 跟飛船碰撞一樣處理
plane_hit(setting, spaceships, plane, stats, screen, bullets)
def update_spaceships(setting, spaceships, plane, stats, screen, bullets):
# 更新飛船的位置
spaceships.update()
# 檢測時候又飛船處於邊緣,並及時更新
check_fleet_edges(setting, spaceships)
# 檢測飛船與飛機直接的碰撞
'''
pygame.sprite.spritecollideany方法
* 接受兩個參數,一個精靈和一個編組,
* 檢測編組中的成員是否與碰撞,如果檢測到碰撞則停止遍歷編組
* 如果沒有發生碰撞則返回None
'''
game_over = pygame.sprite.spritecollideany(plane, spaceships)
if game_over:
plane_hit(setting, spaceships, plane, stats, screen, bullets)
# 檢測是否有飛船落地
check_spaceship_bottom(setting, spaceships, plane, stats, screen, bullets)
def check_high_score(stats, score_board):
"""用於檢測是否產生最高分"""
if stats.score > stats.high_score:
stats.high_score = stats.score # 如果的得分大於最高分,則值賦給最高分
score_board.prep_high_score()
遊戲的開始
文件名爲plane_war.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings # 引入settings.py
from plane import Plane
import game_func as fg
from pygame.sprite import Group
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard
def run_game():
# 初始化遊戲
pygame.init()
# 設置屏幕的分辨率
setting = Settings()
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小爲1000px乘以600px
pygame.display.set_caption("飛機大戰") # 標題
# 創建play按鈕
play_button = Button(setting, screen, "Play")
# 創建一個用於存儲遊戲統計信息的實例
stats = GameStats(setting)
# 創建記分的實例
score_board = Scoreboard(setting, screen, stats)
# 創建小飛機
plane = Plane(screen, setting)
# 創建一個存儲子彈的編組
bullets = Group()
# 創建飛船編組
spaceships = Group()
# 開始遊戲的主循環
while True:
# 不關閉窗口
fg.check_events(plane, setting, screen, bullets, stats, play_button, spaceships, score_board)
if stats.game_active: # 根據遊戲狀態來判斷是否需要創建其圖像
# 調用小飛機移動的方法
plane.update()
# 更新子彈位置
fg.update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board)
# 更新飛船位置
fg.update_spaceships(setting, spaceships, plane, stats, screen, bullets)
# 繪製圖像
fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships, stats, play_button, score_board)
run_game()
甜甜有話說
終於把這個小項目給完成了,這裏用的pygame
模塊一邊學一邊這就很難哎,反正最後也弄出來
現在好好休息幾天,開始學習爬蟲
座右銘:不要在該奮鬥的年紀選擇安逸!!