蘇雄,張宏橋,段凌飛,曾曉華
(湘南學院電子信息與電氣工程學院,郴州423000)
C語言是較為底層的高級語言,由于其標準簡單規(guī)范,在多個領域均較為流行[1-2]。出于教學目的,大部分學生在學習完C語言后,不能進一步探究,對基本的數(shù)據(jù)結(jié)構(gòu)與算法感覺枯燥。為解決這個問題,目前已有部分高校以游戲引擎等與現(xiàn)實緊密結(jié)合的內(nèi)容為導向,提升學生的編程語言學習興趣[3-5]。以此為出發(fā)點,本文使用跨平臺的C語言庫SDL2實現(xiàn)了一套2D游戲框架CSimpleEngine來激發(fā)學生進一步的學習研究的興趣。該框架使用C語言(C99標準)實現(xiàn),依賴于SDL2開源庫,該庫支持音視頻、網(wǎng)絡等功能的操作,且支持跨平臺編譯[6]。對于跨平臺部分,本文使用C4Droid作為跨平臺編譯器,使用的版本為C4Droid v6.97漢化版,其本身已經(jīng)集成好GCC以及SDL2庫,方便本文設計框架的APK編譯。此外,文中所涉及的CSimpleEngine框架與演示工程代碼已開源提交到https://gitee.com/suiwobian/csimple-engine.git。
框架目前包含通用功能、事件編輯、輸入控制、聲音管理、精靈管理、文字繪制、物體管理七個模塊。其中,通用功能模塊主要負責常用算法的實現(xiàn),如平面旋轉(zhuǎn)、碰撞位置檢測等通用功能;事件編輯模塊負責用戶層邏輯,包括物體事件聲明、定義注冊,是用戶層設計的主要工具,出于代碼復用目的,用戶使用該模塊的宏進行操作;輸入控制模塊負責用戶層的輸入檢測,如鼠標或屏幕的點擊、鍵盤按鍵狀態(tài)等信息的獲??;聲音管理模塊負責音效資源的配置與使用,包含音樂與音效兩種類型。精靈管理模塊負責圖片載入以及紋理資源顯示;文字繪制模塊負責字體文件的載入并利用精靈管理模塊對字體紋理進行顯示。
其中,物體管理模塊是整個框架的核心,用戶主要通過操作實例完成邏輯。此外,物體與實例的區(qū)別在于:前者指某一類對象的模板抽象,后者指動態(tài)實例化的可以操作的物體。各模塊的關系如圖1所示。
圖1 框架功能模塊
CSimpleEngine框架基于SDL2開源庫,主要分為初始化、執(zhí)行、銷毀這三個階段。
初始化階段流程如下圖所示:首先調(diào)用SDL_Init函數(shù)進行SDL庫的初始化,包含初始化音頻和視頻功能,使用TTF_Init進行字體功能初始化,并根據(jù)配置創(chuàng)建全局窗口繪制表面以及渲染器。接著調(diào)用用戶實現(xiàn)的資源注冊接口,該接口對資源文件與配置文件中定義的資源枚舉量進行關聯(lián),然后調(diào)用用戶定義的物體初始化接口,該接口主要負責物體事件的注冊,以及其它自定義的初始化工作。其中,資源注冊接口函數(shù)As?setInit與物體注冊接口函數(shù)ObjectInit只提供接口聲明,具體實現(xiàn)必須由用戶定義,否則無法通過編譯。
圖2 框架初始化流程
初始化完成后,框架進入執(zhí)行流程的事件輪詢狀態(tài),直至用戶關閉窗口或強制退出。首先處理用戶輸入、退出消息等內(nèi)容。然后按配置幀率來執(zhí)行各個物體實例事件,事件類型包含創(chuàng)建事件、更新事件、繪制事件、銷毀事件。創(chuàng)建或銷毀事件由用戶在調(diào)用創(chuàng)建或銷毀物體實例的相關的函數(shù)時觸發(fā)回調(diào);更新與繪制事件默認情況下由框架通過輪詢模式執(zhí)行,更新事件主要負責執(zhí)行邏輯,繪制事件主要負責實例顯示,所有物體實例更新完畢后再進行所有實例的繪制,繪制順序可自定義。執(zhí)行階段單幀流程如圖3所示。
圖3 執(zhí)行階段的單幀流程
框架銷毀階段主要進行各類全局與注冊資源的釋放,如調(diào)用函數(shù)釋放字體、SDL等庫、釋放主窗口繪制表面以及渲染器。
框架代碼包含3個文件:CSimpleConfig.h為框架配置頭文件;CSimpleEngine.h、CSimpleEngine.c為核心實現(xiàn)。其中上層應用依賴于CSimpleEngine,而CSim?pleEngine依賴于CSimpleConfig。CSimpleConfig.h主要定義了框架的執(zhí)行環(huán)境,具體如表1所示。
表1 CSimpleConfig工程配置
其中,對于聲音、精靈、字體、物體類型的配置,使用枚舉與全局數(shù)組的形式進行資源管理。利用枚舉的自增特性,當用戶在CSimpleConfig配置文件插入自定義枚舉時,用于存儲數(shù)據(jù)的數(shù)組也在編譯時自動增加。此外,物體的管理數(shù)組分別存放不同類型物體的第一個實例,然后通過實例內(nèi)部的雙向列表結(jié)構(gòu)進行查找,實現(xiàn)物體歸類與多實例檢索功能。
該部分由CSimpleEngine.h、CSimpleEngine.c兩個文件構(gòu)成,為用戶層提供近100個API。功能按類別分為:通用功能、事件編輯、聲音管理、輸入控制、精靈管理、字體繪制、物體管理。其中,事件編輯與物體管理是應用層邏輯實現(xiàn)的主要工具,事件編輯模塊主要利用了C語言中拼接宏#、可變宏參數(shù)__VAR_ARGS__等技巧實現(xiàn)。
精靈管理模塊處理的精靈數(shù)據(jù)結(jié)構(gòu)包含:繪制表面、紋理、子圖個數(shù)(支持水平序列幀自動生成)、錨點位置、子圖高度、子圖寬度。
物體管理模塊所管理的物體實例信息包含:四邊形碰撞遮罩、物體位置、物體繪制深度(值越小離屏幕越近)、關聯(lián)的精靈類型、精靈的子圖號、精靈播放速度、精靈繪制角度(順時針為正)、精靈水平與垂直縮放比例、精靈不透明度(取值范圍0~1,0完全透明,1不透明)、物體類型、是否可見、是否活躍、是否使能、用于管理執(zhí)行順序的上一個和下一個物體對象指針、用于管理繪制順序的上一個和下一個物體對象指針、自定義物體成員變量指針。若物體未定義繪制事件,則默認繪制實例所綁定的精靈,若定義了繪制事件則需要單獨調(diào)用精靈繪制函數(shù)去顯示。
各個模塊的部分核心功能列舉如表2所示。
表2 通用模塊部分功能
表3 事件編輯模塊部分功能
表4 聲音管理模塊部分功能
表5 輸入控制模塊部分功能
表6 精靈管理模塊部分功能
續(xù)表
表7 物體管理模塊部分功能
本節(jié)以雙人彈球設計為例,簡要的介紹框架的使用,其中操作部分使用鍵盤與鼠標進行彈板控制,具體代碼見開源工程。
首先,打開配置文件CSimpleConfig.h進行環(huán)境編輯:修改資源文件路徑RESOURCE_DIR為“./resource/PINGPONG/”,里邊包含了彈球與彈板圖片;開發(fā)時開啟碰撞調(diào)試模式;修改窗口寬ROOM_WIDTH為640,高ROOM_HEIGHT為360;幀率RUN_FPS改為30;默認使用自定義繪制順序。
然后,在精靈枚舉SPRITE_TYPE的SPR_NULL與SPR_NUM之間插入新的枚舉量:彈球精靈SPR_BALL、彈板精靈SPR_BOARD。在物體枚舉OB?JECT_TYPE的OBJ_NULL與OBJ_NUM之間插入彈球OBJ_BALL、障礙OBJ_WALL、彈板OBJ_BOARD。
基本資源定義完成后,在主邏輯文件main.c的As?setInit接口中使用SpriteTypeReg函數(shù)注冊上邊定義的精靈資源,將其與資源目錄下相關的圖像文件進行關聯(lián):
其中,參數(shù)1為定義的資源枚舉,參數(shù)2為資源文件路徑,參數(shù)3為精靈子圖個數(shù),這里配置為1個,參數(shù)4為精靈旋轉(zhuǎn)中心,即錨點位置,這里均配置為正中心。
接著為彈球定義創(chuàng)建與更新事件,事件定義宏內(nèi)有默認指向當前物體實例的指針obj以及當前物體實例的參數(shù)指針args,編寫邏輯代碼時注意不要定義重名變量。此外,如果給物體定義成員變量后必須要定義物體的創(chuàng)建事件才能正常初始化。
彈球邏輯的實現(xiàn)過程如下:①使用DEFINE_EVENT_ARGS定義彈球的水平與垂直速度;②使用DEFINE_EVENT_CREATE定義彈球的創(chuàng)建事件,設置初始位置為中心,并給與隨機速度;③使用DE?FINE_EVENT_STEP定義彈球的更新事件,每幀根據(jù)當前速度修改坐標,如果碰到墻壁則使垂直速度反向,如果碰到彈板則使水平速度反向,如果超過左右邊界則進行新的一輪并重置隨機位置。
彈板邏輯的實現(xiàn)過程如下:①使用DEFINE_EVENT_ARGS定義彈板的控制類型,1為鼠標,0為鍵盤光標上下鍵,并在創(chuàng)建事件中初始化為1;②在更新事件中如果讓類型1的實例跟蹤鼠標垂直位置,讓類型0的實例響應鍵盤上下按鍵。
然后,在ObjectInit接口中使用REGISTER_EVENT_CREATE、REGISTER_EVENT_STEP注冊以上定義好的事件。
接著創(chuàng)建各個物體的實例:使用ObjectCreate創(chuàng)建彈球并綁定精靈圖;然后分別在屏幕上下方向的邊沿外側(cè)創(chuàng)建障礙墻體,無需關聯(lián)精靈顯示,只需使用ObjectSetMask與ObjectSetScale配置碰撞盒的大小與比例即可;最后使用ObjectCreate創(chuàng)建左右兩個彈板,綁定精靈并使用OBJ_ARGS宏設置左邊的彈板為鍵盤控制,右邊為鼠標控制。
最后在程序入口,如main函數(shù)中,調(diào)用框架入口驅(qū)動函數(shù)EngineLoop()即可。
框架提供了默認的Makefile文件,使用gcc編譯器,Windows下可以使用mingw進行編譯。注意,工程編譯時需要添加SDL2的相關頭文件與依賴庫,圖4為彈球工程編譯后的執(zhí)行效果。
圖4 雙人彈球程序
此外,開源代碼的project目錄中附帶了Flappy?Bird的Demo,涵蓋了音效與文字等大部分的使用。為了方便,本文使用Android平臺上的C4Droid進行APK的編譯,本質(zhì)是利用了Android開發(fā)環(huán)境下的Native?Activity,操作流程如下:
首先,將4個主要文件CSimpleConfig.h、CSim?pleEngine.h、CSimpleEngine.c、main.c以及工程相關的資源目錄resource拷貝到手機上,使用C4Droid打開main.c。
然后,長按底側(cè)編譯按鈕調(diào)出編譯選項,選擇編譯多個源代碼文件(簡單),在文件路徑中依次填入文件名,并用空格分開:CSimpleConfig.h CSimpleEngine.h CSimpleEngine.c main.c,點擊確定,在主界面選擇編譯,若沒有錯誤,則會提示編譯成功,接著即可運行測試。
最后,生成安裝包,點擊右上角菜單,選擇導出。導出配置應用標題(英文)、圖標路徑、應用資源目錄,點擊導出。導出后安裝即可運行,編輯操作如圖5所示。
圖5 使用C4Droid編譯FlappyBird工程
本文首先基于SDL2對CSimpleEngine框架進行設計,擴展出通用功能、事件編輯、輸入控制、聲音管理、精靈管理、文字繪制、物體管理等模塊,其中物體管理與事件編輯是整個框架的應用核心。然后介紹了框架的驅(qū)動流程,包含初始化、執(zhí)行、銷毀這三個階段,其中執(zhí)行階段使用了事件輪詢模式。接著分析了框架代碼的3個核心文件:CSimpleConfig.h、CSimpleEngine.h、CSimpleEngine.c,并闡述了它們負責的主要功能。最后,使用雙人彈球與FlappyBird的示例代碼分別演示了框架的基本流程及其支持跨平臺編譯的特性,驗證了框架的易用性和跨平臺性,CSimpleEngine框架可以進一步作為日常C語言教學的課外擴展,提升學生對學習的興趣。