pygame入門


pygame入門

說明

在學習pygame時,主要參考了目光博客的教程。目光博客

原教程是2011年寫的,年代比較久遠了,使用Python2。我學習時使用python3將代碼重新實現了一遍,同時補充了一些細節。相比較原博客,少了一些實例。

pygame官網文檔

文中代碼github

一、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()

 

hello world.png](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(),因為這個程序只要在有動作時執行就好了。

event.png

處理鼠標事件

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

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

和MOUSEMOTION類似的,我們還有MOUSEBUTTONDOWNMOUSEBUTTONUP兩個事件。它們的參數為:

  • button – 這個值代表了哪個按鍵被操作

  • pos – 位置

處理鍵盤事件

鍵盤的事件為KEYDOWNKEYUP

下面這個例子演示的是使用方向鍵來移動圖片。

#!/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_SPACEK_RETURN等。
  • mod – 包含了組合鍵信息,如果mod & KMOD_CTRL是真的話,表示用戶同時按下了Ctrl鍵。類似的還有KMOD_SHIFTKMOD_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的說明如下:

flip.png

update.png

六、字體

創建font對象

Pygame可以直接調用系統字體,或者也可以使用TTF字體。

  1. SysFont(name, size, bold=False, italic=False)

    my_font = pygame.font.SysFont("arial", 16)
    • 1

    第一個參數是字體名,第二個是大小。該函數返回一個系統字體,這個字體與“bold”和“italic”兩個flag相匹配。如果找不到,就會使用pygame的默認字體。可以使用pygame.font.get_fonts()來獲得當前系統所有可用字體。

  2. 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM