程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python基礎:第012課——最小的程序框架(代碼解析)

編輯:Python

參考:pygame詳細教程
參考案例:游戲模塊
Pygame 作為一個入門級的游戲開發庫,其實並不難學,只要掌握 Python 編程的相關知識就能很輕松地掌握它。

Pygame 語法簡單、明了,秉持了 Python 語言一貫的風格。同時,它作為一個游戲開發庫來說,具有圖形編程的基本特點,如果您對於圖形編程從未了解過,即使您有 Python 編程基礎,也會略感迷茫。因此,在接下來的學習中會詳細介紹 Pygame 的常用模塊,以及圖形編程中的相關概念,幫助您快速掌握 Pygame 的使用。

屏幕坐標系

#導入所需的模塊
import pygame, sys
# 使用pygame之前必須初始化
pygame.init()
# 設置主屏窗口
screen = pygame.display.set_mode((600,400))
# 設置窗口的標題,即游戲名稱
pygame.display.set_caption("小小工坊")
# 加載圖像,存儲在ball變量中,ball是一個圖像對象
ball = pygame.image.load('pygame/images/ball.gif')
#獲得顯示對象的rect區域坐標
ball_rect = ball.get_rect()
# 設置顯示對象居中
ball_rect.center = (300, 200)
# 將准備好的圖像繪制到主屏幕 Screen 上。
screen.blit(ball, ball_rect)
# 建立時鐘對象
fclock = pygame.time.Clock()
# 固定代碼段,實現點擊關閉按鈕退出游戲的功能,幾乎所有的pygame都會使用該段代碼
while True :
# 循環獲取事件,監聽事件狀態
for event in pygame.event.get():
# 判斷用戶是否點了"X"關閉按鈕,並執行if代碼段
if event.type == pygame.QUIT:
#卸載所有模塊
pygame.quit()
#終止程序,確保退出程序
sys.exit()
ball_rect = ball_rect.move(speed,speed)
screen.fill((0,0,0))
screen.blit(ball,ball_rect)
#更新屏幕內容
pygame.display.update()
fclock.tick(60)

12. 代碼解析

Python:pygame包的詳細使用方法

12.1 游戲的初始化和退出

# 使用pygame之前必須初始化
pygame.init()
  • 要使用pygame提供的所有功能之前,需要調用init 方法,它的作用是自動檢測 Pygame 軟件包是否正常可用,並檢查電腦的硬件調用接口、基礎功能是否存在問題,比如音頻、光驅、聲卡驅動等設備。同時,它會完成 Pygame 中所有模塊的初始化操作,比如 display(顯示模塊)、font(字體模塊)、mixer(聲音控制模塊)、cursors(光標控制模塊)等。

  • 在游戲結束前需要調用一下quit方法

12.1.1 方法說明

pygame.init() 導入並初始化所有pygame模塊,使用其他模塊前,必須先調用iinit方法
pygame.quit() 卸載所有pygame模塊,在游戲結束之前調用

12.2 Surface對象

12.2.1 坐標系

  • 原點在左上角 (0,0)
  • x軸水平方向向右,逐漸增加
  • y軸垂直方向向下,逐漸增加

如圖:

  • 在游戲中,所有可見的元素都是以矩形區域來描述位置的
  • 要描述一個矩形區域有四個要素: (x, y) (width, height)

(x,y)是物體坐標,(width, height)是物體像素大小

  • pygame 專門提供了一個類pygame.Rect 用於描述矩形區域

x, y
left,top,bottom,right
center,centerx,centery
size,width,height

12.2.2 如何創建Surface對象

那麼我們應該如何創建 Surface 對象呢?Pygame 提供了多種創建 Surface 對象的方法,這裡先介紹以下幾種方法。

上述示例,使用如下方式創建了一個 surface 對象:

#也叫screen對象,本質上是一個Surface,大小400*400
screen = pygame.display.set_mode((400,400))

screen 的本質上就是一個 Surface 對象,它是游戲的主窗口,也就是整個游戲中尺寸最大的“紙”,任何其他的 Surface 對象都需要附著在這張最大的“紙”上,比如創建一個圖像的 Surface 對象,通過以下方法將它繪制在主屏幕上:

# 加載圖像,存儲在ball變量中,ball是一個圖像對象
ball = pygame.image.load('pygame/images/ball.gif')
#獲得顯示對象的rect區域坐標
ball_rect = ball.get_rect()
# 設置顯示對象居中
ball_rect.center = (300, 200)
# 將准備好的圖像繪制到主屏幕 Screen 上。
screen.blit(ball, ball_rect)

比如創建一個包含文本的 Surface 對象,通過以下方法將它繪制在主屏幕上:

# 引入字體類型
f = pygame.font.Font('C:/Windows/Fonts/simhei.ttf',50)
#創建一個包含文字的Surface對象
# 生成文本信息,第一個參數文本內容;第二個參數,字體是否平滑;
# 第三個參數,RGB模式的字體顏色;第四個參數,RGB模式字體背景顏色;
text = f.render("小小工坊",True,(255,0,0),(0,0,0))
textRect =text.get_rect()
#通過blit方法將其繪制在主屏幕上,這裡的textRect表示位置坐標
screen.blit(text,textRect)
set_mode()方法

set_ mode( resolution=(0,0),flags=0, depth=0) -> Surface(名字隨意)

  • 作用:創建游戲顯示窗口
  • 參數
    • resolution 指定屏幕的寬和高,默認創建的窗口大小和屏幕大小一致
    • flags參數指定屏幕的附加選項,例如是否全屏等等,默認不需要傳遞
    • depth參數表示顏色的位數,默認自動匹配
  • 返回值:Surface對象
    Surface對象是內存中的屏幕數據對象,可以理解為游戲的屏幕,游戲的元素都需要被繪制到游戲的屏幕上
    1. 在開發時,可能會需要使用固定的數值,例如屏幕的高度是700
    2. 這個時候,建議不要直接使用固定數值,而應該使用常量
    3. 當實驗需求發生變化時,只需要修改常量的值就可,不需要在一個一個去找數值更改

常量的命名方式:

所有字母都使用大寫,單詞與單詞之間使用下劃線連接

12.2.3 *Rect對象

pygame.Rect是一個比較特殊的類,內部只是封裝了一些數字計算

實例:

import pygame
hero = pygame.Rect(100,200,50,120)
print("英雄的x,y坐標分別是 %d, %d" % (hero.x, hero.y))
print("英雄的寬高分別是:%d,%d" % (hero.width, hero.height))
# Rect中的size屬性是一個元組(寬,高)
print("英雄的寬高分別是:%d,%d" % hero.size))
# Rect中的bottom屬性表示底部的縱坐標 y+height,top屬性表示元素頂部縱坐標y
print("英雄的底部y坐標為:%d,頂部y坐標為:%d" % (hero.bottom, hero.top))
# Rect的right屬性表示元素右邊橫坐標 x+width, left表示元素橫坐標x
print("英雄的最右邊x坐標為%d,最左邊x坐標為:%d" % (hero.right, hero.left))
# Rect的center屬性是一個元組(centerx,centery)
print("英雄的中心點坐標為:%d,%d" % hero.center)

12.3 圖像繪制

那麼要在創建的窗口中按照指定要求顯示圖片,則需要三個步驟:

1. 使用`pygame.image.load()`加載圖像的數據
2. 使用游戲屏幕對象,調用`blit`方法將圖像繪制到指定位置
3. 調用`pygame.display.update()`方法更新整個屏幕的顯示
****load-File 圖像文件blit-image 圖像對象+tuple 位置update-screen update

注:如果不調用pygame.display.update()方法,則運行程序後圖片並不會顯示出來,在所有圖像繪制完成後可放在最後統一更新

12.4 事件監聽

游戲,在我們日常生活中經常接觸到,無論是手游、還是電腦端游戲,已經成了信息社會中,不可或缺的一部分。

游戲大致來講是由動畫和人機交互的體驗兩部分構成,其中動畫則是由一系列連續靜止的圖片,經過一定頻率的刷新構成的,這個頻率被稱為 FPS,如果頻率值越大則畫面越流暢;如果頻率值越小則畫面會出現卡頓的感,在游戲過程中一般人能接受的最低 FPS 約為 30Hz,如果想要畫面流暢則 FPS 要大於 60 Hz。

FPS 越高,細節越好,體驗也越好,但是文件容量也越高

動畫保證了玩家的視覺體驗,而人機交互則是操作上的體驗。通過移動和點擊鼠標、按下鍵盤上的技能鍵,或是滑動手機屏幕等操作來實現人機交互,這些與游戲程序交互的操作被稱為事件(Event)。

Pygame 作為一個游戲開發庫,同樣具有設置和監聽事件的功能。它提供了一個 event 事件模塊,這個模塊中包含了所有常用到游戲事件。下面是退出游戲的代碼示例(其他事件類型,後續會做介紹):

# 循環獲取事件,監聽事件狀態,使用get()獲取事件
for event in pygame.event.get():
# 判斷事件類型,用戶是否點了"X"關閉按鈕
# pygame.QUIT 指點擊右上角窗口的"X"號
if event.type == pygame.QUIT:
#點擊後,卸載所有pygame模塊
pygame.quit()

12.5 游戲循環

當打我們游戲時可能會觸發游戲中的各種事件,比如鼠標事件、鍵盤按鍵事件、攝像拍照事件等等,因此游戲程序需要一直循環監聽玩家的操作,只有當用戶點擊了游戲“關閉”按鈕時,監聽才會結束。如果想要達到“循環監聽”目的,此時就需要設置一個游戲循環(Game Loop)也稱為游戲的主循環,這樣才能保證人機交互的體驗感。代碼示例如下:

#游戲主循環(游戲循環)
while True:
# 循環獲取事件,監聽事件
for event in pygame.event.get():
# 判斷用戶是否點了關閉按鈕
if event.type == pygame.QUIT:
# 當用戶關閉游戲窗口時執行以下操作
# 這裡必須調用quit()方法,退出游戲
pygame.quit()
#終止系統
sys.exit()
#更新並繪制屏幕內容
pygame.display.flip()

游戲主循環是每個 Pygame 游戲程序中必不可少的一部分,它主要承擔著以下三個重要任務:

  • 處理游戲事件
  • 更新游戲狀態
  • 把更新後的游戲狀態繪制到屏幕上


圖2:主循環示意圖

游戲畫面和游戲操作狀態會因為動畫效果和玩家的操作而改變,因此需要以循環的方式實時地更新主屏幕(screen)的顯示內容。把下列代碼放入游戲主循環中即可實現實時更新和繪制屏幕內容,如下所示:

#刷新界面顯示
pygame.display.flip()

除了上述方法外,Pygame 還提供了另一個方法。如下所示:

pygame.display.update()

這兩個方法的主要區別是:後者可以根據選定的區域來更新部分內容,而前者則是更新整個待顯示的內容。如果後者沒有提供區域位置參數時,其作用和 display.flip() 相同。

12.6 關於動畫實現的原理

跟電影的原理類似,游戲中的動畫效果,本質上是快速的在屏幕上繪制圖像,電影是將多張靜止的電影膠片連續、快速的播放,產生連貫的視覺效果!

一般在電腦上每秒繪制60次,就能夠達到非常連續高品質的動畫效果,每次繪制的結果被稱為幀Frame

在每次調用pygame.display.update()方法產生的結果為一幀,也就是說需要每秒鐘調用60次pygame.display.update()方法,就可以達到高品質動畫效果

如何實現每秒60幀?
pygame專門提供了一個類pygame.time.Clock 可以非常方便的設置屏幕繪制速度- -刷新幀率

要使用時鐘對象需要兩步:

  1. 在游戲初始化創建一個時鐘對象
  2. 在游戲循環中讓時鐘對象調用tick(幀率) 方法,tick 方法會根據.上次被調用的時間, 自動設置游戲循環中的延時

實現方法:

# 創建時鐘對象
clock = pygame.time.Clock()
while True:
# 在游戲循環內部調用tick方法,裡面傳入想要的幀數
​ clock.tick(60)

動畫實現案例:飛機向上飛行
例 :

 import pygame
pygame.init()
# 創建游戲窗口
screen = pygame.display.set_mode((480,700))
# 加載游戲中需要的圖像
background = pygame.image.load("./pygame/images/xx.jpg")
plane = pygame.image.load("./pygame/images/xxx.jpg)
# 將圖像顯示到游戲初始化的位置
screen.blit(background, (0, 0))
screen.blit(plane, (120, 500))
# 定義飛機顯示的矩形區域
plane_Rect = pygame.Rect(120, 500, 120, 90)
# 創建時鐘對象
clock = pygame.time.Clock()
while True:
# 定義每秒60幀
clock.tick(60)
# 移動飛機顯示矩形區域的位置
plane_Rect.y = plane_Rect.y -5
# 重繪背景圖像,避免出現殘影
screen.blit(background, (0, 0))
# 重新繪制圖像
screen.blit(plane, place_Rect)
# 更新屏幕
pygame.display.update()
pygame.quit()

12.7 針對鍵盤按鍵捕獲的兩種方式

●第一種方式判斷event.type == pygame.KEYDOWN

實現代碼:

if event.type == pygame. KEYDOWN and event.key == pygame.K RIGHT:
print("向右移動...")

●第二種方式(按住方向鍵不放可持續移動)

  1. 首先使用pygame. key.get_ pressed()返回所有按鍵元組
  2. 通過鍵盤常量,判斷元組中某一個鍵是否被按下——如果被按下,對應數值為1

實現代碼:

變量 = pygame.key.get_pressed()
if 變量[pygame.K_RIGHT]:
print("向右移動")

● 兩種方式的區別

第一種方式 event.type 用戶必須抬起按鍵才算一次按鍵事件(利用列表也可實現持續向某一方向移動,具體見)
第二種方式 用戶可按鍵不放,能夠實現持續向某一個方向移動

*12.8 pygame中提供的高級類精靈和精靈組

圖像加載、位置變化、繪制圖像都需要程序員編寫代碼分別處理,為了簡化代碼,避免過多類似重復的代碼,pygame提供了精靈和精靈組類

心得: 其根本就是,將需要創建的多個執行相同動作的元素通過精靈類統一規劃到一起,再通過精靈組統一執行每個元素的動作並繪制到屏幕上,將多次重復的代碼縮減成一次代碼。

  • pygame.sprite.Sprite —— 存儲圖像數據image和位置rect 的對象
  • pygame.sprite.Group

注:在開發時,如果子類的父類不是Object類,則需要通過super(),__init__()來調用父類的初始化方法,因為父類中的初始化方法中封裝了許多屬性,在繼承父類後只有通過手動調用父類的初始化方法,才能在子類的初始化方法中使用父類初始化方法封裝的屬性

●精靈
。封裝圖像image、位置rect
。提供update() 方法,根據游戲需求,更新位置rect

 # 定義對象的屬性
self.image = pygame.image.load(image)
# 得到圖片的坐標和寬高像素
self.rect = self.image.get_rect()

●精靈組
。包含多個精靈對象
。update 方法,讓精靈組中的所有精靈調用update 方法更新位置
。draw(游戲窗口對象) 方法,在游戲窗口上繪制精靈組中的所有精靈

實例:

#精靈類planeSprites:
import pygame
class planeSprites(pygame.sprite.Sprite):
def __init__(self, image, speed=1):
# 調用父類的初始化方法
super().__init__()
# 定義對象的屬性
self.image = pygame.image.load(image)
# 得到圖片的坐標和寬高像素
self.rect = self.image.get_rect()
# 定義圖片的運行速度
self.speed = speed
def update(self, *args):
# 使飛機斜著飛行
self.rect.x += self.speed
self.rect.y += self.speed
# 殺死精靈,節約內存
self.kill()

主程序中:

import pygame
from plane_sprites import planeSprites
pygame.init()
# 創建敵機1,敵機2
enemy1 = planeSprites("./飛機大戰素材/敵機.png")
enemy2 = planeSprites("./飛機大戰素材/敵機.png")
# 在精靈組中加入敵機1
enemy_group = pygame.sprite.Group(enemy1, enemy2)
while True:
# 調用精靈組的update方法
enemy_group.update()
# 將精靈組中的敵機繪制到屏幕上
enemy_group.draw(screen)
# ***一定記得更新視圖,否則不會顯示出任何圖像
pygame.display.update()
pygame.quit()

12.9 pygame中提供的兩種精靈碰撞檢測方法


pygame.sprite.groupcolide()
groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_ dict

group1和group2都是一個精靈組
dokill1和dokill2都是布爾類型

當把dokill1設置為True後,精靈組1和精靈組2碰撞時精靈組1會自動銷毀
當把dokill2設置為True後,精靈組1和精靈組2碰撞時精靈組2會自動銷毀

pygame.sprite.spritecollide()
●判斷某個精靈和指定精靈組中的精靈的碰撞

spritecollide(sprite, group, dokill, collided = None) -> Sprite_ list

●如果將dokill 設置為True,則指定精靈組中發生碰撞的精靈將被自動移除

● collided參數是用於計算碰捶的回調函數
。如果沒有指定,則每個精靈必須有一一個rect 屬性

●返回精靈組中跟精靈發生碰撞的精靈列表

注:這裡是返回一個列表

代碼實現:

 enemies = pygame.sprite.spritecollide(self.hero, self.enemyGroup, True)
if len(enemies) > 0:
print("游戲結束,飛機受到損壞")
pygame.quit()
exit()

12.10 背景交替滾動

俗稱跑步機模式

在飛機大戰中游戲背景不斷變化產生向上飛行的視覺效果

實現原理:

實際上是兩張尺寸與屏幕一樣大小的背景圖片向下移動,等第一張圖片剛移除出屏幕後立馬改變坐標移動到第二張圖片之後

代碼實現:
精靈類

import pygame
from plane_main import SCREEN_RECT
class planeSprites(pygame.sprite.Sprite):
def __init__(self, image, speed=1):
# 調用父類的初始化方法
super().__init__()
# 定義對象的屬性
self.image = pygame.image.load(image)
# 得到圖片的坐標和寬高像素
self.rect = self.image.get_rect()
# 定義圖片的運行速度
self.speed = speed
def update(self, *args):
# 使精靈向下移動
self.rect.y += self.speed
# 當父類update更新方法已經無法滿足某一精靈類的條件則可以創建心得精靈子類
class background(planeSprites):
def __init__(self, is_single=False):
super().__init__("./飛機大戰素材/游戲背景.jpg")
if is_single:
#將第二張圖片放在第一張圖片的上面
self.rect.y = - self.rect.height
def update(self, *args):
# 調用父類更新方法
super().update()
if self.rect.y == SCREEN_RECT.height:
self.rect.y = - self.rect.height

主方法:

import pygame
from plane_sprites import *
pygame.init()
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
class main(object):
def __init__(self):
# 創建游戲屏幕
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 創建游戲時鐘
self.clock = pygame.time.Clock()
# 創建游戲精靈組
self.__create_spirits()
# 精靈創建
def __create_spirits(self):
bg1 = background()
bg2 = background(True)
self.backGroup = pygame.sprite.Group(bg1, bg2)
# 精靈更新處理
def __update_spirits(self):
self.backGroup.update()
self.backGroup.draw(self.screen)

12.11 定時器

●在 pygame中可以使用pygame.time.set.timer()來添加定時器

●所謂定時器,就是每隔一段時間,去執行些動作

set_timer(eventid, milliseconds) -> None

●set_timer 可以創建一個事件
●可以在游戲循環的事件監聽方法中捕獲到該事件

● 第1個參數事件代號需要基於常量pygame. USEREVENT 來指定

USEREVENT 是一個整數,再增加的事件可以使用USEREVENT + 1指定,依次類推…

●第2個參數是事件觸發間隔的毫秒值

定時器事件的監聽
●通過pygame.event.get() 可以獲取當前時刻所有的事件列表
●遍歷列表並且判斷event.type 是否等於eventid, 如果相等,表示定時器事件發生

實現方法:

# 創建敵機的定時器,每隔1000ms發出一個事件
pygame.time.set_timer(pygameUSEREVENT, 3000)
# 事件監聽
for event in pygame.event.get():
if event.type == pygameUSEREVENT:
pass

實例(敵機隨機飛進屏幕中):

精靈類

import random
import pygame
# 指定屏幕格式大小
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
CREATE_EVVENTID = pygame.USEREVENT
class planeSprites(pygame.sprite.Sprite):
def __init__(self, image, speed=1):
# 調用父類的初始化方法
super().__init__()
# 定義對象的屬性
self.image = pygame.image.load(image)
# 得到圖片的坐標和寬高像素
self.rect = self.image.get_rect()
# 定義圖片的運行速度
self.speed = speed
# 創建敵機的定時器,每隔1000ms發出一個事件
pygame.time.set_timer(CREATE_EVVENTID, 3000)
class enemy(planeSprites):
"""敵機精靈"""
def __init__(self):
super().__init__("./飛機大戰素材/敵機.png")
self.rect.bottom = 0
# 飛機速度從1—3中任取
self.speed = random.randint(1, 3)
max_x = SCREEN_RECT.width - self.rect.width
# 飛機的位置從0-屏幕右邊任取
self.rect.x = random.randint(0, max_x)
def update(self, *args):
super().update()
# 判斷精靈飛出屏幕後殺死精靈
if self.rect.y >= SCREEN_RECT.height:
self.kill()

主方法:

import pygame
from plane_sprites import *
pygame.init()
# 精靈創建
def __create_spirits(self):
self.enemyGroup = pygame.sprite.Group()
# 事件監聽處理
def __event_handler(self):
for event in pygame.event.get():
# 判斷事件類型是否是定時器給出的
if event.type == CREATE_EVVENTID:
# 新增精靈
oneEnemy = enemy()
# 向精靈組中添加精靈
self.enemyGroup.add(oneEnemy)
# 精靈更新處理
def __update_spirits(self):
self.enemyGroup.update()
self.enemyGroup.draw(self.screen)

12.12 音樂導入

#導入音樂
pygame.mixer.init()
def background_music_load():
global hero_fire_music # 導入函數外部變量
pygame.mixer.music.load("./music/PlaneWarsBackgroundMusic.mp3")#游戲背景音樂
pygame.mixer.music.set_volume(0.3)#設置音量(0-1)
pygame.mixer.music.play(-1)#循環播放
hero_fire_music = pygame.mixer.Sound("./music/hero_fire.wav")#飛機開火音樂
hero_fire_music.set_volume(0.2)#設置音量(0-1)


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved