2.3. 編程實現
雖然這個遊戲的功能比較簡單,但是對於新接觸遊戲編程的初學者來說,仍然顯得有些複雜,爲了降低學習難度,原書分5個階段實現這個遊戲,每個階段將建立一個版本,逐步添加功能,最終完成“公主迎聖誕”遊戲。
先建立項目目錄(sdgz-聖誕公主的拼音首字母),然後根據GameZero的固定規則,建立字體、圖像、聲音等素材文件子目錄,將相關文件放在指定目錄下,整個項目文件結構如下圖:
按照前面介紹的編程思路和分階段、多版本的思想編寫這個遊戲程序。在VSCode中,打開sdgz目錄,準備編寫遊戲代碼。
- 配置遊戲資源和搭建遊戲框架
原書的小標題是“加載遊戲資源和搭建遊戲框架”,因爲python的pyglet庫有專門資源管理類pyglet.resource,通過該類的image()、media()方法將資源加載到內存,再通過bit()方法顯示到窗口上。不過GameZero沒有資源管理的概念,因此不必進行資源管理。
在第1個階段,將配置遊戲中使用的各種圖像和聲音資源,並搭建遊戲的基本框架,能夠響應按下回車鍵的鏈盤事件和根據遊戲狀態變化顯示不同的背景圖像。
1)配置遊戲中使用的圖像和聲音
原書新建了一個game_res.py文件用以加載資源文件,GameZero不需要加載資源文件,但是本着資源與邏輯分離的理念,我們還是建立一個文件用來配置相關資源文件。
在根目錄下新建game_res.jl文件,將資源配置寫入該文件:
#圖像資源
welcome_img = "game_welcome.png"
end_img = "game_end.png"
bg_img = "game_play.png"
heart1_img = "heart1.png"
heart2_img = "heart2.png"
heart3_img = "heart3.png"
clipper_img = "剪刀.png"
snowflake_img = "雪花.png"
gift_img = "禮物.png"
princess_img = "公主.png"
#聲音資源
music = "鈴兒響叮噹.mp3"
pop_sound = "pop.wav"
2) 搭建遊戲的基本框
(1) 加載資源配置文件
在julia中,加載另一個代碼文件的方法爲include("source.jl"),這樣文件 source.jl
的內容將在出現 include
調用的模塊的全局作用域內執行。括號中是source.jl
文件的相對路徑。
不過,由於GameZero是通過rungame(“xx.jl”)方法運行遊戲代碼的,相當於遊戲源碼託管給GameZero了,所以不能直接用include("source.jl")加載其他代碼文件,否則GameZero無法識別。好在GameZero給我們提供了加載的方法。
game_include("game_res.jl")
(2) 創建一個記錄遊戲狀態的變量 game_state,並設置初始值爲 0
game_state = 0
說明: 遊戲的 3 個狀態用數字表示: 0 表示等待狀態,1 表示進行狀態,2 表示結束狀態。
(3) 將窗口尺寸設定爲800*600
HEIGHT = 600
WIDTH = 800
(4) 繪製遊戲畫面。根據遊戲狀態 game_state 繪製不同的圖像作爲遊戲背景畫面。現在問題來了,在GameZero裏,窗口背景是由常量BACKGROUND設置的,而常量是不能在程序運行中修改的。當然我們也可以定義一個圖片Actor來充當背景,但是背景的意義就失去了。好在GameZero提供fill函數來更改窗口背景顏色或圖像。
function fill(s::Screen, c::Colorant)
function fill(s::Screen, sf::Ptr{SDL_Surface})
第一個fill函數的參數大家都能理解,一個Screen對象,一個Colorant類型的顏色值。但是第二個fill函數的第二個參數是什麼?其實你如果學過c或C++,應該知道那是一個指針。指向的是一個SDL_Surface類型的對象。不過關於指針的知識已經超過本文的範圍,這裏不再贅述。要吐槽的是,GameZero這裏對調用很不友好,如果直接將圖片路徑作爲參數就比較好,而不需要調用者去將圖片對象轉換成Ptr指針。好在GameZero是開源的,通過對其源碼的研究,我把相關處理代碼提取出來,放到了game_res.jl文件裏,大家只需知道怎麼調用就行。另外還需要用到SimpleDirectMediaLayer.LibSDL2庫,它是SimpleDirectMediaLaye.jl(綁定C++跨平臺庫Simple DirectMedia Layer的Julia庫)的一部分,所以我們還需要安裝SimpleDirectMediaLayer庫(在第二章第一節的擴展閱讀裏,我介紹過julia庫的安裝方法)。
fill方法須在draw()事件裏調用,具體代碼如下:
function draw(g::Game) global game_state screen=g.screen clear() if game_state == 0 fill(screen,image_surface(welcome_img)) elseif game_state == 1 fill(screen,image_surface(bg_img)) else game_state == 2 fill(screen,image_surface(end_img)) end end
注意welcome_img,bg_img,end_img在game_res.jl文件裏已定義。另外,因爲要使用全局變量 game_state,所以在函數或方法中要用 global 關鍵字進行聲明,這一點和python是一致的。
(5) 使用窗口的 on_key_down()事件方法去響應鍵盤按下事件。在遊戲處於等待狀態和結束狀態時,纔會響應用戶按下回車鍵的行爲,將遊戲設定爲進行狀態。 由於遊戲程序沒有完成,爲了方便測試,當用戶按下空格鏈時,遊戲將進入結束狀態:
function on_key_down(g, k) global game_state if k ==Keys.RETURN #13 if game_state != 1 game_state = 1 end end #用於測試遊戲結束的情況 if k == Keys.SPACE game_state = 2 end end
至此,這個程序的第一個版本完成。運行程序,然後按以下流程進行測試。
①遊戲程序啓動後,顯示遊戲歡迎畫面,遊戲處於等待狀態。②按下回車鍵,顯示遊戲進行畫面,遊戲處於進行狀態。③扭安下空格鍵,顯示遊戲結束畫面,遊戲處於結束狀態。④再按下回車鍵,又顯示遊戲進行畫面。如此反覆。
如果程序沒有按上述流程運行,請認真檢查自己編寫的代碼,或者對照本文資源包中提供的源代碼進行檢查。