為自閉症兒童定製的影像檢視遊戲
介紹
我的兒子納特患有自閉症。他喜歡看熟悉的地方、事物和人物的照片。我們買了一臺數碼相機,這樣我們就可以拍攝儘可能多的日常事物照片,而無需擔心膠捲和沖洗的成本和麻煩。納特的品味很難預測,所以我們可能會拍攝一個肯定會讓他高興的主題,但卻發現他對它們根本沒有興趣。我們希望在拍照時能夠廣泛撒網,以確保捕捉到他會感興趣的影像。
一旦有了相機,我們就拍攝了數百張照片,並教納特如何在電腦上使用名為 ThumbsPlus 的現成影像檢視器瀏覽它們。然而,在觀看納特翻閱照片時,我注意到了一些事情:一旦他熟悉了照片的順序,他就會點選照片,好像點選的位置很重要。影像檢視器並不在意:點選圖片上的任何位置都會顯示下一張圖片。但是,如果納特知道下一張照片是當前照片左側的事物,他就會點選影像的左側。如果他知道下一張照片是透過圖片右側的門,他就會點選門。他玩過許多兒童冒險遊戲,例如 Pajama Sam,並且瞭解這種導航方式。即使在不使用該導航方式的程式中,他也開始像它使用了該導航方式一樣使用它。
所以我想,為什麼不做一款以他喜歡的方式工作的影像檢視器呢?為什麼不編寫一個程式,讓我能夠從我們的生活中構建圖片環境,並讓納特以他想要的方式在其中導航呢?

納特世界中的 1500 多張影像之一。使用者介面只是一組方向游標(靠近中心) 放大
選擇 Python
我以前使用 Python 進行小型指令碼專案,並且喜歡它快速啟動的感覺。它將為我提供一個可塑的環境,用於試驗功能,以檢視納特會發現什麼有趣。雖然這是一個僅供我兒子使用的家庭專案,但它與更大、本應更嚴肅的專案存在相同的問題:需求不明確、客戶不可預測以及開發人員資源有限。在選擇開發工具時,我最關心的是我的生產力。由於這是“下班後”的工作,我的時間和注意力會分散,我希望能夠專注於使用者體驗,而不是構建環境、記憶體管理等。
我尋找了現有的軟體,並在 pygame (www.pygame.org) 上找到了許多有趣的示例。Pygame 提供了我需要的用於影像操作和顯示管理的所有功能,並且那裡有一些現有的專案提供了有用的示例。Pyzzle 幾乎提供了我需要的東西,但在內部組織方面不如我希望的那麼好,無法快速試驗新功能。
實施
我使用現有示例來指導我對 pygame 的使用,為我的程式建立了一個新的框架。我的程式,我簡單地命名為“納特的世界”,將提供一個虛擬的世界供探索。它的工作方式將符合納特的期望,提供一個類似於《神秘島》等遊戲的簡單探索環境;點選螢幕左側會向左轉,點選中心會向前移動,依此類推。
最簡單地說,整個環境只是一組節點,每個節點都有一個要顯示的影像,以及一個要導航到的其他連線節點的列表。世界上的一個物理位置由多個節點表示,每個節點對應一個面朝的方向。這些節點合在一起稱為“地點”。隨著時間的推移,此模型被擴充套件以向環境中新增功能,但此簡單模型使我得以啟動。Python 為我構建 Node 類提供了清晰的面向物件基礎,而 pygame 提供了顯示影像、建立游標和收集輸入的基元。
最大的挑戰之一是在組織世界描述的方式之間做出決定。一方面,我可以為世界的結構建立一個描述性語言,並編寫一個直譯器來讀取描述並構建遊戲將執行的記憶體結構。這將是一項相當大的工作,並且需要我正式化世界中可能出現的所有結構。這也意味著在我獲得納特可以實際使用的東西之前會有更長的延遲。另一方面,我可以直接使用 Python 語句來直接構建結構。這將是繁瑣且容易出錯的。
作為一個方便的中間地帶,我使用 Python 語句來構建世界,但大量使用了實用函式和類,為常見結構提供了快捷方式。例如,我的影像都儲存在按日期命名的目錄中,並且我必須為每個節點輸入一個影像檔名。ImgShortcut 類使這變得簡單。定義__call__方法允許我使用 Python 的語法來獲得我想要的效果
class ImgShortcut: def __init__(self, fmt): self.fmt = fmt def __call__(self, arg): return self.fmt % (arg) nov4 = ImgShortcut(r'C:\img\vol3\20011104\dscf%04d.jpg') nov10 = ImgShortcut(r'C:\img\vol3\20011110\dscf%04d.jpg') >>> print nov4(17) c:\img\vol3\20011104\dscf0017.jpg
另一個示例是,世界上的許多地點共享相同的四節點結構:一個用於北檢視,一個用於西檢視,依此類推。使用 Python 的關鍵字引數語法,我能夠編寫一個函式來構建這種常見的地點型別
def nesw(name, **data): """ Make four nodes for n, e, w, s from a location. Keys: images: ni, ei, wi, si. destinations: n, e, w, s. """ # (code omitted)
第一個引數是新節點的基本名稱(節點名稱是透過附加“:n”、“:w”等來建立的)。其餘引數由關鍵字指定:ni 是北節點的影像名稱,n 是北方的節點;wi 是西節點的影像,w 是西方的節點,依此類推。現在,我可以使用簡單的速記語法來建立一個地點
nesw('front43', ni = nov4(17), ei = nov4(18), e = 'allerton_hawthorne:e', si = nov10(381), s = 'hall:s', wi = nov4(16), w = 'markliesl:w' )
由於使用了關鍵字語法,我可以省略不適用於特定地點的引數。現在我有一種幾乎是宣告性的語法,它對於建立常見結構很方便,而無需為新的迷你語言編寫直譯器。而且,如果我遇到需要完整 Python 的不尋常情況,我可以隨時使用它。
世界和支援它的程式並行增長。當我想到新的地點結構的想法時,我會編寫新的實用函式(如 nesw())來輕鬆建立它們。我可以觀看納特玩遊戲,看看他希望它做什麼,並快速新增功能。Python 的許多功能(解釋程式碼、面向物件、垃圾回收、列表和字典)都支援這種快速週轉。
Node 類被擴充套件和子類化以建立新的節點型別。例如,我添加了一個 MenuNode 子類來處理螢幕上的目的地選單。這允許點選汽車,然後選擇您想開車去哪裡。添加了節點之間的過渡,以使效果更加令人愉悅。點選立體聲音響會產生一個要播放的歌曲選單(當然,透過 pygame)。
結果
此時,原始碼的總大小約為 1400 行程式碼,加上 1300 行描述世界的程式碼,目前有 1500 多個節點。
當我向朋友展示納特的世界時,他們總是談論如果我能為其他人提供類似的東西會多麼棒。我考慮過編寫一個 WorldBuilder 應用程式。它將允許非技術人員使用 GUI 來瀏覽影像,並將它們連線到節點世界中。這將是一個很好的構建專案,也是一個更好的擁有專案,因為我自己可以使用它來擴充套件納特的世界。我不知道我是否會有時間和精力在業餘時間構建這樣的東西,但如果我這樣做,我知道 Python 將是勝任這項工作的工具。
關於作者
Ned Batchelder 是馬薩諸塞州布魯克林的軟體工程師。他的妻子和三個兒子都喜歡玩納特的世界。他的網站是 https://www.nedbatchelder.com。