pygame入門
說明
在學習pygame時,主要參考了目光博客的教程。目光博客
原教程是2011年寫的,年代比較久遠了,使用Python2。我學習時使用python3將代碼重新實現了一遍,同時補充了一些細節。相比較原博客,少了一些實例。
一、pygame的安裝
pip install pygame
- 1
安裝好后,可以用下面的方法確認有沒有安裝成功
>>>import pygame >>>print(pygame.ver) 1.9.3
- 1
- 2
- 3
二、pygame模塊概覽
三、pygame的“hello world”
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = 'image/sushiplate.jpg' mouse_image = 'image/fugu.png' # 初始化pygame,為使用硬件做准備 pygame.init() # 創建了一個窗口 screen = pygame.display.set_mode((640, 480), 0, 32) # 設置窗口標題 pygame.display.set_caption("hello world") # 加載並轉換圖像 background = pygame.image.load(background_image).convert() mouse_cursor = pygame.image.load(mouse_image).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()
](https://i.loli.net/2018/01/25/5a6978dd4f545.png)
set_mode:返回一個Surface對象,代表了桌面上出現的窗口。第一個參數代表分辨率;第二個參數是標志位,如果不需要使用熱河特性,則指定為0;第三個為色深。
標志位 | 功能 |
---|---|
FULLSCREEN | 創建一個全屏窗口 (create a fullscreen display) |
DOUBLEBUF | 創建一個“雙緩沖”窗口,建議在HWSURFACE或者OPENGL時使用( recommended for HWSURFACE or OPENGL) |
HWSURFACE | 創建一個硬件加速的窗口,必須和FULLSCREEN同時使用( hardware accelerated, only in FULLSCREEN) |
OPENGL | 創建一個OPENGL渲染的窗口 (create an OpenGL-renderable display) |
RESIZABLE | 創建一個可以改變大小的窗口 (display window should be sizeable) |
NOFRAME | 創建一個沒有邊框的窗口 (display window will have no border or controls) |
convert:將圖像轉化為Surface對象,每次加載完圖像后就要使用這個函數.
convert_alpha:相比convert,保留了Alpha 通道信息(可以簡單理解為透明的部分),這樣我們的光標才可以是不規則的形狀。可以試試不用convert_alpha()生成的效果。
blit:第一個參數為一個Surface對象,第二個為左上角位置。畫完以后得用update更新,否則畫面一片漆黑。
四、事件
理解事件
我們上一個程序,一直運行直到關閉窗口而產生了一個QUIT事件,Pygame會接受用戶的各種操作(比如按鍵盤,移動鼠標等)產生事件。事件隨時可能發生,而且量也可能會很大,Pygame的做法是把一系列的事件存放一個隊列里,逐個的處理。
事件檢索
上個程序中,使用了pygame.event.get()來處理所有的事件;也可以使用pygame.event.wait(),pygame會等到發生一個時間才繼續下去;另外一個方法pygame.event.poll(),一旦調用,它會根據現在的情形返回一個真實的事件,或者一個“什么都沒有”。下表是一個常用事件集:
事件 | 產生途徑 | 參數 |
---|---|---|
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 |
接下來我們寫一個把所有發生的事件輸出的程序
#!/usr/bin/env python # -*- coding:utf-8 -*- 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("MicrosoftYaHei", 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 pygame.display.update()
結果如下,會將發生的事件列出。在程序中使用wait(),因為這個程序只要在有動作時執行就好了。
處理鼠標事件
MOUSEMOTION事件會在鼠標動作的時候發生,它有三個參數:
- buttons – 一個含有三個數字的元組,三個值分別代表左鍵、中鍵和右鍵,1就是按下了。
- pos – 位置
- rel – 代表了現在距離上次產生鼠標事件時的距離
和MOUSEMOTION類似的,我們還有MOUSEBUTTONDOWN和MOUSEBUTTONUP兩個事件。它們的參數為:
-
button – 這個值代表了哪個按鍵被操作
-
pos – 位置
處理鍵盤事件
鍵盤的事件為KEYDOWN和KEYUP。
下面這個例子演示的是使用方向鍵來移動圖片。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = '../image/sushiplate.jpg' screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).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: 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()
KEYDOWN和KEYUP的參數描述如下:具體描述請點擊這里
- key – 按下或者放開的鍵值,是一個數字,Pygame中可以使用K_xxx來表示,比如字母a就是K_a,還有K_SPACE和K_RETURN等。
- mod – 包含了組合鍵信息,如果mod & KMOD_CTRL是真的話,表示用戶同時按下了Ctrl鍵。類似的還有KMOD_SHIFT,KMOD_ALT。
- unicode – 代表了按下鍵的Unicode值
事件過濾
並不是所有的事件都需要處理。我們使用pygame.event.set_blocked(type)來完成。如果有好多事件需要過濾,可以傳遞一個列表,比如pygame.event.set_blocked([KEYDOWN, KEYUP]),如果你設置參數None,那么所有的事件有被打開了。與之相對的,我們使用pygame.event.set_allowed()來設定允許的事件。
產生事件
通常玩家做什么,Pygame就產生對應的事件就可以了,不過有的時候我們需要模擬出一些事件來,比如錄像回放的時候,我們就要把用戶的操作再現一遍。
為了產生事件,必須先造一個出來,然后再傳遞它:
my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=' ') # 你也可以像下面這樣寫 my_event = pygame.event.Event(KEYDOWN, {"key":K_SPACE, "mod":0, "unicode":' '}) pygame.event.post(my_event)
- 1
- 2
- 3
- 4
Event():Event(type, dict) 或者 Event(type, **attributes),
post():把新的事件放在事件隊列的最后。
也可以產生一個完全自定義的全新事件。
import pygame from pygame.locals import * pygame.init() my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=' ') # my_event = pygame.event.Event(KEYDOWN,{"key":K_SPACE, "mod":0, "unicode":' '}) pygame.event.post(my_event) ############### # 產生一個自定義的全新事件 CATONKEYBOARD = USEREVENT + 1 my_event = pygame.event.Event(CATONKEYBOARD, message="bad act!") pygame.event.post(my_event) # 獲得這個事件 for event in pygame.event.get(): if event.type == CATONKEYBOARD: print( event.message)
五、顯示
全屏顯示
在第一個例子“hello world”中,使用了如下語句
screen = pygame.display.set_mode((640, 480), 0, 32)
- 1
如果把第二個參數設置成FULLSCREEN,就會得到一個全屏顯示的窗口。
在全屏模式下,顯卡可能就切換了一種模式,你可以用如下代碼獲得您的機器支持的顯示模式
>>> import pygame >>> pygame.init() >>> pygame.display.list_modes()
- 1
- 2
- 3
接下來這個程序,按“f鍵實現全屏和窗口之間的切換。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = '../image/sushiplate.jpg' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).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()
改變窗口尺寸
pygame的默認顯示窗口是不支持拖動邊框改變大小的,改變set_mode函數的參數后可以實現。
screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32)
- 1
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = '../image/sushiplate.jpg' SCREEN_SIZE = (640, 480) pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32) background = pygame.image.load(background_image).convert() while True: event = pygame.event.wait() if event.type == QUIT: exit() if event.type == 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 — 高
復合模式
screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE | FULLSCREEN, 32)
- 1
其他
當使用OPENGL時,不能使用pygame.display.update()來更新窗口,而是pygame.display.flip()。
flip和update的說明如下:
六、字體
創建font對象
Pygame可以直接調用系統字體,或者也可以使用TTF字體。
-
SysFont(name, size, bold=False, italic=False)
my_font = pygame.font.SysFont("arial", 16)
- 1
第一個參數是字體名,第二個是大小。該函數返回一個系統字體,這個字體與“bold”和“italic”兩個flag相匹配。如果找不到,就會使用pygame的默認字體。可以使用pygame.font.get_fonts()來獲得當前系統所有可用字體。
-
Font(filename, size) 或者Font(object, size)
my_font = pygame.font.Font("simsun.ttf", 16)
- 1
使用這個方法,需要把字體文件隨同游戲一起發送,這樣可以避免使用者機器上沒有所需的字體
使用字體
render(text, antialias, color, background=None)
text_surface = my_font.render("Pygame is cool!", True, (0,0,0), (255, 255, 255))
- 1
第一個參數文字;第二個參數是個布爾值,表示是否開啟抗鋸齒,如果為True,字體會比較平滑,不過相應的速度有一點點影響;第三個參數是字體的顏色;第四個是背景色,如果你想沒有背景色(也就是透明),就不加這第四個參數。
中文字體
下面這個例子演示了用pygame書寫中文
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background_image = 'image/sushiplate.jpg' background = pygame.image.load(background_image).convert() # 以下兩種方法都可以,第一種需要把字體文件復制到代碼文件目錄下 font = pygame.font.Font("simsun.ttc", 40) # font = pygame.font.SysFont("simsunnsimsun", 40) text_surface = font.render("你好", True, (0, 0, 255)) x = 0 y = (480 - text_surface.get_height())/2 while True: for event in pygame.event.get(): if event.type == QUIT: exit() screen.blit(background, (0, 0)) x -= 1 if x < -text_surface.get_width(): x = 640 - text_surface.get_width() screen.blit(text_surface, (x, y)) pygame.display.update()
七、顏色
一般的32位RGB,每個像素可以顯示16.7百萬種顏色。
第一個例子,可以生成所有的顏色。
#!/usr/bin/env python # -*- coding:utf-8 -*- 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")
第二個例子,用鼠標移動三個點,代表三原色的值,下方是三原色混合得到的結果。
#!/usr/bin/env python # -*- coding:utf-8 -*- 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, 0)) 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()
八、圖像
使用Surface對象
加載圖片用pygame.image.load,返回一個Surface對象。事實上,屏幕也只是一個surface對象,pygame.display.set_mode返回一個屏幕的surface對象。
創建Surface對象
除了上面說的pygame.image.load外,還可以指定尺寸創建一個空的surface。
>>> a = pygame.Surface((256,256))
這個Surface對象是全黑的。除了大小外,Surface函數還有flags和depth兩個參數。
- HWSURFACE – creates the image in video memory
- SRCALPHA – the pixel format will include a per-pixel alpha。創建有Alpha通道的surface,選擇這個選項需要depth為32。
alpha_surface = pygame.Surface((256,256), flags=SRCALPHA, depth=32)
轉換Surfaces
convert(): Creates a new copy of the Surface with the pixel format changed. 當一個surface多次使用blit時,最好使用convert。轉換后的surface沒有alpha。
convert_alpha(): change the pixel format of an image including per pixel alphas.
矩形對象(Rectangle Objects)
pygame中有Rect類,用來存儲和處理矩形對象(包含在pygame.locals)中。
Rect(left, top, width, height)
Rect((left, top), (width, height))
有了Rect對象之后,可以對其做很多操作,例如調整大小、位置,判斷一個點是否在其中等。
剪裁(Clipping)
surface中有裁剪區域(clip area),是一個矩形,定義了哪部分會被繪制,即若定義了這個區域,只有這個區域內的像素會被修改。
set_clip(screen_rect=None): 設定區域,當參數為None時,重置。一個surface對象默認的剪裁區域為這個surface。
get_clip() : 得到剪裁區域,返回一個Rect對象。
子表面(Subsurfaces)
Subsurfaces是在一個surface中再提取出一個surface。當在subsurface上操作時,同時也向父表面上操作。這可以用來繪制圖形文字,比如吧文字變成多種顏色。把整張圖讀入后,用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
fill(color, rect=None, special_flags=0)
當rect參數為默認參數時,整個surface都會被填充。color參數可以為RGB或者RGBA。如果使用RGBA,除非surface有alpha通道(使用了SRCALPHA flag),否則RGBA的Alpha會被忽略。
設置Surface的像素
set_at((x, y), Color) : 設置單個像素的顏色
get_at((x, y)) : 得到單個像素的顏色
鎖定Surface
當對像素進行讀或寫操作時,surface會被鎖定。一個鎖定的surface,經常不能被顯示或被pygame操作,所以除非必要,在手動lock之后不要忘了unlock。
所有pygame的函數如過需要鎖定和解鎖,這些操作時自動發生的。如果不想發生自動的lock和unlock(有些時候為了提高效率),可以在一些會造成自動鎖定和解鎖的語句前后注釋掉這兩句。
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)() for _ in range(100): rand_pos = (randint(0, 639), randint(0, 479)) screen.set_at(rand_pos, rand_col) # screen.unlock() pygame.display.update()
blit
blit(source, dest, area=None, special_flags = 0)
將源圖像畫到目標位置,dest可以為一個點,也可以是一個矩形,但只有矩形的左上角會被使用,矩形的大小不會造成影響。
area參數可以指定源圖像中的一部分被畫到目標位置。
九、繪制各種圖形
pygame使用pygame.draw來繪制圖形。其包含以下幾種函數。
函數 | 作用 | 用法 |
---|---|---|
rect | 繪制矩形 | rect(Surface, color, Rect, width=0) |
polygon | 繪制多邊形 | polygon(Surface, color, pointlist, width=0) |
circle | 繪制圓 | circle(Surface, color, pos, radius, width=0) |
ellipse | 繪制橢圓 | ellipse(Surface, color, Rect, width=0) |
arc | 繪制圓弧 | arc(Surface, color, Rect, start_angle, stop_angle, width=1) |
line | 繪制線 | line(Surface, color, start_pos, end_pos, width=1) |
lines | 繪制一系列的線 | lines(Surface, color, closed, pointlist, width=1) |
aaline | 繪制一根平滑的線 | aaline(Surface, color, startpos, endpos, blend=1) |
aalines | 繪制一系列平滑的線 | aalines(Surface, color, closed, pointlist, blend=1) |
一些說明
-
width參數:width參數為0或省略,則填充。
-
畫填充的矩形,有另一個方法Surface.fill(),事實上,這種方法速度更快。
- lines函數的closed為一個布爾變量,如果closed為真,則會畫一條連接第一個和最后一個點的線,是整個圖形閉合。
十、運動
直線運動
下面這個程序讓“hello world”程序中的魚動起來。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) # sprite的起始坐標 x = 0 while True: for event in pygame.event.get(): if event.type == QUIT: exit() screen.blit(background, (0, 0)) screen.blit(sprite, (x, 100)) x += 1 if x>640: x = 0 pygame.display.update()
時間
在上面的程序中,幀率是很高的。而且電腦的性能不同,魚的速度就會不同,如果動畫的的元素很多,速度就會下降。
為了解決這個問題,可以使用pygame的時間模塊。
clock = pygame.time.Clock()
time_passed = clock.tick()
time_passed = clock.tick(30)
第一行初始化了一個Clock對象。第二行返回了距上一次調用這個函數,過去了多長時間(注意,得到的值是以毫秒為單位的)。第三行,在函數中添加了framerate參數,這個函數會延時使得游戲的幀率不會超過給定值。
給定的值僅僅是最大幀率,當動作復雜或機器性能不足時,實際幀率無法達到這個值,需要一種手段控制動畫效果。比如給吾提一個恆定的速度,再通過時間,計算出移動的距離。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) # clock對象 clock = pygame.time.Clock() x = 0 speed = 250 while True: for event in pygame.event.get(): if event.type == QUIT: exit() screen.blit(background, (0, 0)) screen.blit(sprite, (x, 100)) time_passed = clock.tick() time_passed_seconds = time_passed/1000 distance_moved = time_passed_seconds * speed x += distance_moved if x > 640: x -= 640 pygame.display.update()
斜線運動
接下來這個程序,使得物體斜線運動並且觸邊反彈。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) clock = pygame.time.Clock() x, y = 100, 100 speed_x, speed_y = 133, 170 while True: for event in pygame.event.get(): if event.type == QUIT: exit() screen.blit(background, (0, 0)) screen.blit(sprite, (x, y)) time_passed = clock.tick(30) time_passed_seconds = time_passed/1000 x += speed_x * time_passed_seconds y += speed_y * time_passed_seconds # 到達邊界后速度反向 if x > 640 - sprite.get_width(): speed_x = -speed_x x = 640 - sprite.get_width() elif x < 0: speed_x = -speed_x x = 0 if y > 480 - sprite.get_height(): speed_y = -speed_y y = 480 - sprite.get_height() elif y < 0: speed_y = -speed_y y = 0 pygame.display.update()
向量
下面這個例子,使用向量代替之前的x和y的計算,實現了魚在鼠標周圍游動的效果。
使用向量類來存儲和計算向量。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit from vector import Vec2d background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) clock = pygame.time.Clock() position = Vec2d(100, 100) heading = Vec2d((0, 0)) while True: for event in pygame.event.get(): if event.type == QUIT: exit() screen.blit(background, (0, 0)) screen.blit(sprite, position) time_passed = clock.tick(30) time_passed_seconds = time_passed/1000 # 在參數前面加*意味着把列表或元組展開 destination = Vec2d(*pygame.mouse.get_pos()) - Vec2d(*sprite.get_size())/2 # 計算當魚兒當前位置到鼠標位置的向量 vector_to_mouse = Vec2d.__sub__(destination, position) vector_to_mouse.normalized() # heading可以看做是魚的速度,魚的速度大小、方向不斷改變 heading = heading + (vector_to_mouse * 0.1) position += heading * time_passed_seconds pygame.display.update()
十一、用戶輸入
鍵盤輸入
在之前的例子中使用過鍵盤輸入,使用pygame.event.get()獲取所有事件,當event.type==KEYDOWN時,再判斷event.key的種類。也可以使用pygame.key.get_pressed()來獲取所有按下的鍵值,它會返回一個元組,這個元祖的索引就是鍵值,對應的值為True就是按下。
但是,如果要處理文本輸入,這個函數不是正確的方法。因為我們不知道按鍵被按下的順序。
key模塊下還有如下的函數:
-
key.get_focused —— 返回當前的pygame窗口是否激活
-
key.get_mods —— 按下的組合鍵(Alt, Ctrl, Shift)
if pygame.key.get_mods() & KMOD_SHIFT:
- 1
-
key.set_mods —— 你也可以模擬按下組合鍵的效果(KMOD_ALT, KMOD_CTRL, KMOD_SHIFT)
-
key.set_repeat —— 參數為(delay,interval)。當有參數時,即repeat被激活時,被按住的鍵會產生多次KEYDOWN事件。第一次發送KEYDOWN事件后,經過delay時間(ms)發送第二次,然后每隔interval時間(ms)發送一次事件。如果沒有參數,不產生重復按鍵事件。當pygame初始化之后,重復按鍵默認為disabled
-
key.name —— 接受鍵值返回鍵名
使用鍵盤控制方向
下面這個例子使用ASDW控制方向。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit from vector import Vec2d background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) clock = pygame.time.Clock() sprite_pos = Vec2d(200, 150) sprite_speed = 300 while True: for event in pygame.event.get(): if event.type == QUIT: exit() pressed_keys = pygame.key.get_pressed() key_direction = Vec2d(0, 0) if pressed_keys[K_LEFT]: key_direction.x = -1 elif pressed_keys[K_RIGHT]: key_direction.x = +1 if pressed_keys[K_UP]: key_direction.y = -1 elif pressed_keys[K_DOWN]: key_direction.y = +1 key_direction.normalized() screen.blit(background, (0, 0)) screen.blit(sprite, sprite_pos) time_passed = clock.tick(30) time_passed_seconds = time_passed/1000 sprite_pos += key_direction * sprite_speed * time_passed_seconds pygame.display.update()
接下來這個例子,使魚做到全方位移動。先轉向,再移動。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit from vector import Vec2d from math import * background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) clock = pygame.time.Clock() sprite_pos = Vec2d(200, 150) sprite_speed = 300 sprite_rotation = 0 sprite_rotation_speed = 360 while True: for event in pygame.event.get(): if event.type == QUIT: exit() pressed_keys = pygame.key.get_pressed() rotation_direction = 0 movement_direction = 0 # 更改角度 if pressed_keys[K_LEFT]: rotation_direction = +1 elif pressed_keys[K_RIGHT]: rotation_direction = -1 # 前進、后退 if pressed_keys[K_UP]: movement_direction = +1. if pressed_keys[K_DOWN]: movement_direction = -1. screen.blit(background, (0, 0)) # 將魚轉向 rotated_sprite = pygame.transform.rotate(sprite, sprite_rotation) # 轉向后,圖片的長寬會變化,因為圖片永遠是矩形,為了放得下一個轉向后的矩形,外接的矩形勢必會比較大 w, h = rotated_sprite.get_size() sprite_draw_pos = Vec2d(sprite_pos.x - w / 2, sprite_pos.y - h / 2) screen.blit(rotated_sprite, sprite_draw_pos) time_passed = clock.tick(30) time_passed_seconds = time_passed/1000 # 圖片的轉向速度也通過時間來 控制 sprite_rotation += rotation_direction * sprite_rotation_speed * time_passed_seconds # 獲得前進(x方向和y方向) heading_x = sin(sprite_rotation * pi / 180) heading_y = cos(sprite_rotation * pi / 180) # 轉換為單位速度向量 heading = Vec2d(heading_x, heading_y) # 轉換為速度 heading *= movement_direction sprite_pos += heading * sprite_speed * time_passed_seconds pygame.display.update()
鼠標控制
我們可以從MOUSEMOTION和pygame.mouse.get_pos()獲得鼠標坐標。我們可以使用這個坐標來控制方向,如使用鼠標的偏移量來控制視角。在接下來的這個例子中,使用鼠標左右移動來轉動魚。
#!/usr/bin/env python # -*- coding:utf-8 -*- import pygame from pygame.locals import * from sys import exit from vector import Vec2d from math import * background_image = '../image/sushiplate.jpg' sprite_image = '../image/fugu.png' pygame.init() screen = pygame.display.set_mode((640, 480), 0, 32) background = pygame.image.load(background_image).convert() sprite = pygame.image.load(sprite_image) clock = pygame.time.Clock() pygame.mouse.set_visible(False) # 使所有的輸入都鎖定在這個程序中 pygame.event.set_grab(True) sprite_pos = Vec2d(200, 150) sprite_speed = 300 sprite_rotation = 0 sprite_rotation_speed = 360 while True: for event in pygame.event.get(): if event.type == QUIT: exit() # 按Esc,退出游戲 if event.type == KEYDOWN: if event.key == K_ESCAPE: exit() pressed_keys = pygame.key.get_pressed() pressed_mouse = pygame.mouse.get_pressed() rotation_direction = 0 movement_direction = 0 # 得到鼠標的偏移量(x, y) rotation_direction = pygame.mouse.get_rel()[0]/5 if pressed_keys[K_LEFT]: rotation_direction = -1 elif pressed_keys[K_RIGHT]: rotation_direction = +1 if pressed_keys[K_UP] or pressed_mouse[0]: movement_direction = -1 if pressed_keys[K_DOWN] or pressed_mouse[2]: movement_direction = +1 screen.blit(background, (0, 0)) rotated_sprite = pygame.transform.rotate(sprite, sprite_rotation) w, h = rotated_sprite.get_size() sprite_draw_pos = Vec2d(sprite_pos.x - w / 2, sprite_pos.y - h / 2) screen.blit(rotated_sprite, sprite_draw_pos) time_passed = clock.tick(30) time_passed_seconds = time_passed/1000 sprite_rotation += rotation_direction * sprite_rotation_speed * time_passed_seconds heading_x = sin(sprite_rotation * pi / 180) heading_y = cos(sprite_rotation * pi / 180) heading = Vec2d(heading_x, heading_y) heading *= movement_direction sprite_pos += heading * sprite_speed * time_passed_seconds pygame.display.update()
pygame.mouse的函數:
-
pygame.mouse.get_pressed —— 返回按鍵按下情況,返回的是一元組,分別為(左鍵, 中鍵, 右鍵),如按下則為True
-
pygame.mouse.get_rel —— 返回相對偏移量,(x方向, y方向)的一元組
-
pygame.mouse.get_pos —— 返回當前鼠標位置(x, y)
-
pygame.mouse.set_pos —— 設置鼠標位置
-
pygame.mouse.set_visible —— 設置鼠標光標是否可見
-
pygame.mouse.get_focused —— 檢查窗口是否接受鼠標事件,即鼠標是否focus到窗口
-
pygame.mouse.set_cursor —— 設置鼠標光標式樣
-
pyGame.mouse.get_cursor ——得到鼠標圖片
版本信息
1.0 20180209
--------------------- 本文來自 FengF2017 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/fengf2017/article/details/79300801?utm_source=copy