Cocos2d-x项目配Android环境总是踩坑,这里记录一下完整流程和常见问题。
环境准备 开发环境要求
组件
推荐版本
说明
Cocos2d-x
3.17+
稳定版本,支持Android Studio
Android Studio
3.0+
官方推荐IDE
NDK
r16b - r20
用于编译C++代码
JDK
1.8+
Java开发工具包
Python
2.7.x
用于运行setup脚本
NDK版本选择 Cocos2d-x对NDK版本有要求,太新或太旧都不行。
推荐版本:
Cocos2d-x 3.17.x: NDK r16b - r19c
Cocos2d-x 4.0+: NDK r20+
NDK下载地址:
1 https://developer.android.com/ndk/downloads/
项目结构配置 标准项目目录 一个标准的Cocos2d-x项目应该有以下目录结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 MyGame/ ├── Classes/ # C++源代码 │ ├── AppDelegate.cpp │ ├── AppDelegate.h │ └── HelloWorldScene.cpp ├── Resources/ # 游戏资源文件 ├── cocos2d/ # Cocos2d-x引擎代码 ├── proj.android-studio/ # Android Studio项目 │ ├── app/ │ │ ├── build.gradle │ │ ├── jni/ │ │ │ ├── Android.mk │ │ │ └── Application.mk │ │ └── src/ │ ├── build.gradle │ ├── settings.gradle │ └── gradle.properties └── CMakeLists.txt
local.properties配置 在项目根目录创建或修改local.properties文件:
1 2 3 4 5 6 7 8 ndk.dir =C\:\\android-ndk-r16b sdk.dir =C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk
注意:
Windows路径用双反斜杠\\
确保路径指向实际安装的目录
Gradle项目配置 settings.gradle配置 1 2 3 4 include ':libcocos2dx' project (':libcocos2dx' ).projectDir = new File (settingsDir, '../cocos2d/cocos/platform/android/libcocos2dx' )include ':xxxGame' project (':xxxGame' ).projectDir = new File (settingsDir, 'app' )
重要提示:
libcocos2dx的路径必须使用相对路径
libcocos2dx版本不能太低,否则会出现nativeSetAudioDeviceInfo方法找不到的错误
xxxGame改为你的游戏项目名称
gradle.properties配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 org.gradle.jvmargs =-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.parallel =true PROP_TARGET_SDK_VERSION =14 PROP_APP_ABI =armeabi-v7a
支持的ABI架构:
ABI
说明
armeabi-v7a
32位ARM,兼容性最好
arm64-v8a
64位ARM,性能更好
x86
Intel x86架构,模拟器使用
x86_64
64位Intel架构
gradle-wrapper配置 1 2 3 4 5 6 distributionBase =GRADLE_USER_HOME distributionPath =wrapper/dists zipStoreBase =GRADLE_USER_HOME zipStorePath =wrapper/dists distributionUrl =https\://services.gradle.org/distributions/gradle-4.1-all.zip
项目级build.gradle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 buildscript { repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' } } allprojects { repositories { jcenter() google() } }
模块级build.gradle 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 import org.apache.tools.ant .taskdefs.condition.Osapply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "26.0.2" android{compileOptions.encoding="UTF-8" } defaultConfig { applicationId "com.xxxx.xxxGame" minSdkVersion 10 targetSdkVersion 23 versionCode 1 versionName "1.0" externalNativeBuild { ndkBuild { if (!project .hasProperty("PROP_NDK_MODE" ) || PROP_NDK_MODE.compareTo ('none' ) != 0 ) { targets 'cocos2dcpp' arguments 'NDK_TOOLCHAIN_VERSION=4.9' arguments 'APP_PLATFORM=android-' +PROP_TARGET_SDK_VERSION def module_paths = [project .file ("../../cocos2d" ).absolutePath, project .file ("../../cocos2d/cocos" ).absolutePath, project .file ("../../cocos2d/external" ).absolutePath] if (Os.isFamily(Os.FAMILY_WINDOWS)) { module_paths = module_paths.collect {it.replaceAll('\\\\' , '/' )} arguments 'NDK_MODULE_PATH=' + module_paths.join (";" ) } else { arguments 'NDK_MODULE_PATH=' + module_paths.join (':' ) } arguments '-j' + Runtime .runtime .availableProcessors() abiFilters.addAll(PROP_APP_ABI.split(':' ).collect {it as String}) } } } } sourceSets .main { java.srcDir "src" res.srcDir "res" jniLibs.srcDir "libs" manifest.srcFile "AndroidManifest.xml" assets.srcDir "../../Resources" } externalNativeBuild { ndkBuild { if (!project .hasProperty("PROP_NDK_MODE" ) || PROP_NDK_MODE.compareTo ('none' ) != 0 ) { path "jni/Android.mk" } } } signingConfigs { release { keyAlias 'xxx' keyPassword 'xxxx' storeFile file ('E:/xxxx.keystore' ) storePassword 'xxx' } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt' ), 'proguard-rules.pro' if (project .hasProperty("RELEASE_STORE_FILE" )) { signingConfig signingConfigs.release } externalNativeBuild { ndkBuild { arguments 'NDK_DEBUG=0' } } signingConfig signingConfigs.release } debug { externalNativeBuild { ndkBuild { arguments 'NDK_DEBUG=1' } } } } } dependencies { compile fileTree (include : ['*.jar' ], dir: 'libs' ) compile project (':libcocos2dx' ) }
NDK编译配置 Android.mk配置 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 LOCAL_PATH := $(call my-dir ) include $(CLEAR_VARS) $(call import-add-path,$(LOCAL_PATH) /../../../cocos2d) $(call import-add-path,$(LOCAL_PATH) /../../../cocos2d/external) $(call import-add-path,$(LOCAL_PATH) /../../../cocos2d/cocos) $(call import-add-path,$(LOCAL_PATH) /../../../cocos2d/cocos/audio/include) LOCAL_MODULE := cocos2dcpp_shared LOCAL_MODULE_FILENAME := libcocos2dcpp define walk $(wildcard $(1) ) $(foreach e, $(wildcard $(1) /*), $(call walk, $(e) ) ) endef define uniq $(eval seen :=) $(foreach _,$(1) ,$(if $(filter ${_},${seen}) ,,$(eval seen += ${_}) )) ${seen} endef ALLFILES = $(call walk, $(LOCAL_PATH) /../../../Classes) FILE_LIST := $(filter %.cpp, $(ALLFILES) ) LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH) /%=%)\ LOCAL_SRC_FILES += ../../../Classes/Libs/SQLite/sqlite3secure.c $(info "UI path2:" $(LOCAL_SRC_FILES) ) FILES_PATH := $(LOCAL_PATH) /../../../Classes FILE_INCLUDES := $(dir $(foreach src_path,$(FILES_PATH) , $(call walk,$(src_path) ,*/) ) ) FILE_INCLUDES := $(call uniq,$(FILE_INCLUDES) ) LOCAL_C_INCLUDES := $(FILE_INCLUDES) LOCAL_STATIC_LIBRARIES := cocos2dx_static include $(BUILD_SHARED_LIBRARY) $(call import-module,.)
Application.mk配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 APP_STL := gnustl_static APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char APP_LDFLAGS := -latomic APP_SHORT_COMMANDS := true APP_CPPFLAGS += -Wno-error=format-security ifeq ($(NDK_DEBUG) ,1) APP_CPPFLAGS += -DCOCOS2D_DEBUG=1 APP_OPTIM := debug else APP_CPPFLAGS += -DNDEBUG APP_OPTIM := release endif
关键配置说明:
配置项
说明
APP_STL
C++标准库类型,gnustl_static表示静态链接
APP_CPPFLAGS
C++编译器选项,-std=c++11启用C++11
APP_LDFLAGS
链接器选项
APP_SHORT_COMMANDS
解决Windows命令行长度过长问题
AndroidManifest配置 游戏主模块配置 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 <?xml version="1.0" encoding="utf-8" ?> <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.xxxx.xxxGame" android:installLocation ="auto" > <uses-feature android:glEsVersion ="0x00020000" /> <application android:label ="@string/app_name" android:icon ="@drawable/icon" > <meta-data android:name ="android.app.lib_name" android:value ="cocos2dcpp" /> <activity android:name ="com.xxxx.xxxGame.AppActivity" android:label ="@string/app_name" android:launchMode ="singleTop" android:screenOrientation ="sensorPortrait" android:configChanges ="orientation|keyboardHidden" android:theme ="@android:style/Theme.NoTitleBar.Fullscreen" > <intent-filter > <action android:name ="android.intent.action.MAIN" /> <category android:name ="android.intent.category.LAUNCHER" /> </intent-filter > </activity > </application > <supports-screens android:anyDensity ="true" android:smallScreens ="true" android:normalScreens ="true" android:largeScreens ="true" android:xlargeScreens ="true" /> <uses-permission android:name ="android.permission.INTERNET" /> </manifest >
libcocos2dx模块配置 1 2 3 4 5 6 7 <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="org.cocos2dx.lib" > <application android:allowBackup ="true" > </application > </manifest >
自定义AppActivity 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 70 71 72 73 74 75 package com.gamedo.clonehero.pass;import android.content.Context;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.view.WindowManager;import android.widget.ImageView;import org.cocos2dx.lib.Cocos2dxActivity;public class AppActivity extends Cocos2dxActivity { protected static Handler mUIHandler; private Context mContext; private static ImageView mWelcome = null ; protected ImageView createLaunchImage () { mWelcome = new ImageView (mContext); mWelcome.setImageResource(R.drawable.splash); mWelcome.setScaleType(ImageView.ScaleType.CENTER_CROP); return mWelcome; } public static void removeLaunchImage () { mUIHandler.post(new Runnable () { @Override public void run () { if (mWelcome != null ) { mWelcome.setVisibility(View.GONE); } } }); } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); mContext = this ; mUIHandler = new Handler (); addContentView(createLaunchImage(), new WindowManager .LayoutParams( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)); new Thread () { @Override public void run () { super .run(); try { Thread.sleep(3000 ); }catch (Exception e){ } removeLaunchImage(); } }.start(); } static { System.loadLibrary("cocos2dcpp" ); } }
常见问题与解决方案 nativeSetAudioDeviceInfo错误 错误信息:
1 No implementation found for void org.cocos2dx.lib.Cocos2dxHelper.nativeSetAudioDeviceInfo(boolean, int, int)
原因分析:
模拟器是x86架构,需要x86架构的so包
Cocos2d-x版本过低,新版本中已移除该方法
解决方案1 - 添加x86支持:
修改jni/Application.mk文件:
1 APP_ABI := armeabi armeabi-v7a x86
解决方案2 - 升级Cocos2d-x:
更换为新版本Cocos2d-x,新版中Cocos2dxHelper里已经没有调用nativeSetAudioDeviceInfo方法。
dx.jar加载失败 错误信息:
1 Failed to load ..\sdk\build-tools\28.0.3\lib\dx.jar
原因分析: 加载dx.jar时默认使用最新版,但最新版一般都有兼容性问题。
解决方案:
打开%SDK_HOME%\build-tools目录
找一个老版本(如21.1.2)
打开lib文件夹,复制dx.jar
打开最新版本(如28.0.3)的lib文件夹
用复制的dx.jar替换
格式安全警告 错误信息:
1 format not a string literal and no format arguments [-Werror=format-security]
解决方案:
修改NDK目录下的build/core/default-build-commands.mk文件:
将:
1 TARGET_FORMAT_STRING_CFLAGS := -Wformat -Werror=format-security
修改为:
1 TARGET_FORMAT_STRING_CFLAGS := -Wformat
C++标准库函数缺失 错误信息:
1 'stoi' is not a member of 'std'
原因分析: 旧版本NDK对C++11支持不完整。
解决方案: 使用std::atoi替代std::stoi
malloc未声明 错误信息:
1 error: 'malloc' was not declared in this scope
解决方案: 在C++文件中添加头文件引用:
MovieClip数据格式 标准数据格式 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 { "mc" : { "mcName" : { "frameRate" : 24 , "labels" : [ { "name" : "fall" , "frame" : 1 , "end" : 3 } , { "name" : "move" , "frame" : 4 , "end" : 6 } ] , "events" : [ { "name" : "@fallhalf" , "frame" : 2 } , { "name" : "@movehalf" , "frame" : 5 } ] , "frames" : [ { "res" : "7ADF0F11" , "x" : 224 , "y" : 123 } , { "res" : "69D013A4" , "x" : 228 , "y" : 122 } ] } } , "res" : { "7ADF0F11" : { "x" : 1 , "y" : 87 , "w" : 105 , "h" : 80 } , "69D013A4" : { "x" : 111 , "y" : 1 , "w" : 104 , "h" : 85 } } }
字段说明 mc字段:
字段名
类型
说明
frameRate
int
帧率,默认24,可选
labels
array
帧标签列表,可选
events
array
帧事件标签列表,可选
frames
array
关键帧数据列表
res字段:
字段名
类型
说明
x
int
资源在纹理集位置的x坐标
y
int
资源在纹理集位置的y坐标
w
int
资源宽度
h
int
资源高度
性能优化 渲染合批 在Cocos2d-x中,可以通过设置实现自动渲染合批:
引擎会根据所用材质、Mesh的顶点格式、Primitive Type自动进行合批。
建议:
只对可以被合批并且顶点数量较少的模型启用此选项
相同材质的精灵尽量使用同一张纹理图集
总结一下 Cocos2d-x Android配置坑不少,主要注意NDK版本、Gradle配置和常见编译错误。
配置阶段
关键文件
注意事项
环境配置
local.properties
NDK和SDK路径正确
Gradle配置
settings.gradle, build.gradle
模块依赖和版本
NDK配置
Android.mk, Application.mk
源文件路径和编译选项
Manifest配置
AndroidManifest.xml
权限和Activity
常见问题
-
版本兼容性和架构支持
参考: