早就知道pygame子產品,就是沒怎麼深入研究過,恰逢這周未沒約到妹子,隻能自己在家玩自己啦,一時興起,花了幾個小時寫了個打飛機程式。
很有意思,跟大家分享下。
先看一下項目結構
"""
PlayPlane/
|-- bin/
| |-- main.py 程式運作主體程式
|-- config/
| |-- settings.py 程式配置(例如: 遊戲背景音樂的加載等)
|-- material 程式素材放置(打飛機遊戲素材放置)
|-- ...
|-- src/ 程式主體子產品存放
| |-- __init__.py
| |-- bullet.py 我方飛機發射子彈實作代碼存放
| |-- enemy.py 敵方飛機實作代碼存放
| |-- plane.py 我方飛機實作代碼存放
|-- manage.py 程式啟動檔案
|-- README.md
"""
再曬下項目成果圖

實作步驟
一、首先在 config/settings.py 中進行以下功能的實作
- 遊戲初始化
- 遊戲混音器初始化
- 背景音樂初始化
- 我方飛機挂了音樂
- 敵方飛機挂了音樂
- 子彈發射音樂
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
pygame.init() # 遊戲初始化
pygame.mixer.init() # 混音器初始化
# 遊戲背景音樂
pygame.mixer.music.load("material/sound/game_music.wav")
pygame.mixer.music.set_volume(0.2)
# 子彈發射音樂
bullet_sound = pygame.mixer.Sound("material/sound/bullet.wav")
bullet_sound.set_volume(0.2)
# 我方飛機挂了的音樂
me_down_sound = pygame.mixer.Sound("material/sound/game_over.wav")
me_down_sound.set_volume(0.2)
# 敵方飛機挂了的音樂
enemy1_down_sound = pygame.mixer.Sound("material/sound/enemy1_down.wav")
enemy1_down_sound.set_volume(0.2)
config/settings.py
注:遊戲素材滑動到文章底部點選連結即可下載下傳
二、小試牛刀
飛機和子彈都是可移動的,那麼怎麼實作讓它們動起來呢(我方飛機可以玩家進行操控,敵機就是随機性的出現,子彈暫由我方飛機發射)。
在Pygame中,所有移動對象都可看做是一個精靈(sprite),精靈之間能夠進行互相的互動通信,例如如何讓碰撞檢測更加精準等等。
那麼先讓我們先在螢幕上制作一個遊戲闆,根據 settings.py 配置,并讓它有聲音播放,首先我們在 bin/main.py 中這麼寫:
我們可以直接運作它,那麼我們會看到以下畫面,并且還會有激情的聲音吆!!!但是我們要将檔案配置為絕對路徑才可以運作,因為剛剛在settings中的加載的音樂檔案為相對路徑。
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from config.settings import *
bg_size = 480, 852 # 初始化遊戲背景大小(寬, 高)
screen = pygame.display.set_mode(bg_size) # 設定背景對話框
pygame.display.set_caption("飛機大戰") # 設定标題
background = pygame.image.load(os.path.join(BASE_DIR, "material/image/background.png")) # 加載背景圖檔,并設定為不透明
def main():
pygame.mixer.music.play(loops=-1) # loops 對應的值為 -1 則音樂會無限循環播放
while True:
# 繪制背景圖
screen.blit(background, (0, 0))
# 響應使用者的操作(一定要有響應的使用者操作)
for event in pygame.event.get():
if event.type == 12: # 如果使用者按下螢幕上的關閉按鈕,觸發QUIT事件,程式退出
pygame.quit()
sys.exit()
# 再而我們将背景圖像并輸出到螢幕上面
pygame.display.flip()
if __name__ == '__main__':
main()
接下來呢,我們将要制作我方飛機,敵方飛機和子彈如何讓它們展示在遊戲畫闆上,繼而讓它們變得可移動起來,請看代碼實作方案...
從遊戲畫闆上添加飛機,首先我們應怎樣在螢幕上輸出飛機???
上述講過,pygame中的 sprite(精靈)可使一張圖檔或者一個靜态物體動起來,那麼制作飛機需要考慮并做些什麼呢?
- 飛機的初始位置
- 通過按鍵 上下左右 來調控飛機的位置移動
- 飛機隻能呆在制作的遊戲畫闆中
- 飛機的速度
- 飛機死亡的加載
- 設定一個狀态辨別飛機的存活
- 讓飛機具有動态的噴氣式效果
那麼如何實作以上的功能呢?接下來結合上述的示例代碼我們先将我方飛機繪制到畫闆上方,并且我們通過按鍵 J 判定我方飛機的存活狀态為死亡,繪制飛機的死亡畫面并重置飛機
"""
建立飛機
在pygame中, 所有可移動的對象均叫可看作一個精靈(sprite)
該類并實作了碰撞方法 spritecollide
我方飛機和敵方飛機指定掩膜屬性以及生存狀态标志位 添加 self.mask 屬性(可以實作更精準的碰撞效果)
"""
# 倒入精靈子產品, 使飛機可以動起來
import pygame
class OurPlane(pygame.sprite.Sprite):
def __init__(self, bg_size):
super(OurPlane, self).__init__()
# 确定我方飛機背景圖(有倆張,可以讓它們不停的切換,形成動态效果)
self.image_one = pygame.image.load("material/image/hero1.png")
self.image_two = pygame.image.load("material/image/hero2.png")
# 擷取我方飛機的位置
self.rect = self.image_one.get_rect()
# 本地化背景圖檔的尺寸
self.width, self.height = bg_size[0], bg_size[1]
# 擷取飛機圖像的掩膜用以更加精确的碰撞檢測
self.mask = pygame.mask.from_surface(self.image_one)
# 定義飛機初始化位置,底部預留60像素
self.rect.left, self.rect.top = (self.width - self.rect.width) // 2, (self.height - self.rect.height - 60)
# 設定飛機移動速度
self.speed = 10
# 設定飛機存活狀态(True為存活, False為死亡)
self.active = True
# 加載飛機損毀圖檔
self.destroy_images = []
self.destroy_images.extend(
[
pygame.image.load("material/image/hero_blowup_n1.png"),
pygame.image.load("material/image/hero_blowup_n2.png"),
pygame.image.load("material/image/hero_blowup_n3.png"),
pygame.image.load("material/image/hero_blowup_n4.png")
]
)
def move_up(self):
"""
飛機向上移動的操作函數,其餘移動函數方法類似
"""
if self.rect.top > 0: # 如果飛機尚未移動出背景區域
self.rect.top -= self.speed
else: # 若即将移動出背景區域,則及時糾正為背景邊緣位置
self.rect.top = 0
def move_down(self):
"""
飛機向下移動
"""
if self.rect.bottom < self.height - 60:
self.rect.top += self.speed
else:
self.rect.bottom = self.height - 60
def move_left(self):
"""
飛機向左移動
"""
if self.rect.left > 0:
self.rect.left -= self.speed
else:
self.rect.left = 0
def move_right(self):
"""
飛機向右移動
"""
if self.rect.right < self.width:
self.rect.right += self.speed
else:
self.rect.right = self.width
def reset(self):
# 初始化飛機(飛機挂了, 初始化到初始位置)
self.rect.left, self.rect.top = (self.width - self.rect.width) // 2, (self.height - self.rect.height - 60)
# 重置飛機的存活狀态
self.active = True
上面的代碼寫了一個 我們的飛機 (OurPlane) 類,它初始化了一些屬性以及 上下左右 移動的方法和重置方法,接下來将要運用它展示到遊戲畫闆上面
由于飛機是一直存在的,接下我們主程式 main 下面的死循環中這樣寫
def main():
pygame.mixer.music.play(loops=-1) # loops 對應的值為 -1 則音樂會無限循環播放
our_plane = OurPlane(bg_size) # 初始化
switch_image = False # 定義飛機的切圖效果辨別
while True:
# 繪制背景圖
screen.blit(background, (0, 0))
# 飛機狀态是存活
if our_plane.active:
if switch_image:
screen.blit(our_plane.image_one, our_plane.rect)
else:
screen.blit(our_plane.image_two, our_plane.rect)
switch_image = not switch_image # 讓切圖狀态不停的變換
else:
pass
# 響應使用者的操作(一定要有響應的使用者操作)
for event in pygame.event.get():
if event.type == 12: # 如果使用者按下螢幕上的關閉按鈕,觸發QUIT事件,程式退出
pygame.quit()
sys.exit()
# 再而我們将背景圖像并輸出到螢幕上面
pygame.display.flip()
if __name__ == '__main__':
main()
1.在螢幕上繪制飛機
def main():
pygame.mixer.music.play(loops=-1) # loops 對應的值為 -1 則音樂會無限循環播放
our_plane = OurPlane(bg_size) # 初始化
switch_image = False # 定義飛機的切圖效果辨別
while True:
# 繪制背景圖
screen.blit(background, (0, 0))
# 飛機狀态是存活
if our_plane.active:
if switch_image:
screen.blit(our_plane.image_one, our_plane.rect)
else:
screen.blit(our_plane.image_two, our_plane.rect)
switch_image = not switch_image # 讓切圖狀态不停的變換
else:
pass
# 獲得使用者所有的鍵盤輸入序列(如果使用者通過鍵盤發出“向上”的指令,其他類似)
key_pressed = pygame.key.get_pressed()
if key_pressed[K_w] or key_pressed[K_UP]:
our_plane.move_up()
if key_pressed[K_s] or key_pressed[K_DOWN]:
our_plane.move_down()
if key_pressed[K_a] or key_pressed[K_LEFT]:
our_plane.move_left()
if key_pressed[K_d] or key_pressed[K_RIGHT]:
our_plane.move_right()
# 響應使用者的操作(一定要有響應的使用者操作)
for event in pygame.event.get():
if event.type == 12: # 如果使用者按下螢幕上的關閉按鈕,觸發QUIT事件,程式退出
pygame.quit()
sys.exit()
# 再而我們将背景圖像并輸出到螢幕上面
pygame.display.flip()
if __name__ == '__main__':
main()
2.讓飛機上下左右動起來
def main():
pygame.mixer.music.play(loops=-1) # loops 對應的值為 -1 則音樂會無限循環播放
our_plane = OurPlane(bg_size) # 初始化
switch_image = False # 定義飛機的切圖效果辨別
our_plane_destroy_index = 0
while True:
# 繪制背景圖
screen.blit(background, (0, 0))
# 飛機狀态是存活(如果按鍵 為 J, 讓飛機死亡并繪制爆炸效果)
if our_plane.active:
if switch_image:
screen.blit(our_plane.image_one, our_plane.rect)
else:
screen.blit(our_plane.image_two, our_plane.rect)
switch_image = not switch_image # 讓切圖狀态不停的變換
else:
"""
飛機死亡也是進行按順序的圖檔切換, 那麼在死循環之外定義索引
"""
me_destroy_index = (our_plane_destroy_index + 1) % 4
if me_destroy_index == 1:
me_down_sound.play() # 爆炸聲音效果
our_plane.reset() # 初始化飛機
if our_plane_destroy_index >= len(our_plane.destroy_images):
our_plane_destroy_index = 0
else:
screen.blit(our_plane.destroy_images[our_plane_destroy_index], our_plane.rect)
our_plane_destroy_index += 1
# 獲得使用者所有的鍵盤輸入序列(如果使用者通過鍵盤發出“向上”的指令,其他類似)
key_pressed = pygame.key.get_pressed()
if key_pressed[K_w] or key_pressed[K_UP]:
our_plane.move_up()
if key_pressed[K_s] or key_pressed[K_DOWN]:
our_plane.move_down()
if key_pressed[K_a] or key_pressed[K_LEFT]:
our_plane.move_left()
if key_pressed[K_d] or key_pressed[K_RIGHT]:
our_plane.move_right()
# 按鍵為 j 飛機更改存活辨別
if key_pressed[K_j]:
our_plane.active = False
# 響應使用者的操作(一定要有響應的使用者操作)
for event in pygame.event.get():
if event.type == 12: # 如果使用者按下螢幕上的關閉按鈕,觸發QUIT事件,程式退出
pygame.quit()
sys.exit()
# 再而我們将背景圖像并輸出到螢幕上面
pygame.display.flip()
3.按鍵為 j 繪制飛機的死亡狀态
那麼上述的功能都已經實作了,接下來就開始真正的"打飛機"
三、接下來可以制作我方飛機,敵方戰機,子彈等,這些功能均在 src/ 目錄下實作
- 我方飛機根據按鍵上下左右進行移動,初始化位置,噴氣式圖檔加載切換及重置效果等
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
建立飛機
在pygame中, 所有可移動的對象均叫可看作一個精靈(sprite)
該類并實作了碰撞方法 spritecollide
我方飛機和敵方飛機指定掩膜屬性以及生存狀态标志位 添加 self.mask 屬性(可以實作更精準的碰撞效果)
"""
# 倒入精靈子產品, 使飛機可以動起來
import pygame
class OurPlane(pygame.sprite.Sprite):
def __init__(self, bg_size):
super(OurPlane, self).__init__()
# 确定我方飛機背景圖
self.image_one = pygame.image.load("material/image/hero1.png")
self.image_two = pygame.image.load("material/image/hero2.png")
# 擷取我方飛機的位置
self.rect = self.image_one.get_rect()
# 本地化背景圖檔的尺寸
self.width, self.height = bg_size[0], bg_size[1]
# 擷取飛機圖像的掩膜用以更加精确的碰撞檢測
self.mask = pygame.mask.from_surface(self.image_one)
# 定義飛機初始化位置,底部預留60像素
self.rect.left, self.rect.top = (self.width - self.rect.width) // 2, (self.height - self.rect.height - 60)
# 設定飛機移動速度
self.speed = 10
# 設定飛機存活狀态(True為存活, False為死亡)
self.active = True
# 加載飛機損毀圖檔
self.destroy_images = []
self.destroy_images.extend(
[
pygame.image.load("material/image/hero_blowup_n1.png"),
pygame.image.load("material/image/hero_blowup_n2.png"),
pygame.image.load("material/image/hero_blowup_n3.png"),
pygame.image.load("material/image/hero_blowup_n4.png")
]
)
def move_up(self):
"""
飛機向上移動的操作函數,其餘移動函數方法類似
"""
if self.rect.top > 0: # 如果飛機尚未移動出背景區域
self.rect.top -= self.speed
else: # 若即将移動出背景區域,則及時糾正為背景邊緣位置
self.rect.top = 0
def move_down(self):
if self.rect.bottom < self.height - 60:
self.rect.top += self.speed
else:
self.rect.bottom = self.height - 60
def move_left(self):
if self.rect.left > 0:
self.rect.left -= self.speed
else:
self.rect.left = 0
def move_right(self):
if self.rect.right < self.width:
self.rect.right += self.speed
else:
self.rect.right = self.width
def reset(self):
# 初始化飛機(飛機挂了, 初始化到初始位置)
self.rect.left, self.rect.top = (self.width - self.rect.width) // 2, (self.height - self.rect.height - 60)
self.active = True
src/plane.py
src/plane.py
- 敵方飛機随機移動出現及重置(制作出我方飛機之後,敵機和子彈其實都是大同小異的)
-
python 之路,200行Python代碼寫了個打飛機遊戲! python 之路,200行Python代碼寫了個打飛機遊戲!
src/enemy.py#! /usr/bin/env python # -*- coding: utf-8 -*- """ 定義敵機 """ from random import randint import pygame class SmallEnemy(pygame.sprite.Sprite): """ 定義小飛機敵人 """ def __init__(self, bg_size): super(SmallEnemy, self).__init__() self.image = pygame.image.load("material/image/enemy1.png") self.rect = self.image.get_rect() self.width, self.height = bg_size[0], bg_size[1] self.mask = pygame.mask.from_surface(self.image) # 擷取飛機圖像的掩膜用以更加精确的碰撞檢測 self.speed = 2 # 定義敵機出現的位置, 保證敵機不會在程式已開始就立即出現 self.rect.left, self.rect.top = ( randint(0, self.width - self.rect.width), randint(-5 * self.rect.height, -5), ) self.active = True # 加載飛機損毀圖檔 self.destroy_images = [] self.destroy_images.extend( [ pygame.image.load("material/image/enemy1_down1.png"), pygame.image.load("material/image/enemy1_down2.png"), pygame.image.load("material/image/enemy1_down3.png"), pygame.image.load("material/image/enemy1_down4.png") ] ) def move(self): """ 定義敵機的移動函數 :return: """ if self.rect.top < self.height: self.rect.top += self.speed else: self.reset() def reset(self): """ 當敵機向下移動出螢幕時, 以及敵機死亡 :return: """ self.rect.left, self.rect.top = (randint(0, self.width - self.rect.width), randint(-5 * self.rect.height, 0)) self.active = True
- 子彈按照我方飛機正中上方發射及頻率調控,重置
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
子彈的實作
"""
import pygame
class Bullet(pygame.sprite.Sprite):
def __init__(self, position):
super(Bullet, self).__init__()
self.image = pygame.image.load("material/image/bullet1.png")
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = position
self.speed = 30
self.active = True
self.mask = pygame.mask.from_surface(self.image)
def move(self):
"""
子彈移動, 超出螢幕範圍, 則設定死亡
:return:
"""
if self.rect.top < 0:
self.active = False
else:
self.rect.top -= self.speed
def reset(self, position):
"""
複位函數
:param position:
:return:
"""
self.rect.left, self.rect.top = position
self.active = True
src/bullet.py
src/bullet.py
在上面的内容中,使用面向對象的形式制作了遊戲中可移動的對象并繼承 pygame.sprite.Sprite
四、然後在 bin/main.py 中進行主體功能的實作
- 初始化背景圖及大小
- 我方飛機移動及發射子彈
- 敵方飛機移動
- 我方飛機和敵方飛機碰撞檢測
- 鍵盤按鍵監測效果
- 我方飛機和敵方飛機挂了效果繪制
import sys
from pygame.locals import *
from config.settings import *
from src.plane import OurPlane
from src.enemy import SmallEnemy
from src.bullet import Bullet
bg_size = 480, 852 # 初始化遊戲背景大小(寬, 高)
screen = pygame.display.set_mode(bg_size) # 設定背景對話框
pygame.display.set_caption("飛機大戰") # 設定标題
background = pygame.image.load("material/image/background.png") # 加載背景圖檔,并設定為不透明
# 擷取我方飛機
our_plane = OurPlane(bg_size)
def add_small_enemies(group1, group2, num):
"""
添加小型敵機
指定個敵機對象添加到精靈組(sprite.group)
參數group1、group2是兩個精靈組類型的形參,用以存儲多個精靈對象(敵機)。
需要注意的一點是group既然是特定的精靈組結構體,在向其内部添加精靈對象時需要調用其對應的成員函數add()
:return:
"""
for i in range(num):
small_enemy = SmallEnemy(bg_size)
group1.add(small_enemy)
group2.add(small_enemy)
def main():
# 響應音樂
pygame.mixer.music.play(-1) # loops 接收該參數, -1 表示無限循環(預設循環播放一次)
running = True
switch_image = False # 切換飛機的辨別位(使飛機具有噴氣式效果)
delay = 60 # 對一些效果進行延遲, 效果更好一些
enemies = pygame.sprite.Group() # 生成敵方飛機組(一種精靈組用以存儲所有敵機精靈)
small_enemies = pygame.sprite.Group() # 敵方小型飛機組(不同型号敵機建立不同的精靈組來存儲)
add_small_enemies(small_enemies, enemies, 4) # 生成若幹敵方小型飛機
# 定義子彈, 各種敵機和我方敵機的毀壞圖像索引
bullet_index = 0
e1_destroy_index = 0
me_destroy_index = 0
# 定義子彈執行個體化個數
bullet1 = []
bullet_num = 6
for i in range(bullet_num):
bullet1.append(Bullet(our_plane.rect.midtop))
while running:
# 繪制背景圖
screen.blit(background, (0, 0))
# 微信的飛機貌似是噴氣式的, 那麼這個就涉及到一個幀數的問題
clock = pygame.time.Clock()
clock.tick(60)
# 繪制我方飛機的兩種不同的形式
if not delay % 3:
switch_image = not switch_image
for each in small_enemies:
if each.active:
# 随機循環輸出小飛機敵機
for e in small_enemies:
e.move()
screen.blit(e.image, e.rect)
else:
if e1_destroy_index == 0:
enemy1_down_sound.play()
screen.blit(each.destroy_images[e1_destroy_index], each.rect)
e1_destroy_index = (e1_destroy_index + 1) % 4
if e1_destroy_index == 0:
each.reset()
# 當我方飛機存活狀态, 正常展示
if our_plane.active:
if switch_image:
screen.blit(our_plane.image_one, our_plane.rect)
else:
screen.blit(our_plane.image_two, our_plane.rect)
# 飛機存活的狀态下才可以發射子彈
if not (delay % 10): # 每十幀發射一顆移動的子彈
bullet_sound.play()
bullets = bullet1
bullets[bullet_index].reset(our_plane.rect.midtop)
bullet_index = (bullet_index + 1) % bullet_num
for b in bullets:
if b.active: # 隻有激活的子彈才可能擊中敵機
b.move()
screen.blit(b.image, b.rect)
enemies_hit = pygame.sprite.spritecollide(b, enemies, False, pygame.sprite.collide_mask)
if enemies_hit: # 如果子彈擊中飛機
b.active = False # 子彈損毀
for e in enemies_hit:
e.active = False # 小型敵機損毀
# 毀壞狀态繪制爆炸的場面
else:
if not (delay % 3):
screen.blit(our_plane.destroy_images[me_destroy_index], our_plane.rect)
me_destroy_index = (me_destroy_index + 1) % 4
if me_destroy_index == 0:
me_down_sound.play()
our_plane.reset()
# 調用 pygame 實作的碰撞方法 spritecollide (我方飛機如果和敵機碰撞, 更改飛機的存活屬性)
enemies_down = pygame.sprite.spritecollide(our_plane, enemies, False, pygame.sprite.collide_mask)
if enemies_down:
our_plane.active = False
for row in enemies:
row.active = False
# 響應使用者的操作
for event in pygame.event.get():
if event.type == 12: # 如果使用者按下螢幕上的關閉按鈕,觸發QUIT事件,程式退出
pygame.quit()
sys.exit()
if delay == 0:
delay = 60
delay -= 1
# 獲得使用者所有的鍵盤輸入序列(如果使用者通過鍵盤發出“向上”的指令,其他類似)
key_pressed = pygame.key.get_pressed()
if key_pressed[K_w] or key_pressed[K_UP]:
our_plane.move_up()
if key_pressed[K_s] or key_pressed[K_DOWN]:
our_plane.move_down()
if key_pressed[K_a] or key_pressed[K_LEFT]:
our_plane.move_left()
if key_pressed[K_d] or key_pressed[K_RIGHT]:
our_plane.move_right()
# 繪制圖像并輸出到螢幕上面
pygame.display.flip()
bin/main.py
五、暢汗淋漓,一氣呵成打飛機
from bin.main import main
if __name__ == '__main__':
"""
環境: python3 + pygame
running 起來就可以打飛機了O(∩_∩)O~.
"""
main()
最終效果!
完整項目代碼 : https://github.com/triaquae/jerkoff
另外,本項目所用到的基礎知識視訊 已上傳至 路飛學城 ,需要者自取! http://luffy.oldboyedu.com/course/detail/python/5