今天,我們將描述內建操縱桿的使用 — 繼續我們的Sense HAT系列!
該系列之前的文章如下所示,您可以隨時查閱:
用於Raspberry Pi的Sense HAT擴展板 — 操作LED顯示幕
用於Raspberry Pi的Sense HAT擴展板 — 六種感測器
Sense HAT專為Astro Pi任務而設計,內建操縱桿是其中一個輸入裝置。
該操縱桿位於Sense HAT上的Raspberry Pi標誌旁邊,如上圖所示。操縱桿手柄有點小(尤其是您的手很大時),但是,手柄控制非常容易。它就像遊戲機控制器上的其他操縱桿一樣(但很小)。
這次我們將展示如何透過Python語言接收這個操縱桿的輸入。我們還會使用LED顯示幕設計相關遊戲。
Sense HAT操縱桿映射到四個鍵盤游標鍵,操縱桿中鍵按一下映射為Return(返回)鍵。這意味著行動操縱桿與按下對應鍵的效果完全相同。請記住,下向是指HDMI埠朝下的方向。
操縱桿的方向如下圖所示。HDMI埠朝下。
除了四個方向(即向上、向下、向左和向右)之外,您還可以直接向下按下操縱桿,因此可以說總共有5個按鍵。
好了,我們啟動IDEL,然後運行第一個範例代碼。
/home/pi/nas/keyboad_mapping.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import pygame from pygame.locals import * from sense_hat import SenseHat pygame.init() pygame.display.set_mode((640, 480)) sense = SenseHat() sense.clear() running = True while running: for event in pygame.event.get(): print(event) if event.type == QUIT: running = False print("BYE") |
代碼執行時,系統會啟動一個名為“pygame window”的黑屏。這是我們程式6-7行調用的 pygame 程式館窗口。
Pygame 是一個由多個Python模組組成的跨平臺套件,專為視訊遊戲編寫而設計。它包括電腦佈局程式館和聲音程式館,旨在與Python程式設計語言一起使用。
這會獲取所有消息並將其從佇列中刪除。如果給定類型或類型序列,那麼系統只會從佇列中刪除這些類型的消息。
程式第14行中的“pygame.event.get()”會檢查該事件,第15行負責列印事件內容。
如果將滑鼠移到“pygame視窗”上或按鍵盤上的鍵,“事件”內容將顯示在控制台螢幕上。這同樣可以檢測來自Sense HAT操縱桿的輸入事件。
我按照順時針方向進行操作,並且在操縱桿和鍵盤之間交替操作(比如操縱桿-向上、鍵盤-向上、操縱桿-向右、鍵盤-向右等)。您可以看到兩種輸入方法顯示相同的事件內容(圖 3)。
操縱桿不僅可以向4個方向傾斜,還可以豎直按下,就像鍵盤上的“Enter”鍵一樣。
此外,每個輸入操作都會生成兩種類型的事件“KYEDOWN”和“KEYUP”。很簡單,按下操縱桿一段時間,您就會理解事件發生的時序。
在 keyboad_mapping.py 中,pygame自身視窗無法斷路,因為根據程式設計,該視窗要顯示“BYE”並在視窗斷路時終止處理(第16到18行)。如果要退出,斷路IDLE控制台螢幕即可。
現在我們連接LED矩陣顯示器,以顯示操縱桿的動作!
我們將編寫一個程式,使得操縱桿行動時只點亮特定的矩陣。
具體過程大致分為兩部分:
如果同時執行這兩個過程,那麼就可以根據操縱桿的行動將光線掠過LED矩陣。
要單獨控制LED,請參閱 用於Raspberry Pi的Sense HAT擴展板 — 操作LED顯示幕介紹的“set_pixel”功能。
將指定XY座標處的單個LED矩陣像素設定為指定的顏色。
第一個參數是X座標(0-7),第二個參數是Y座標(0-7),而第三個參數可以指定顏色。您可以使用以下代碼打開/斷路矩陣:
1 2 3 4 |
#Lights up (white) sense.set_pixel(0, 0, 255, 255, 255) #Lights off (black) sense.set_pixel(0, 0, 0, 0, 0) |
LED顯示幕的座標起始於左上角——此處的座標為0,如圖5所示。操縱桿的動作以及X座標和Y座標的變化如下圖所示。
操縱桿動作 | 座標變化 |
向上 | y-1 |
向下 | y+1 |
向左 | x-1 |
向右 | x+1 |
座標值必須分別設定在0和7之間。如果設定了無效座標,那麼會出現以下錯誤。
ValueError: Y position must be between 0 and 7
以下範例代碼解決了這個錯誤,因此可以避免該錯誤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import pygame from pygame.locals import * from sense_hat import SenseHat pygame.init() pygame.display.set_mode((640, 480)) sense = SenseHat() sense.clear() running = True x = 0 y = 0 sense.set_pixel(x, y, 255, 255, 255) while running: for event in pygame.event.get(): if event.type == KEYDOWN: sense.set_pixel(x, y, 0, 0, 0) # Black 0,0,0 means OFF if event.key == K_DOWN and y < 7: y = y + 1 elif event.key == K_UP and y > 0: y = y - 1 elif event.key == K_RIGHT and x < 7: x = x + 1 elif event.key == K_LEFT and x > 0: x = x - 1 sense.set_pixel(x, y, 255, 255, 255) if event.type == QUIT: running = False print("BYE") |
第16行是初始過程。首先點亮左上方的LED(X座標0,Y座標0)。
當發生輸入時,程式首先斷路當前位置的LED,如第21行所示。然後程式會決定座標是否處於有效範圍內(從0到7),第32行負責點亮目標LED(第23到30行)。如果設定了無效值(比如,嘗試從矩陣右端進一步向右行動),看起來沒有行動,其實系統只是斷路並打開相同的LED。
Sense HAT Pong | Raspberry Pi 學習資源
工作表 – Sense HAT Pong | Raspberry Pi 學習資源
Pong 是一款老式的復古遊戲。在該遊戲中,玩家用一個球拍/條塊桿擊打行動的球。
遊戲可以使用傳統的鍵盤操作,但我想自訂一個程式,以便可以用操縱桿玩遊戲。
我們首先啟動遊戲,然後再進行客製化。完成的來源程式請參見第8章末尾。
/home/pi/nas/pong.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
from time import sleep from sense_hat import SenseHat import curses import threading sense = SenseHat() sense.clear(0,0,0) screen = curses.initscr() screen.keypad(True) curses.cbreak() curses.noecho() y = 4 ball_position=[6,3] ball_speed=[-1,-1] def drawbat(): sense.set_pixel(0,y,255,255,255) sense.set_pixel(0,y+1,255,255,255) sense.set_pixel(0,y-1,255,255,255) def moveball(): global game_over while True: sleep(0.15) sense.set_pixel(ball_position[0],ball_position[1],0,0,0) ball_position[0] += ball_speed[0] ball_position[1] += ball_speed[1] if ball_position[1] == 0 or ball_position[1] == 7: ball_speed[1] = -ball_speed[1] if ball_position[0] == 7: ball_speed[0] = -ball_speed[0] if ball_position[0] == 1 and y-1 <= ball_position[1] <= y+1: ball_speed[0] = -ball_speed[0] if ball_position[0] == 0: break sense.set_pixel(ball_position[0],ball_position[1],0,0,255) game_over = True game_over = False thread = threading.Thread(target=moveball) thread.start() while not game_over: drawbat() key = screen.getch() sense.clear() if key == curses.KEY_UP: if y > 1: y -= 1 if key == curses.KEY_DOWN: if y < 6: y += 1 sense.show_message("You Lose", text_colour=(255,0,0)) screen.keypad(0) curses.nocbreak() curses.echo() curses.endwin() |
第3行導入的“Curses”負責檢測鍵盤輸入。由於curses似乎不能與IDEL一起使用,所以我們從命令列運行它。
運行命令
python3 /home/pi/nas/pong.py
使用鍵盤的向上和向下鍵行動左側的白光條塊以擊中藍色球。如果沒有將球反彈回去,矩陣會顯示“You Lose”(你輸了),遊戲就會結束。現在,球以0.15秒的間隔行動(第26行),速度非常快,遊戲異常困難!
我們來調整一下這個程式,以便可以使用操縱桿進行操作。
/home/pi/nas/pong_joy.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
from time import sleep from sense_hat import SenseHat import pygame from pygame.locals import * import threading sense = SenseHat() sense.clear(0,0,0) pygame.init() pygame.display.set_mode((320, 240)) y = 4 ball_position=[6,3] ball_speed=[-1,-1] def drawbat(): sense.set_pixel(0,y,255,255,255) sense.set_pixel(0,y+1,255,255,255) sense.set_pixel(0,y-1,255,255,255) def moveball(): global game_over while True: sleep(0.5) sense.set_pixel(ball_position[0],ball_position[1],0,0,0) ball_position[0] += ball_speed[0] ball_position[1] += ball_speed[1] if ball_position[1] == 0 or ball_position[1] == 7: ball_speed[1] = -ball_speed[1] if ball_position[0] == 7: ball_speed[0] = -ball_speed[0] if ball_position[0] == 1 and y-1 <= ball_position[1] <= y+1: ball_speed[0] = -ball_speed[0] if ball_position[0] == 0: break sense.set_pixel(ball_position[0],ball_position[1],0,0,255) game_over = True game_over = False thread = threading.Thread(target=moveball) thread.start() while not game_over: drawbat() for event in pygame.event.get(): if event.type == KEYDOWN: if event.key == K_UP and y > 1: sense.set_pixel(0,y+1,0,0,0) y -= 1 if event.key == K_DOWN and y < 6: sense.set_pixel(0,y-1,0,0,0) y += 1 sense.show_message("You Lose", text_colour=(255,0,0)) |
運行命令
python3 /home/pi/nas/pong_joy.py
由於pygame與SSH連接或遠端桌面連接不相容,所以我們直接從命令列運行pygame。
我用pygame程式館替換了curses程式館,並對其進行了更改,以接收操縱桿的輸入(您也可以使用鍵盤上的向上和向下鍵)。
主要變化包括:第3-4行中的pygame調用部分;第10-11行中pygame的初始處理;以及第53-60行中輸入值的判斷。
我嘗試將速度改為0.5秒,以便更容易地預測球的運動情況。球變慢後,“sense.clear()”引起的閃爍變得明顯(原程式:第55行),因此我們僅重新編寫了必要部分。
內建操縱桿肯定是Sense HAT板的最大特色之一。說起Raspberry Pi的輸入裝置,外接的裝置很常見,但是內建裝置很少見,因此搭載操縱桿的Sense HAT在眾多Pi模型中脫穎而出。您還可以將按鈕連至GPIO引腳。
在本教程中,我們重建了一個Pong遊戲,以便使用內建操縱桿來行動球拍並擊球。雖然顯示器非常小,但我認為在小型LED矩陣上玩像Pong這樣的遊戲非常獨特有意思。您可以改變球的速度,這樣就能獲得更高的分數!