引言:我們使用cocos2d-x引擎制作了一款飛行射擊游戲,其中創新性地融入了手勢識別功能。但是我們在移植過程中遇到了很多的問題,同時也發現網上的資料少而不全。所以在項目行將結束的時候,我們特地寫了這篇文章來完整記錄我們整個移植的過程,紀念我們項目的成功完成,更以此來表達對幫助過我們的人的感謝。移植過程中我們在網上得到了很多幫助,更要感謝黃楊學長在最后時刻幫助我們突破難關!
0、開發平台
系統:win8 profession 64bit
IDE:vs2012 rtm, eclipse
cocos2d-x版本:2.1.2
1、移植准備
在windows下移植我們需要如下環境支持:Android SDK、 NDK、 Eclipse、 Cygwin.
1.1下載Cygwin安裝程序
在Cygwin官網上找(http://cygwin.com/install.html)到在線安裝程序,下載對應版本。這里我選擇 windows 64bit版本。
1.2 Cygwin安裝過程
打開下載好的setup-x86_64.exe,可一路單擊下一步(篇幅所限,太過細節的問題就不一一闡述了),直到如下頁面:
選擇下載的站點,正確選擇站點之后下載會很快。我們(在北航)選擇了ftp://mirrors.neusoft.edu.cn,速度還是挺快的。這里要提醒大家注意,並不是所有站點的下載都是完整的!Rylynn(博客名)選擇的好幾個站點都下載不完整,大家如果遇到這個問題,可嘗試不同的站點。
選擇完站點之后將出現如上界面,這是要選擇安裝包。為了使Cywin能夠編譯程序,我們需要選擇一些必要的組件。在不改動默認選擇的前提下,我們需要保證在Devel分支里如下包已經被選中:binutils, gcc-c++,gcc-core,gdb,make,mingw-gcc-core,mingw-gcc-c++。安裝成功后,運行Cygwin,可以在Cygwin窗口中分別輸入gcc+ -v, g++ -version, make -v, gdb -v等命令並回車,如果看到窗口輸出了各組件的版本信息,那么說明安裝成功。如果輸入某條命令,窗口輸出 command not found,那么就需要重新打開安裝程序並安裝對應的組件,不需要全部重新安裝。
1.3 安裝Eclipse、Android SDK、 Android NDK
這里提供網站鏈接,大家可自行下載和安裝。NDK下載后解壓即可。Eclipse的安裝和環境配置,請大家參考相應的文章,在此不再贅述。
Android SDK:http://developer.android.com/sdk/index.html
Android NDK:http://developer.android.com/tools/sdk/ndk/index.html
Eclipse:http://www.eclipse.org/downloads/
1.4 編輯create-android-project腳本
在cocos2d-x引擎的根目錄下,可以找到create-android-project腳本。在windows下,對應的是"create-android-project.bat",這個腳本用於生成游戲的android工程。
使用任何支持unix風格換行的編輯器(我們選擇了Ultra Editor)打開腳本文件,找到如下語句,並進行修改。
set _CYGBIN = e:\cygwin\bin
set _ANDROIDTOOLS =e:\android\andoird-sdk\tools
set _NDKROOT = e:\android\android-ndk-r8d
其中將_CYGBIN設置為Cygwin安裝目錄下的"bin"文件夾路徑,_ANDROIDTOOLS設置為Andoird SDK安裝路徑下"tools"文件夾路徑,_NDKROOT設置為NDK的解壓路徑。
完成設置后,在Cygwin中運行"create-android-project"腳本(可直接將文件拖入Cygwin窗口后回車)。若提示如下,則修改成功:
Input package path. For example: org.cocos2dx.example:
2、生成項目
接下來我們執行create-android-project腳本。用Cygwin運行"create-android-project.bat",看到如下提示:
Input package path. For example: org.cocos2dx.example:
此時輸入我們想要創建的程序包名(程序包的命名應遵守Andoird命名規范)。我們輸入:"org.cocofish.skyline"(我們這個項目的隊名叫cocofish,項目叫做skyline。大家可隨意選擇,只要記住就好)。回車后,窗口輸出:
Input project name:
我們輸入了"Skyline"
回車后,窗口會列出計算機中安裝的所有Android SDK版本,大家可以根據需要自行選擇。我們輸入了"1",即選擇android 4.1.2的版本。在Lmeng(博客名)的計算機里,整個過程窗口輸出的內容貼在下方:
Please enter your package path. For example: org.cocos2dx.example:org.cocofish.skyline Please enter your project name:Skyline "Now cocos2d-x suppurts Android 2.1-update1, 2.2, 2.3 & 3.0" "Other versions have not tested." Available Android targets: ---------- id: 1 or "android-16" Name: Android 4.1.2 Type: Platform API level: 16 Revision: 4 Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, W XGA720, WXGA800, WXGA800-7in ABIs : armeabi-v7a, mips, x86 ---------- id: 2 or "Google Inc.:Google APIs:16" Name: Google APIs Type: Add-On Vendor: Google Inc. Revision: 3 Description: Android + Google APIs Based on Android 4.1.2 (API level 16) Libraries: * com.google.android.media.effects (effects.jar) Collection of video effects * com.android.future.usb.accessory (usb.jar) API for USB Accessories * com.google.android.maps (maps.jar) API for Google Maps Skins: WVGA854, WQVGA400, WSVGA, WXGA800-7in, WXGA720, HVGA, WQVGA432, WVGA 800 (default), QVGA, WXGA800 ABIs : armeabi-v7a ---------- id: 3 or "android-17" Name: Android 4.2.2 Type: Platform API level: 17 Revision: 2 Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, W XGA720, WXGA800, WXGA800-7in ABIs : armeabi-v7a, mips, x86 ---------- id: 4 or "Google Inc.:Google APIs:17" Name: Google APIs Type: Add-On Vendor: Google Inc. Revision: 3 Description: Android + Google APIs Based on Android 4.2.2 (API level 17) Libraries: * com.google.android.media.effects (effects.jar) Collection of video effects * com.android.future.usb.accessory (usb.jar) API for USB Accessories * com.google.android.maps (maps.jar) API for Google Maps Skins: WVGA854, WQVGA400, WSVGA, WXGA800-7in, WXGA720, HVGA, WQVGA432, WVGA 800 (default), QVGA, WXGA800 ABIs : armeabi-v7a Please input target id:1
完成之后,腳本會在根目錄下創建"Skyline"的目錄,並在此目錄里生成Android項目文件。新創建的項目文件中有Helloworld的項目,大家接着可直接跳到從"執行build_native.sh"繼續后續操作,通過完成這樣一個默認的項目的移植來檢測整個配置和操作過程的正確性。這里將繼續自己項目的移植。
打開新創建的項目目錄(我們這里是"Skyline"),可以看到"Classes"和"Resources"目錄,腳本會在這兩個目錄下創建默認的Helloworld代碼和資源文件,我們把它們全部刪除。接着,把我們自己的項目源碼和資源復制到這兩個目錄里。通常,我們將源代碼(.cpp和.h)復制到"Classes"目錄下,把資源文件(圖片,音樂等)復制到"Resources"目錄下。操作完成后,進入下一步。
重要!在新建的項目后,proj.android\jni\hellocpp下,有個main.cpp,是整個項目的入口。直接點擊,會彈出"拒絕訪問"。如果想要查看里面的內容的話,要先修改它的屬性-安全-高級,在權限條目里將權限設置為"完全控制"(雙擊后可修改)。main.cpp的內容,一般情況下不需要做修改。
3、編譯
完成上述操作后,我們要對項目進行編譯,並生成.so文件(unix的動態鏈接庫,與windows的.dll類似)。
3.1 修改"Android.mk"
"Android.mk"文件位於項目的"proj.android\jni"目錄下,它記錄了項目所包含的源碼文件信息。利用編輯器(如Ultra Editor)打開此文件,參照下面的文件進行修改。/*...*/里的內容是筆者加上的注釋
LOCAL_PATH := $(call my-dir) /*不改動*/ include $(CLEAR_VARS) /*不改動*/ LOCAL_MODULE := sky_shared /*默認為game_sharred,如果改動,請一定記住*/ LOCAL_MODULE_FILENAME := libsky /*默認為libgame,如果改動,請一定記住。*/ LOCAL_SRC_FILES := hellocpp/main.cpp \ ../../Classes/AppDelegate.cpp \ ../../Classes/其他源文件.cpp /*添加上自己項目里所有的源文件,結尾的'\'是連接符,如果下一行還有內容,請不要漏掉.而且'\'后應直接回車*/ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \ $(LOCAL_PATH)/../../../cocos2dx/platform/third_party/win32 \ $(LOCAL_PATH)/../../../extensions /*添加上自己項目里頭文件所在的目錄。后兩行是筆者因為項目而單獨添加的,前一個是為線程,后一個是為 控件類引入的頭文件所在的目錄*/ LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static cocosdenshion_static cocos_extension_static /*以下均不作改動*/ include $(BUILD_SHARED_LIBRARY) $(call import-module,CocosDenshion/android) \ $(call import-module,cocos2dx) \ $(call import-module,extensions)
重要!出錯點!筆者在這一步遇到很多問題,所以也請讀者加以重視。
(1)對LOCAL_MODULE與LOCAL_MODULE_FILENAME修改后請務必記住自己所修改的名字。比如筆者將它們改為skyline_shared和libskyline。因為這里的skyline將會在導入eclipse那一步之后用到。我們需要將在通過執行“create-android-project”創建的項目文件夾里的"<你的項目名>.java”(比如"skyline.java")這個文件里System.loadLibrary("game")的“game”改成自己所修改的名字,在筆者這里就是"skyline",否則就是黑屏。估計是無法加載生成的libskyline.so(后面會提到)。筆者因為這個問題糾結了很久!最后才發現原來就一個字符串的事情!!如果不改動前面的LOCAL_MODULE和LOCAL_MODULE_FILENAME,因為默認的是"game",那就不會有問題。
(2)如果你在項目里引入了線程和控件類,可參考筆者這個代碼框里的LOCAL_C_INCLUDES。對於線程,請注意:將你引入線程的那個文件里"#include 'pthread/pthread.h'"改為"#include'pthread.h'",以使編譯能夠通過。(筆者不知道原因,也許沒有這么麻煩,但是在筆者計算機里成功了。也希望明白的人告知~)
3.2 執行"build_native.sh"
接下來就啟動編譯了。進入android項目路徑下,使用Cygwin執行"build_native.sh"腳本(tip:可直接將文件拖入Cygwin窗口),如果順利,我們可以在Cygwin窗口輸出的最后發現“Install : libsky.so => libs/armeabi/libsky.so”,並且在libs/armeabi目錄下找到編譯獲得的"libskyline.so"(注意到了嗎,這是上一步設置的LOCAL_MODULE_FILENAME)。
重要!出錯點!如果此時提示“please define NDK_ROOT”,找到cygwin\home\xxx(你的用戶名)目錄下.bash_profile文件,用文本編輯器打開,在最后加入下面兩行:
NDK_ROOT=/cygdrive/d/Coco/android-ndk-r9
export NDK_ROOT
錯誤將不再出現。d/Coco/android-ndk-r9 是你ndk解壓目錄,筆者的原目錄是D:\Coco\android-ndk-r9,做類似改動即可。
然而,事情往往不那么順利!
重要!出錯點!很大可能你會看到Cygwin輸出一大堆亂七八糟的東西,而且在中途停下。這時候就是編譯出錯了。但是千萬不要着急,一點點調。雖然出錯了,但是報錯點還是很准確的。所以請大家從最上面往下一行一行找。發現有"error"的這行,這就是報錯點。仔細看這行的內容,它很明確地告訴你是哪個文件哪一行出了錯。這時候去找到那個出錯的位置,根據報錯的信息,查找一些資料,加上自己的判斷和嘗試,把錯誤處理掉。然后再編譯一遍。如果有錯誤,繼續處理。不要看一堆字母感覺煩躁(看着就像亂碼的感覺),這時候你最需要的就是慢慢來。而且這個錯誤可能來自於引擎代碼本身!!
比如筆者遇到這樣幾個錯誤:
(1)使用了未聲明的"itoa"。於是我去查Classes目錄下的props_number_shower.cpp第27行,確實調用了itoa這個方法。雖然這個在vs2012里運行沒問題,但是這里並不支持。於是我從網上找了一個替代itoa功能的函數源代碼,加入到這個源文件中,編譯就通過了。網友涵曦(博客名)提示,這是包含文件的問題。在此表示感謝!
(2)格式問題。同樣的思路,找到CocosDenshion/android/SimpleAudioEngine.cpp第77行。發現了LOGD(deviceModel)(如圖)。上網查了下發現好像linux對格式要求比較嚴格,於是筆者嘗試把這行改為LOGD("%s", deviceModel);發現編譯通過。在編譯的過程中發現非常多類似的報錯,大家如果看到報錯信息如下圖,找到指定位置修改即可。
(3)文件流。筆者對c++文件流是否能在Cygwin中編譯通過沒有做足夠的測試。但是筆者將fstream換成c的FILE操作的時候就不報錯了,如果大家在這里遇到問題,可以嘗試着改改。但是對於移植到android上,還應采CCFileUtils。范例如下:
string fullPath=CCFileUtils::sharedFileUtils()->getWritablePath()+"rank.txt"; /*"rank.txt"是自己使用的資源文件,其他的不用改動。此行用於獲得文件位置*/ unsigned long file_size; /*聲明變量,在下一行中使用,用於接收文件內容大小*/ char *filedata=(char*)CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(),"r",&file_size); /*取得文件指針*/ if(filedata==NULL)... /*對於文件指針的用法,與C++一樣*/
如果遇到其他的錯誤,讀者可按照以上解決問題的思路自行解決問題。
4、導入Eclipse
4.1新建項目
打開Eclipse,一次打開"File"-"New"-"Project"-"Android"-"Android Project from Existing Code"項目,在隨后出現的界面中點擊browser,后找到游戲項目的"proj.android"並確認,此時即從原有代碼中新建了安卓項目。
4.2復制必要文件
4.2.1將cocos2d-2.1rc0-x-2.1.2(根目錄)\cocos2dx\platform\android\java\src下的文件復制到Sky(游戲項目根目錄)\proj.android\src里,並在eclipse中手動導入
4.2.2如果Sky(游戲項目根目錄)\proj.android\assets里沒有資源文件,將Sky(游戲項目根目錄)\Resources下你的所有資源文件復制到assets中,並在eclipse中手動導入
4.3 做必要的修改
4.3.1 在Sky(游戲根目錄)\src\<項目名>里的Sky.java文件里,下圖所標示的那行中,請記得將loadLibrary("xxx");里的字符串改為先前修改過的LOCAL_MODULE_FILENAME的值(去掉lib),如筆者改為sky。注意!這在上面已經強調過,如果這里的字符串和LOCAL_MODULE_FILENAME(去掉lib)的不匹配,結果很慘烈!看不見界面!筆者在這里費了很多時間!
4.3.2 其他簡單的錯誤,比如下圖所示:找不到drawable/icon,可在eclipse工程中res里加入一個icon.png文件即可。當然也可將icon改名成其他以存在於工程的圖片文件(一般為png文件)
5、調試
虛擬機十分卡,而且好像並不支持opengl,所以建議使用真機進行調試(使用方法這里就不詳細展開,大家可在網上查閱相關資料)。調試過程中難免發生問題,這里將列舉出筆者在調試過程中遇到的問題,探討解決的思路。
5.1 文件權限問題。在調試過程中我們在logcat message窗口看到這樣一個錯誤,意思是某個文件權限不夠。如果這時候大家復制這么長串的看起來像目錄的東西去搜索,是找不到的。因為他們畢竟不是目錄名。這個時候,大家可以選去關鍵詞去搜索。比如這張圖片里,我們認為"shader"是關鍵詞,果然在項目安卓工程里找到了"shaders"文件夾,將里面所有文件的權限修改成“完全控制”,發現調試不再報錯,可以認為修改成功!
5.2文件讀寫問題
某種意義上筆者並不願意把它稱作文件讀寫問題。后面將做解釋。
如果讀者還記得的話,上面已經提到過讀寫問題。如果單純采用C的文件讀寫寫法,工程就會找不到資源文件。因此將它改成上述的CCFileUtils,將解決此類問題。
string fullPath=CCFileUtils::sharedFileUtils()->getWritablePath()+"rank.txt"; /*"rank.txt"是自己使用的資源文件,其他的不用改動。此行用於獲得文件位置*/ unsigned long file_size; /*聲明變量,在下一行中使用,用於接收文件內容大小*/ char *filedata=(char*)CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(),"r",&file_size); /*取得文件指針*/ if(filedata==NULL)...
5.3 如何調試
之所以說讀者不願意把“5.2文件讀寫問題”稱作“文件讀寫問題”,是因為讀者在解決這類問題的時候使用的方法其實是告訴了我們如何進行調試。筆者在完成5.1那一步之后發現真機上還是沒有顯示出任何內容。黑屏!這個時候我們着急了,曾經已經將helloword顯示在手機上了,為什么這個項目就不可以?!學長提示我們是不是主菜單加載不成功,並知道我們對每一個關鍵部分加上一個輸出,通過在控制台的輸出查看哪一步出了問題!比如我們在主菜單"mainmenu.cpp"的"init()"里每一個加載操作后加上了CCLOG("%s","init() xxx;--From MainMenu")("xxx"這里改成特定的值,用於確定出錯位置)。也是通過這個方法,我們才確定了所有的“文件讀寫問題”。因此,如果大家遇到問題,請大家不要氣餒,一點點地去判斷出錯位置,然后修改,嘗試。
6、分辨率適配
最后一個問題就是分辨率適配了。在網上翻看了相關資料后,總算解決了問題。其實就是一行代碼的事情。
在 AppDelegate.cpp的 bool AppDelegate::applicationDidFinishLaunching() 方法里,加上
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(576,324,kResolutionNoBorder);即可解決分辨率適配問題。
其中,576,324 是筆者在win32設計游戲時采用的寬度和高度,大家改為自己所設置的寬高即可。kResolutionNoBorder是指在屏幕拉伸時采用的其中一種策略,保證拉伸后無邊界,但是設計時界面的邊緣部分可能在最后被遮蓋。筆者通過vs2012的 "go to definition"操作找到了如下的定義。以下即是全部的適配策略了。
enum ResolutionPolicy { // The entire application is visible in the specified area without trying to preserve the original aspect ratio. // Distortion can occur, and the application may appear stretched or compressed. kResolutionExactFit, // The entire application fills the specified area, without distortion but possibly with some cropping, // while maintaining the original aspect ratio of the application. kResolutionNoBorder, // The entire application is visible in the specified area without distortion while maintaining the original // aspect ratio of the application. Borders can appear on two sides of the application. kResolutionShowAll, kResolutionUnKnown, };
參考資料:參考的資料較為零碎,多為網上的文章。因為比較繁亂,沒有記錄下來。另外參考了《Cocos2d-x高級開發教程》的移植部分。
總結:
筆者和隊友在整個移植過程中備受煎熬,幾近絕望。但是始終相信既然別人能夠移植成功,我們也一定可以!最終我們用了兩天多的時間終於讓我們的游戲成功運行在手機上!那個興奮感、那種絕處逢生的快感到現在還十分難忘!所以請各位cocos2d-x的戰友們不要氣餒,問題都是可以解決的,一點一點來,不要着急,給自己信心。先把helloworld顯示出來,然后再把自己的項目慢慢調出來。堅持下去,總會成功的!
Here is our game <SkyLine Beta1.0>, Please download it to support us!!!
希望這篇文章能夠幫到困擾在cocosd-x移植android過程中的朋友們。如果文章內容與錯誤或者某些部分有更好的解法,也請留言。我們很希望和大家交流經驗!謝謝!