2019.5.5_我能作到,哈哈哈哈_pygame突擊

最近要寫 pygame 的小項目了,快複習一下 pygame !!複習的有點兒急!之後返回來改好,原文大佬是2011年寫的可是依然經典,不過是拿python2 寫的,我稍微改了改,大BUG應該沒有,可能有小問題,我會回來改的相信我!!html


寫這個玩意兒時還出現點兒小問題,哈哈 python

摘自這位大佬的博客學習 blog.csdn.net/mingzznet/a…我當年就是如今這裏入門的,哈哈,回來,看看師傅,順便複習!至於圖片你們訪問大佬的博客吧,哈哈,別打我!


一.初識

pygame 有不少模塊:

模塊名字 功能
pygame.cdrom 訪問光驅
pygame.cursors 加載光標
pygame.display 訪問顯示設備
pygame.draw 繪製形狀、線和點
pygame.event 管理事件
pygame.font 使用字體
pygame.image 加載和存儲圖片
pygame.joystick 使用遊戲手柄或者 相似的東西
pygame.key 讀取鍵盤按鍵
pygame.mixer 聲音
pygame.mouse 鼠標
pygame.movie 播放視頻
pygame.music 播放音頻
pygame.overlay 訪問高級視頻疊加
pygame 就是咱們在學的這個東西了……
pygame.rect 管理矩形區域
pygame.sndarray 操做聲音數據
pygame.sprite 操做移動圖像
pygame.surface 管理圖像和屏幕
pygame.surfarray 管理點陣圖像數據
pygame.time 管理時間和幀信息
pygame.transform 縮放和移動圖像

有些模塊可能在某些平臺上不存在,你能夠用None來測試一下小程序

>>> pygame.font is None
複製代碼

Helloworld.py

# 先指定圖像文件名稱
background_image_filename = '1.jpeg'
mouse_image_filename = 'fugu.png'

import pygame                     # 導入pygame庫
from pygame.locals import *       # 導入經常使用的常亮
from sys import exit              # 向sys模塊借一個exit函數用來退出程序

pygame.init()                     # 初始化pygame,爲使用硬件作準備
screen = pygame.display.set_mode((640, 480), 0, 32)       # 建立了一個窗口
pygame.display.set_caption("Hello, World!")               # 設置窗口標題

#加載並轉換圖像
background = pygame.image.load(background_image_filename).convert()
mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()

#遊戲主循環
while True:
    for event in pygame.event.get():
        if event.type == QUIT:   
            exit()                     # 接收到退出事件後退出程序
            
    screen.blit(background, (0,0))     # 將背景圖畫上去
    x, y = pygame.mouse.get_pos()      # 得到鼠標位置
    
    x-= mouse_cursor.get_width() / 2 
    y-= mouse_cursor.get_height() / 2  # 計算光標的左上角位置
    
    screen.blit(mouse_cursor, (x, y))  # 把光標畫上去
    
    pygame.display.update()            #刷新一下畫面
複製代碼

注意: 在退出時使用IPython會報錯windows

runfile('/home/jack/桌面/haozidachaung/Helloworld.py', wdir='/home/jack/桌面/haozidachaung')
An exception has occurred, use %tb to see the full traceback.

SystemExit
複製代碼

我也不知道爲什麼但時間比較緊,我發現命令行下運行沒有問題,就直接用命令行了,之後在解決。後端

幾個簡單的點:

  • set_mode會返回一個Surface對象,表明了在桌面上出現的那個窗口,三個參數第一個爲元祖,表明分 辨率(必須);第二個是一個標誌位,具體意思見下表,若是不用什麼特性,就指定0;第三個爲色深。
標誌位 功能
FULLSCREEN 建立一個全屏窗口
DOUBLEBUF 建立一個「雙緩衝」窗口,建議在HWSURFACE或者OPENGL時使用
HWSURFACE 建立一個硬件加速的窗口,必須和FULLSCREEN同時使用
OPENGL 建立一個OPENGL渲染的窗口
RESIZABLE 建立一個能夠改變大小的窗口
NOFRAME 建立一個沒有邊框的窗口
  • convert函數是將圖像數據都轉化爲Surface對象,每次加載完圖像之後就應該作這件事件(事實上由於 它太經常使用了,若是你不寫pygame也會幫你作);convert_alpha相比convert,保留了Alpha 通道信息(能夠簡單理解爲透明的部分),這樣咱們的光標才能夠是不規則的形狀。數組

  • 遊戲的主循環是一個無限循環,直到用戶跳出。在這個主循環裏作的事情就是不停地畫背景和更新光標位置,雖然背景是不動的,咱們仍是須要每次都畫它, 不然鼠標覆蓋過的位置就不能恢復正常了。app

  • blit是個重要函數,第一個參數爲一個Surface對象,第二個爲左上角位置。畫完之後必定記得用update更新一下,不然畫面一片漆黑。dom

二.事件

理解事件

事件是什麼,其實從名稱來看咱們就能想到些什麼,並且你所想到的基本就是事件的真正意思了。咱們上一個程序,會一直運行下去,直到你關閉窗口而產生了一個QUIT事件,Pygame會接受用戶的各類操做(好比按鍵盤,移動鼠標等)產生事件。事件隨時可能發生,並且量也可能會很大,Pygame的作法是把一系列的事件存放一個隊列裏,逐個的處理。函數

事件檢索

上個程序中,使用了**pygame.event.get()來處理全部的事件,這好像打開大門讓全部的人進入。若是咱們使用pygame.event.wait()Pygame就會等到發生一個事件才繼續下去,就好像你在門的貓眼上盯着外面同樣,來一個放一個……通常遊戲中不太實用,由於遊戲每每是須要動態運做的;而另一個方法pygame.event.poll()**就好一些,一旦調用,它會根據如今的情形返回一個真實的事件,或者一個「什麼都沒有」。下表是一個經常使用事件集:post

事件 產生途徑 參數
QUIT 用戶按下關閉按鈕 none
ATIVEEVENT Pygame被激活或者隱藏 gain, state
KEYDOWN 鍵盤被按下 unicode, key, mod
KEYUP 鍵盤被放開 key, mod
MOUSEMOTION 鼠標移動 pos, rel, buttons
MOUSEBUTTONDOWN 鼠標按下 pos, button
MOUSEBUTTONUP 鼠標放開 pos, button
JOYAXISMOTION 遊戲手柄(Joystick or pad)移動 joy, axis, value
JOYBALLMOTION 遊戲球(Joy ball)?移動 joy, axis, value
JOYHATMOTION 遊戲手柄(Joystick)?移動 joy, axis, value
JOYBUTTONDOWN 遊戲手柄按下 joy, button
JOYBUTTONUP 遊戲手柄放開 joy, button
VIDEORESIZE Pygame窗口縮放 size, w, h
VIDEOEXPOSE Pygame窗口部分公開(expose)? none
USEREVENT 觸發了一個用戶事件 code

若是你想把這個表如今就背下來,固然我不會阻止你,但實在不是個好主意,在實際的使用中,天然而然的就會記住。咱們先來寫一個能夠把全部方法輸出的程序,它的結果是這樣的。

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
SCREEN_SIZE = (640, 480)
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)

font = pygame.font.SysFont("arial", 16);
font_height = font.get_linesize()
event_text = []

while True:
	event = pygame.event.wait()
    event_text.append(str(event))         # 得到事件的名稱
     
    # 這個切片操做保證了列表event_text裏面只保留一個屏幕的文字
    event_text = event_text[-SCREEN_SIZE[1]//font_height:]
    
    if event.type == QUIT:
        exit()
        
    screen.fill((255, 255, 255))          # 全白
    
    #找一個合適的起筆位置,最下面開始可是要留一行的空
    y = SCREEN_SIZE[1] - font_height
    
    for text in reversed(event_text):
        
        screen.blit( font.render(text, True, (0, 0, 0)), (0, y) )   #以後會寫
        
        y -= font_height # 把筆提一行
複製代碼

注:若是你把填充色的(0, 0, 0)改成(0, 255, 0),效果會想黑客帝國的字幕雨同樣,我得說,實際試一下並不太像……不過之後你徹底能夠寫一個以假亂真甚至更酷的!

這個程序在你移動鼠標的時候產生了海量的信息,讓咱們知道了Pygame是多麼的繁忙……咱們第一個程序那樣是調用pygame.mouse.get_pos()來獲得當前鼠標的位置,而如今利用事件能夠直接得到!

處理鼠標事件

MOUSEMOTION事件會在鼠標動做的時候發生,它有三個參數:

  • buttons – 一個含有三個數字的元組,三個值分別表明左鍵、中鍵和右鍵,1就是按下了。
  • pos – 就是位置了……
  • rel – 表明瞭如今距離上次產生鼠標事件時的距離

MOUSEMOTION相似的,咱們還有MOUSEBUTTONDOWNMOUSEBUTTONUP兩個事件,看名字就明白是什麼意思了。不少時候,你只須要知道鼠標點下就能夠了,那就能夠不用上面那個比較強大(也比較複雜)的事件了。它們的參數爲:

  • button – 看清楚少了個s,這個值表明了哪一個按鍵被操做
  • pos – 和上面同樣

處理鍵盤事件

鍵盤和遊戲手柄的事件比較相似,爲KEYDOWNKEYUP,下面有一個例子來演示使用方向鍵移動一些東西。

background_image_filename = '1.jpeg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

x, y = 0, 0
move_x, move_y = 0, 0

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
           exit()
        if event.type == KEYDOWN:
            # 鍵盤有按下?
            
        	if event.key == K_LEFT:
                # 按下的是左方向鍵的話,把x座標減一
                move_x = -1
            elif event.key == K_RIGHT:
                # 右方向鍵則加一
                move_x = 1
            elif event.key == K_UP:
                # 相似了
                move_y = -1
            elif event.key == K_DOWN:
                move_y = 1
                
        elif event.type == KEYUP:
            # 若是用戶放開了鍵盤,圖就不要動了
            move_x = 0
            move_y = 0
            
        # 計算出新的座標
        x += move_x
        y += move_y
        
        screen.fill((0,0,0))
        screen.blit(background, (x,y))
        # 在新的位置上畫圖
        pygame.display.update()
複製代碼

當咱們運行這個程序的時候,按下方向鍵就能夠把背景圖移動,可是等等!爲何我只能按一下動一下啊……太很差試了吧?!用腳掌考慮下就應該按着就一直動下去纔是啊!?Pygame這麼垃圾麼……

哦,真是抱歉上面的代碼有點小bug,可是真的很小,你都不須要更改代碼自己,只要改一下縮進就能夠了,你能夠發現麼?Python自己是縮進編排來表現層次,有些時候可能會出現一點小麻煩,要咱們本身注意才能夠。

KEYDOWNKEYUP的參數描述以下:

  • key – 按下或者放開的鍵值,是一個數字,估計地球上不多有人能夠記住,因此Pygame中你可使用K_xxx來表示,好比字母a就是K_a,還有K_SPACEK_RETURN等。
  • mod – 包含了組合鍵信息,若是mod & KMOD_CTRL是真的話,表示用戶同時按下了Ctrl鍵。相似的還有KMOD_SHIFTKMOD_ALT
  • unicode – 表明了按下鍵的Unicode值,這個有點很差理解,真正說清楚又太麻煩,遊戲中也不太經常使用,說明暫時省略,何時須要再講吧。

事件過濾

並非全部的事件都須要處理的,就好像不是全部登門造訪的人都是咱們歡迎的同樣。好比,俄羅斯方塊就無視你的鼠標,而在遊戲場景切換的時候,你按什麼都是徒勞的。咱們應該有一個方法來過濾掉一些咱們不感興趣的事件(固然咱們能夠不處理這些沒興趣的事件,但最好的方法仍是讓它們根本不進入咱們的事件隊列,就好像在門上貼着「XXX免進」同樣),咱們使用pygame.event.set_blocked(事件名)來完成。若是有好多事件須要過濾,能夠傳遞一個列表,好比pygame.event.set_blocked([KEYDOWN, KEYUP]),若是你設置參數None,那麼全部的事件有被打開了。與之相對的,咱們使用**pygame.event.set_allowed()**來設定容許的事件。

產生事件

一般玩家作什麼,Pygame就產生對應的事件就能夠了,不過有的時候咱們須要模擬出一些事件來,好比錄像回放的時候,咱們就要把用戶的操做再現一遍。

爲了產生事件,必須先造一個出來,而後再傳遞它:

my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=u' ')
# 你也能夠像下面這樣寫,看起來比較清晰(但字變多了……)
my_event = pygame.event.Event(KEYDOWN, {"key":K_SPACE, "mod":0, "unicode":u' '})
pygame.event.post(my_event)
複製代碼

你甚至能夠產生一個徹底自定義的全新事件,有些高級的話題,暫時不詳細說,僅用代碼演示一下:

CATONKEYBOARD = USEREVENT + 1
my_event = pygame.event.Event(CATONKEYBOARD, message="Bad cat!")
pgame.event.post(my_event)

#而後得到它
for event in pygame.event.get():
    if event.type == CATONKEYBOARD:
        print event.message
複製代碼

三.顯示

全屏顯示

咱們在第一個程序裏使用了以下的語句

screen = pygame.display.set_mode((640, 480), 0, 32)
複製代碼

也講述了各個參數的意思,當咱們把第二個參數設置爲FULLSCREEN時,就能獲得一個全屏窗口了

screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)
複製代碼

注:若是你的程序有什麼問題, 極可能進入了全屏模式就不容易退出來了,最好先調試一下,聽哥一句勸。

在全屏模式下,顯卡可能就切換了一種模式,你能夠用以下代碼得到您的機器支持的顯示模式:

>>> import pygame
>>> pygame.init()
>>> pygame.display.list_modes()
[(1920, 1080)]
複製代碼

看下一個實例:

background_image_filename = '1.jpeg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

Fullscreen = False      # 給全屏賦初值

while True:

    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
        if event.type == KEYDOWN:
            if event.key == K_f:
                Fullscreen = not Fullscreen
                if Fullscreen:
                    screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)
                else:
                    screen = pygame.display.set_mode((640, 480), 0, 32)

    screen.blit(background, (0,0))
    pygame.display.update()
複製代碼

運行這個程序,默認仍是窗口的,按「f 」,顯示模式會在窗口和全屏之間切換。程序也沒有什麼難度,應該都能看明白。

可變尺寸的顯示

雖然通常的程序窗口都能拖邊框來改變大小,pygame的默認顯示窗口是不行的,而事實上,不少遊戲確實也不能改變顯示窗口的大小,咱們可使用一個參數來改變這個默認行爲。

background_image_filename = '1.jpeg'

import pygame
from pygame.locals import *
from sys import exit

SCREEN_SIZE = (640, 480)

pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32) # 將參數改成RESIZABLE

background = pygame.image.load(background_image_filename).convert()

while True:

    event = pygame.event.wait()
    if event.type == QUIT:
        exit()
    if event.type == VIDEORESIZE: # 新事件 VIDEORESIZE
        SCREEN_SIZE = event.size
        screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32)
        pygame.display.set_caption("Window resized to "+str(event.size)) # 改變窗口標題

    screen_width, screen_height = SCREEN_SIZE
    
    # 這裏須要從新填滿窗口
    for y in range(0, screen_height, background.get_height()):
        for x in range(0, screen_width, background.get_width()):
            screen.blit(background, (x, y))

    pygame.display.update()
複製代碼

當你更改大小的時候,後端控制檯會顯示出新的尺寸,這裏咱們學習到一個新的事件VIDEORESIZE,它包含以下內容:

  • size — 一個二維元組,值爲更改後的窗口尺寸,size[0]爲寬,size[1]爲高
  • w — 寬
  • h — 一目瞭然,高;之因此多出這兩個,無非是爲了方便

原做者注:注意:在個人Windows 7 64bit上運行的時候,一改變窗口大小就非法退出;在Linux機器上很正常,應該是系統的兼容性問題(Pygame還只支持32位),不過想來平時都不會更改遊戲窗口大小,問題不大。

其餘、複合模式

咱們還有一些其餘的顯示模式,但未必全部的操做系統都支持(放心windows、各類比較流行的Linux發行版都是沒問題的),通常來講窗口就用0全屏就用FULLSCREEN,這兩個老是OK的。

若是你想建立一個硬件顯示(surface會存放在顯存裏,從而有着更高的速度),你必須和全屏一塊兒使用:

screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE | FULLSCREEN, 32)
複製代碼

固然你徹底能夠把雙緩衝(更快)DOUBLEBUF也加上,這就是一個很棒的遊戲顯示了,不過記得你要使用pygame.display.flip()來刷新顯示。pygame.display.update()是將數據畫到前面顯示,而這個是交替顯示的意思。

稍微說一下雙緩衝的意思,能夠作一個比喻:個人任務就是畫黑板報,若是隻有一塊黑板,那我得不停的寫,所有寫完了稍微Show一下就要擦掉重寫,這樣一來別人看的基本都是我在寫黑板報的過程,看到的都是不完整的黑板報;若是我有兩塊黑板,那麼能夠掛一塊給別人看,我本身在底下寫另外一塊,寫好了把原來的換下來換上新的,這樣一來別人基本老是看到完整的內容了。雙緩衝就是這樣維護兩個顯示區域,快速的往屏幕上換內容,而不是每次都慢慢地重畫。

還有OPENGL模式,這是一個獲得普遍應用的3D加速顯示模式。不過一旦使用了這個模式,pygame中的2D圖像函數就不能用了,咱們會在之後講詳細的內容。

四.字體模塊

使用字體模塊

就像上一次說的,一個遊戲,再怎麼寒磣也得有文字,俄羅斯方塊還有個記分數的呢;印象中沒有文字的電子遊戲只有電腦剛剛誕生的那種打乒乓的了。Pygame能夠直接調用系統字體,或者也可使用TTF字體,稍有點電腦知識的都知道這是什麼。爲了使用字體,你得先建立一個Font對象,對於系統自帶的字體:

my_font = pygame.font.SysFont("arial", 16)
複製代碼

第一個參數是字體名,第二個天然就是大小,通常來講「Arial」字體在不少系統都是存在的,若是找不到的話,就會使用一個默認的字體,這個默認的字體和每一個操做系統相關,你也可使用pygame.font.get_fonts()來得到當前系統全部可用字體。還有一個更好的方法的,使用TTF的方法:

my_font = pygame.font.Font("my_font.ttf", 16)
複製代碼

這個語句使用了一個叫作「my_font.ttf」,這個方法之因此好是由於你能夠把字體文件隨遊戲一塊兒分發,避免用戶機器上沒有須要的字體。。一旦你建立了一個font對象,你就可使用render方法來寫字了,而後就能blit()到屏幕上:

text_surface = my_font.render("Pygame is cool!", True, (0,0,0), (255, 255, 255))
複製代碼

第一個參數是寫的文字;第二個參數是個布爾值,是否開啓抗鋸齒,就是說True的話字體會比較平滑,不過相應的速度有一點點影響;第三個參數是字體的顏色;第四個是背景色,若是你想沒有背景色(也就是透明),那麼能夠不加這第四個參數。

下面是一個小例子演示下文字的使用,不過並非顯示在屏幕上,而是存成一個圖片文件。

my_name = "Will McGugan"
import pygame
pygame.init()
my_font = pygame.font.SysFont("arial", 64)
name_surface = my_font.render(my_name, True, (0, 0, 0), (255, 255, 255))
pygame.image.save(name_surface, "name.png")
複製代碼

追加說明一下如何顯示中文, 簡單來講,首先你得用一個可使用中文的字體,宋體、黑體什麼的,或者你直接用中文TTF文件,而後文字使用unicode,即u」中文的文字」這種,最後不要忘了源文件里加上一句關於文件編碼的「魔法註釋」,具體的能夠查一下Python的編碼方面的文章。

注:若是我沒記錯的話,在python3中已經取代了unicode字符串直接用str來代替,也就是說Python3的話能夠沒必要加‘u’

舉一個這樣的例子:

# -*- coding: utf-8 -*-
# 記住上面這行是必須的,並且保存文件的編碼要一致!
import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)

#font = pygame.font.SysFont("宋體", 40)
#上句在Linux可行,在個人Windows 7 64bit上不行,XP不知道行不行
#font = pygame.font.SysFont("simsunnsimsun", 40)
#用get_fonts()查看後看到了這個字體名,在個人機器上能夠正常顯示了
font = pygame.font.Font("方正瘦金體.ttf", 40)
#這句話老是能夠的,因此仍是TTF文件保險啊
text_surface = font.render("你好", True, (0, 0, 255))

x = 0
y = (480 - text_surface.get_height())/2

background = pygame.image.load("1.jpeg").convert()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    screen.blit(background, (0, 0))

    x -= 2                                   # 文字滾動太快的話,改改這個數字
    if x < -text_surface.get_width():        # 注意這是文本的寬度
        x = 640 - text_surface.get_width()   # 思考下是啥意思

    screen.blit(text_surface, (x, y))

    pygame.display.update()
複製代碼

pygame的錯誤處理

程序總會出錯的,好比當內存用盡的時候Pygame就沒法再加載圖片,或者文件根本就不存在。再好比下例:

>>> import pygame
>>> screen = pygame.display.set_mode((640, -1))
---------------------------------
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
pygame.error: Cannot set 0 sized display mode
----------------------------------
複製代碼

對付這種錯誤一個比較好的方法:

try:
    screen = pygame.display.set_mode(SCREEN_SIZE)
except pygame.error, e:
    print "Can't create the display :-("
    print e
    exit()
複製代碼

其實就是Python的標準的錯誤捕捉方法就是了,實際的遊戲(或者程序)中,錯誤捕捉實在過重要了,若是你寫過比較大的應用,應該不用我來講明這一點,Pygame中也是同樣的。

Pygame的基礎就到這裏,後面咱們會進行一些高級的介紹,下一次的話,就開始講畫東西了~

五.遊戲中的視覺——像素

像素的威力

湊近顯示器,你能看到圖像是由一個一個點構成,這就是像素。至於屏幕分辨率的意義,也就不用多說了吧,一個1280×1024的顯示器,有着1310720個像素,通常的32爲RGB系統,每一個像素能夠顯示16.7百萬種顏色,咱們能夠寫一個小程序來顯示這麼多的顏色~

import pygame
pygame.init()

screen = pygame.display.set_mode((640, 480))

all_colors = pygame.Surface((4096,4096), depth=24)

for r in range(256):
    print(r+1, "out of 256")
    x = (r&15)*256
    y = (r>>4)*256
    for g in range(256):
        for b in range(256):
            all_colors.set_at((x+g, y+b), (r, g, b))

pygame.image.save(all_colors, "allcolors.bmp")
複製代碼

色彩的威力

色彩是一個頗有趣的話題,好比把藍色和黃色混合產生綠色,事實上你能夠用紅黃藍混合出全部的顏色(光學三原色),電腦屏幕上的三原色是紅綠藍(RGB),要想更深入的理解這個東西,你得學習一下(就看看李濤的PhotoShop講座吧,VeryCD上有下的,講的仍是很清楚的)~

稍有點經驗的圖像設計者應該看到RGB的數值就能想象出大概的顏色,咱們來用一個Python腳本增強這個認識。

import pygame
from pygame.locals import *
from sys import exit
 
pygame.init()
 
screen = pygame.display.set_mode((640, 480), 0, 32)

def create_scales(height):
    red_scale_surface = pygame.surface.Surface((640, height))
    green_scale_surface = pygame.surface.Surface((640, height))
    blue_scale_surface = pygame.surface.Surface((640, height))
    for x in range(640):
        c = int((x/640.)*255.)
        red = (c, 0, 0)
        green = (0, c, 0)
        blue = (0, 0, c)
        line_rect = Rect(x, 0, 1, height)
        pygame.draw.rect(red_scale_surface, red, line_rect)
        pygame.draw.rect(green_scale_surface, green, line_rect)
        pygame.draw.rect(blue_scale_surface, blue, line_rect)
    return red_scale_surface, green_scale_surface, blue_scale_surface
 
red_scale, green_scale, blue_scale = create_scales(80)  # 返回三個」長條「

color = [127, 127, 127]
 
while True:
 
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
 
    screen.fill((0, 0, 0))
 
    screen.blit(red_scale, (0, 00))
    screen.blit(green_scale, (0, 80))
    screen.blit(blue_scale, (0, 160))
 
    x, y = pygame.mouse.get_pos()
 
    if pygame.mouse.get_pressed()[0]:
        for component in range(3):
            if y > component*80 and y < (component+1)*80:
                color[component] = int((x/639.)*255.)
        pygame.display.set_caption("PyGame Color Test - "+str(tuple(color)))
 
    for component in range(3):
        pos = ( int((color[component]/255.)*639), component*80+40 )
        pygame.draw.circle(screen, (255, 255, 255), pos, 20)
 
    pygame.draw.rect(screen, tuple(color), (0, 240, 640, 240))
 
    pygame.display.update()
複製代碼

這個程序稍稍有點難度了,並且用到了一些沒講到的知識(pygame.draw),咱們之後會介紹,如今無所謂。在這個例子裏,你能夠用鼠標移動三個白點,表明了三原色的量,下面就是不一樣混合獲得的結果,在標題上你能夠看到RGB三個數值。

當咱們有了一個顏色,好比說一顆流星劃過天際,那麼那個時候它是個「火球般的橘黃色」,不過一旦它着地了,它就會滅掉,慢慢變暗,如何能找到比這個「火球般的橘黃色」更暗的顏色?

顏色的縮放

「縮放顏色」並非一種合適的說法,它的準確意義就是上面所說的把顏色變亮或者變暗。通常來講,把顏色的RGB每個數值乘以一個小於1的正小數,顏色看起來就會變暗了(記住RGB都是整數因此可能須要取整一下)。咱們很容易能夠寫一個縮放顏色的函數出來,我就不贅述了。

很天然的能夠想到,若是乘以一個大於1的數,顏色就會變亮,不過一樣要記住每一個數值最多255,因此一旦超過,你得把它歸爲255!使用Python的內置函數min,你能夠方便的作到這事情,也很少說了。若是你乘的數字偏大,顏色很容易就爲變成純白色,就失去了原來的色調。並且RGB也不多是負數,因此謹慎選擇你的縮放係數!

顏色的混合

不少時候咱們還須要混合顏色,好比一個殭屍在路過一個火山熔岩坑的時候,它會由綠色變成橙紅色,再變爲正常的綠色,這個過程必須表現的很平滑,這時候咱們就須要混合顏色。

咱們用一種叫作「線性插值(linear interpolation)」的方法來作這件事情。爲了找到兩種顏色的中間色,咱們將這第二種顏色與第一種顏色的差乘以一個0~1之間的小數,而後再加上第一種顏色就好了。若是這個數爲0,結果就徹底是第一種顏色;是1,結果就只剩下第二種顏色;中間的小數則會皆有二者的特點。

import pygame
from pygame.locals import *
from sys import exit
 
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
 
color1 = (221, 99, 20)
color2 = (96, 130, 51)
factor = 0.
 
def blend_color(color1, color2, blend_factor):
    r1, g1, b1 = color1
    r2, g2, b2 = color2
    r = r1 + (r2 - r1) * blend_factor
    g = g1 + (g2 - g1) * blend_factor
    b = b1 + (b2 - b1) * blend_factor
    return int(r), int(g), int(b)
 
while True:
 
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
 
    screen.fill((255,255,255))
 
    tri = [ (0, 120), (639, 100), (639, 140) ]
    pygame.draw.polygon(screen, (0, 255, 0), tri)
    pygame.draw.circle(screen, (0, 0, 0), (int(factor * 639.0), 120), 10)
 
    x, y = pygame.mouse.get_pos()
    if pygame.mouse.get_pressed()[0]:
        factor = x / 639.0
        pygame.display.set_caption("Pygame Color Blend Test - %.3f" % factor)
 
    color = blend_color(color1, color2 , factor)
    pygame.draw.rect(screen, color, (0, 240, 640, 240))
 
    pygame.display.update()


複製代碼

在這裏例子裏,移動小球你能看到下方的顏色在「火球橙」和「殭屍綠」之間漸變,更改代碼裏的color1和color2,你能看到任意兩種顏色漸變的過程!

六.遊戲中的視覺——圖像

簡述

掌握了小小的像素,咱們可使用更加複雜一點的東西了,對,就是圖像,無數的像素的集合~還記得上次咱們爲了生成的一張圖片,花了無數時間,還好通常遊戲不會在遊戲的過程當中動態生成圖像,都是將畫好的做爲資源封裝到遊戲中。對2D遊戲,圖像可能就是一些背景、角色等,而3D遊戲則每每是大量的貼圖。

雖然是基礎,這裏仍是要羅嗦一下,以前說的RBG圖像,在遊戲中咱們每每使用RGBA圖像,這個A是alpha,也就是表示透明度的部分,值也是0~255,0表明徹底透明,255是徹底不透明,而像100這樣的數字,表明部分透明。你可使用多種軟件建立含有Alpha通道的圖片,具體的網上查查吧……

這個世界上有不少存儲圖像的方式(也就是有不少圖片格式),好比JPEG、PNG等,Pygame都能很好的支持,具體支持的格式以下:

  • JPEG(Join Photograhpic Exper Group),極爲經常使用,通常後綴名爲.jpg或者.jpeg。數碼相機、網上的圖片基本都是這種格式。這是一種有損壓縮方式,儘管對圖片質量有些損壞,但對於減少文件尺寸很是棒。優勢不少只是不支持透明。
  • PNG(Portable Network Graphics)將會大行其道的一種格式,支持透明,無損壓縮。對於網頁設計,軟件界面設計等等都是很是棒的選擇!
  • GIF 網上使用的不少,支持透明和動畫,只是只能有256種顏色,軟件和遊戲中使用不多
  • BMP Windows上的標準圖像格式,無壓縮,質量很高但尺寸很大,通常不使用
  • PCX
  • TGA
  • TIF
  • LBM, PBM
  • XPM

使用Surface對象

對於Pygame而已,加載圖片就是pygame.image.load,給它一個文件名而後就還給你一個surface對象。儘管讀入的圖像格式各不相同,surface對象隱藏了這些不一樣。你能夠對一個Surface對象進行塗畫、變形、複製等各類操做。事實上,屏幕也只是一個surface,pygame.display.set_mode就返回了一個屏幕surface對象。

建立Surfaces對象

一種方法就是剛剛說的pygame.image.load,這個surface有着和圖像相同的尺寸和顏色;另一種方法是指定尺寸建立一個空的surface,下面的語句建立一個256×256像素的surface:

bland_surface = pygame.Surface((256, 256))
複製代碼

若是不指定尺寸,那麼就建立一個和屏幕同樣大小的。

你還有兩個參數可選,第一個是flags

  • HWSURFACE – 相似於前面講的,更快!不過最好不設定,Pygame能夠本身優化。
  • SRCALPHA – 有Alpha通道的surface,若是你須要透明,就要這個選項。這個選項的使用須要第二個參數爲32~

第二個參數是depth,和pygame.display.set_mode中的同樣,你能夠不設定,Pygame會自動設的和display一致。不過若是你使用了SRCALPHA,仍是設爲32吧:

bland_alpha_surface = pygame.Surface((256, 256), flags=SRCALPHA, depth=32)
複製代碼

轉換Surfaces

一般你不用在乎surface裏的具體內容,不過也許須要把這些surface轉換一下以得到更高的性能,還記得一開始的程序中的兩句話嗎:

background = pygame.image.load(background_image_filename).convert()
mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
複製代碼

第一句是普通的轉換,相同於display;第二句是帶alpha通道的轉換。若是你給convert或者conver_alpha一個surface對象做爲參數,那麼這個會被做爲目標來轉換。

矩形對象(Rectangle Objects)

通常來講在制定一個區域的時候,矩形是必須的,好比在屏幕的一部分畫東西。在pygame中矩形對象極爲經常使用,它的指定方法能夠用一個四元素的元組,或者兩個二元素的元組,前兩個數爲左上座標,後兩位爲右下座標。

Pygame中有一個Rect類,用來存儲和處理矩形對象(包含在pygame.locals中,因此若是你寫了from pygame.locals import *就能夠直接用這個對象了),好比:

my_rect1 = (100, 100, 200, 150)
my_rect2 = ((100, 100), (200, 150))
#上兩種爲基礎方法,表示的矩形也是同樣的
my_rect3 = Rect(100, 100, 200, 150)
my_rect4 = Rect((100, 100), (200, 150))
複製代碼

一旦有了Rect對象,咱們就能夠對其作不少操做,好比調整位置和大小,判斷一個點是否在其中等等。之後會慢慢接觸到,求知慾旺盛的能夠在www.pygame.org/docs/ref/re…中找到Rect的詳細信息。

剪裁(Clipping)

一般遊戲的時候你只須要繪製屏幕的一部分。好比魔獸上面是菜單,下面是操做面板,中間的小兵和英雄打的不可開交時候,上下的部分也是保持相對不動的。爲了實現這一點,surface就有了一種叫*裁剪區域(clipping area)*的東西,也是一個矩形,定義了哪部分會被繪製,也就是說一旦定義了這個區域,那麼只有這個區域內的像素會被修改,其餘的位置保持不變,默認狀況下,這個區域是全部地方。咱們可使用set_clip來設定,使用get_clip來得到這個區域。

下面幾句話演示瞭如何使用這個技術來繪製不一樣的區域:

screen.set_clip(0, 400, 200, 600)
draw_map()
#在左下角畫地圖
screen.set_clip(0, 0, 800, 60)
draw_panel()
#在上方畫菜單面板
複製代碼

子表面(Subsurfaces)

Subsurface就是在一個Surface中再提取一個Surface,記住當你往Subsurface上畫東西的時候,同時也向父表面上操做。這能夠用來繪製圖形文字,儘管pygame.font能夠用來寫很不錯的字,但只是單色,遊戲可能須要更豐富的表現,這時候你能夠把每一個字母(中文的話有些吃力了)各自作成一個圖片,不過更好的方法是在一張圖片上畫滿全部的字母。把整張圖讀入,而後再用Subsurface把字母一個一個「摳」出來,就像下面這樣:

my_font_image = Pygame.load("font.png")
letters = []
letters["a"] = my_font_image.subsurface((0,0), (80,80))
letters["b"] = my_font_image.subsurface((80,0), (80,80))
複製代碼

填充Surface

填充有時候能夠做爲一種清屏的操做,把整個surface填上一種顏色:

screen.fill((0, 0, 0))
複製代碼

一樣能夠提供一個矩形來制定填充哪一個部分(這也能夠做爲一種畫矩形的方法)。

設置Surface的像素

咱們能對Surface作的最基本的操做就是設置一個像素的色彩了,雖然咱們基本不會這麼作,但仍是要了解。set_at方法能夠作到這一點,它的參數是座標和顏色,下面的小腳本會隨機的在屏幕上畫點:

import pygame
from pygame.locals import *
from sys import exit
from random import randint  # 用來生成隨機數

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    rand_col = (randint(0, 255), randint(0, 255), randint(0, 255))
    #screen.lock() #很快你就會知道這兩句lock和unlock的意思了
    for _ in range(100):
        rand_pos = (randint(0, 639), randint(0, 479))
        screen.set_at(rand_pos, rand_col)
    #screen.unlock()

    pygame.display.update()
複製代碼

得到Surface上的像素

set_at的兄弟get_at能夠幫咱們作這件事,它接受一個座標返回指定座標點上的顏色。不過記住get_at在對hardware surface操做的時候很慢,而全屏的時候老是hardware的,因此慎用這個方法!

鎖定Surface

當Pygame往surface上畫東西的時候,首先會把surface鎖住,以保證不會有其它的進程來干擾,畫完以後再解鎖。鎖和解鎖時自動發生的,因此有時候可能不那麼有效率,好比上面的例子,每次畫100個點,那麼就得鎖解鎖100次,如今咱們把兩句註釋去掉,再執行看看是否是更快了(好吧,其實我沒感受出來,由於如今的機器性能都不錯,這麼點的差別還不太感受的出來。不過請相信我~複雜的狀況下會影響效率的)?

當你手動加鎖的時候,必定不要忘記解鎖,不然pygame有可能會失去響應。雖然上面的例子可能沒問題,可是隱含的bug是咱們必定要避免的事情。

Blitting

blit的的中文翻譯給人摸不着頭腦的感受,能夠譯爲位塊傳送(bit block transfer),其意義是將一個平面的一部分或所有圖象整塊從這個平面複製到另外一個平面,下面仍是直接使用英文。

blit是對錶面作的最多的操做,咱們在前面的程序中已經屢次用到,很少說了;blit的還有一種用法,每每用在對動畫的表現上,好比下例經過對frame_no的值的改變,咱們能夠把不一樣的幀(同一副圖的不一樣位置)畫到屏幕上:

screen.blit(ogre, (300, 200), (100 * frame_no, 0, 100, 100))
複製代碼

七.圖形繪製

pygame.draw中函數的第一個參數老是一個surface,而後是顏色,再後會是一系列的座標等。稍有些計算機繪圖經驗的人就會知道,計算機裏的座標,(0,0)表明左上角。而返回值是一個Rect對象,包含了繪製的領域,這樣你就能夠很方便的更新那個部分了。

函數 做用
rect 繪製矩形
polygon 繪製多邊形(三個及三個以上的邊)
circle 繪製圓
ellipse 繪製橢圓
arc 繪製圓弧
line 繪製線
lines 繪製一系列的線
aaline 繪製一根平滑的線
aalines 繪製一系列平滑的線

咱們下面一個一個詳細說明。

pygame.draw.rect

用法:pygame.draw.rect(Surface, color, Rect, width=0)

pygame.draw.rect在surface上畫一個矩形,除了surface和color,rect接受一個矩形的座標和線寬參數,若是線寬是0或省略,則填充。咱們有一個另外的方法來畫矩形——fill方法,若是你還記得的話。事實上fill可能還會快一點點,由於fill由顯卡來完成。

pygame.draw.polygon

用法:pygame.draw.polygon(Surface, color, pointlist, width=0)

polygon就是多邊形,用法相似rect,第1、第2、第四的參數都是相同的,只不過polygon會接受一系列座標的列表,表明了各個頂點。

pygame.draw.circle

用法:pygame.draw.circle(Surface, color, pos, radius, width=0)

很簡單,畫一個圓。與其餘不一樣的是,它接收一個圓心座標和半徑參數。

pygame.draw.ellipse

用法:pygame.draw.ellipse(Surface, color, Rect, width=0)

你能夠把一個ellipse想象成一個被壓扁的圓,事實上,它是能夠被一個矩形裝起來的。pygame.draw.ellipse的第三個參數就是這個橢圓的外接矩形。

pygame.draw.arc

用法:pygame.draw.arc(Surface, color, Rect, start_angle, stop_angle, width=1)

arc是橢圓的一部分,因此它的參數也就比橢圓多一點。但它是不封閉的,所以沒有fill方法。start_angle和stop_angle爲開始和結束的角度。

pygame.draw.line

用法:pygame.draw.line(Surface, color, start_pos, end_pos, width=1)

我相信全部的人都能看明白。

pygame.draw.lines

用法:pygame.draw.lines(Surface, color, closed, pointlist, width=1)

closed是一個布爾變量,指明是否須要多畫一條線來使這些線條閉合(感受就和polygone同樣了),pointlist是一個點的數組。

上面的表中咱們還有aaline和aalines,玩遊戲的都知道開出「抗鋸齒(antialiasing)」效果會讓畫面更好看一些,模型的邊就不會是鋸齒形的了,這兩個方法就是在畫線的時候作這事情的,參數和上面同樣,暫時不寫。

import pygame
from pygame.locals import *
from sys import exit

from random import *
from math import pi


pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
points = []

while True:

    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
        if event.type == KEYDOWN:
            # 按任意鍵能夠清屏並把點回復到原始狀態
            points = []
            screen.fill((255,255,255))
        if event.type == MOUSEBUTTONDOWN:
            screen.fill((255,255,255))
            # 畫隨機矩形
            rc = (randint(0,255), randint(0,255), randint(0,255))
            rp = (randint(0,639), randint(0,479))
            rs = (639-randint(rp[0], 639), 479-randint(rp[1], 479))
            pygame.draw.rect(screen, rc, Rect(rp, rs))
        
        	# 畫隨機圓形
            rc = (randint(0,255), randint(0,255), randint(0,255))
            rp = (randint(0,639), randint(0,479))
            rr = randint(1, 200)
            pygame.draw.circle(screen, rc, rp, rr)
            
            # 得到當前鼠標點擊位置
            x, y = pygame.mouse.get_pos()
            points.append((x, y))

            # 根據點擊位置畫弧線
            angle = (x/639.)*pi*2.
            pygame.draw.arc(screen, (0,0,0), (0,0,639,479), 0, angle, 3)
            
            # 根據點擊位置畫橢圓
            pygame.draw.ellipse(screen, (0, 255, 0), (0, 0, x, y))
            
            # 從左上和右下畫兩根線鏈接到點擊位置
            pygame.draw.line(screen, (0, 0, 255), (0, 0), (x, y))
            pygame.draw.line(screen, (255, 0, 0), (640, 480), (x, y))
            
            # 畫點擊軌跡圖
            if len(points) > 1:
                pygame.draw.lines(screen, (155, 155, 0), False, points, 2)
            # 和軌跡圖基本同樣,只不過是閉合的,由於會覆蓋,因此這裏註釋了
            #if len(points) >= 3:
            # pygame.draw.polygon(screen, (0, 155, 155), points, 2)
            
            # 把每一個點畫明顯一點
            for p in points:
                pygame.draw.circle(screen, (155, 155, 155), p, 3)

    pygame.display.update()
複製代碼

到此爲止,最簡單的部分都看完了,我返回來會仔細更改這篇的,畢竟是原博主11年寫的

相關文章
相關標籤/搜索