版本说明
3.x的最新版本是 cocos2d-x 3.17.2 ,4.x的最新版本是 cocos2d-x4.0
。
3.x的架构图:
4.x的架构图:
可以明显地看出:从4.0开始,不再只是JavaScript
引擎,也不再支持网页端,支持网页端的JavaScript
部分被单独剥离出来,统一使用Cocos Creator
来完成。因此定位就比较清晰了,Cocos Creator
主打小游戏的开发,当然也支持网页端,cocos2dx4.0
开始只支持原生端的Lua和C++。因为网页端的小游戏本身就可以在原生端运行,因此以后主推的开发平台就是Cocos Creator
,并配合TypeScript
语言进行开发。cocos2dx
基本上已经成为了过去式,但是也不代表无法使用,如果需要,仍然可以使用该引擎进行开发,相信很多团队都有很多适合自己的版本。
环境配置
基础环境
官方的安装编译环境要求:Build Requirements - cocos2d-x
下载解压 cocos2d-x 3.17.2 ,并把
X:\cocos2d-x-3.17.2\tools\cocos2d-console\bin
设置到环境变量Path
中,使得可以直接使用cocos
命令。如果未设置到环境变量中,使用绝对路径也可以
(推荐)。
-
Python 2.7.5+, Python 2,7.10 reccomended, NOT Python 3+
- 使用pyenv安装一个2.7的最新版即可。
-
NDK r91c+ is required to build Android games (tested with r19c) May be called 19.2.xx from within Android Studio
-
Android Studio 3.4+ to build Android games (tested with 3.0)。 也可以不安装,不影响使用命令行打包。
Java8 Downloads - Oracle download java from oracle without login
安装。执行:
python setup.py
,主要是按照提示设置两个路径:NDK
路径和AndroidSDK
路径,设置好后重新打开控制台终端。新建项目:
cocos new HelloWorld -p com.xx.yy -l lua -d F:\CocosProjects
,提示是否同意发送数据时,可以选择N(不同意。)修改64位支持:修改
gradle.properties
:PROP_APP_ABI=arm64-v8a
-
运行项目
Windows版本:
VisualStudio
打开\frameworks\runtime-src\proj.win32
目录下的sln
文件即可。-
Android版本:进入
frameworks\runtime-src\proj.android
文件夹,运行如下命令:cocos run -p android
待
gradle
相关配置更新或下载完成,运行至真机/模拟器查看效果。或者直接用AndroidStudio打开frameworks\runtime-src\proj.android
Cocos Studio
当前能找到的最新的版本是cocos studio v3.10 ,下载地址:
- Cocos Studio 3.10 Mac :http://download.cocos.com/CocostudioMac/Download/v3.10/CocosForMac-v3.10.dmg
- Cocos Studio 3.10 Win:http://download.cocos.com/CocosStudio/v3.10/CocosForWin-v3.10.exe
默认使用的引擎版本是:cocos2d-x-3.10
认识Cocos Studio的文件:
- Cocos.Launcher.Start:可以认为是dashboard,程序为
Cocos.exe
,通过它来管理创建的项目。 - CocosStudio:开发IDE,程序为:
CocosStudio.exe
- simulator:模拟器,程序为:
simulator.exe
随便创建个项目,然后运行项目,
发布项目成功
项目运行结束
Based on: cocos2d-x-3.10
编译模式:debug
正在编译...
要求的 VS 版本:[2012, 2013]
无法从注册表中找到可用的 VS 安装路径。
微软官方已经找不到可用的vs2013下载了,不过没有关系,vs强大之处就是高版本可以打开任意低版本项目,后面咱们直接上vs2022。
Cocos2dX新版搭配CocosStudio
截止目前为止,Cocos2d-X-3.x的最高版本是3.17.2,4.X的版本是4.0,我们以3.17.2版本为例来介绍,搭建一个开发环境。
安装cocos studio v3.10,例如安装在
D:\Cocos
-
下载Cocos2d-X SDK包,由于Cocos2d源码比较庞大,GitHub上的文件不全,直接拉取的不能使用,从这里下载:https://cocos2d-x.org/download 。解压到cocos studio安装目录下,目录组织如下(cocos2d-x-3.10是cocos studio安装自带的):
├─Cocos Studio ├─Cocos2d-x │ ├─cocos2d-x-3.10 │ ├─cocos2d-x-3.17.2 │ └─cocos2d-x-4.0 └─tools
-
使用用Cocos2d-X的命令行创建项目,进入目录:
D:\Cocos\Cocos2d-x\cocos2d-x-3.17.2\tools\cocos2d-console\bin
,参考官方方式创建项目:# 需要Python2.7环境,推荐使用pyenv安装 # lua项目 cocos new NewCocosProject -p com.your_company.mygame -l lua -d NEW_PROJECTS_DIR # c++项目 cocos new NewCocosProject -p com.your_company.mygame -l cpp -d NEW_PROJECTS_DIR
-
使用cocos studio随便创建一个项目,复用如下几个文件,把它们复制到
NEW_PROJECTS_DIR
项目根目录下。NewCocosProject.ccs NewCocosProject.cfg NewCocosProject.udf
修改配置文件。主要是修改
cfg
文件,如果cocos创建的是cpp项目则需要将PublishDirectory
的Value
修改为Resources/res
,这个主要是为了适应Cocos2d-X的资源目录组织形式,如果cocos创建的是lua项目则无须做任何修改。这里把全部的文件都贴上来,后面就不用再用CocosStudio创建项目了,直接复用这几个文件即可。
NewCocosProject.cfg
<SolutionConfig Version="3.10.0.0">
<PublishDirectory Value="res" />
<PackageDirectory Value="package/" />
<PublishType Value="Reference" />
<SolutionSize Value="960 * 640" />
<ResolutionName Value="iPhone 4/4S" />
<DefaultSerializer Value="Serializer_FlatBuffers" />
<CustomSerializer Value="Serializer_FlatBuffers" />
<IsNameStandardized Value="False" />
<CustomProperties>
<Item Key="CCS_CocosPropertis">
<Value ctype="CocosProperties">
<SolutionCodeType Value="Complete" />
<ProgramLanguage Value="lua" />
<CreateFrameworkVersion Value="cocos2d-x-3.10" />
<CurrentFrameworkVersion Value="cocos2d-x-3.10" />
<EngineType Value="Prebuilt" />
</Value>
</Item>
</CustomProperties>
</SolutionConfig>
NewCocosProject.ccs
<Solution>
<PropertyGroup Name="NewCocosProject" Version="3.10.0.0" Type="CocosStudio" />
<SolutionFolder>
<Group ctype="ResourceGroup">
<RootFolder Name="." />
</Group>
</SolutionFolder>
</Solution>
NewCocosProject.udf
<UserData Version="3.10.0.0">
<Properties>
<Item Key="CocosRecentOperation">
<Value ctype="CocosRecentOperation">
<LastPublishType Value="Resource" />
<IsLastPublish Value="True" />
<LastRunType Value="Windows" />
</Value>
</Item>
<Item Key="PackageParamsKey">
<Value ctype="PackageParams">
<LuaIsEncrypt Value="False" />
<LuaEncryptKey Value="" />
<LuaEncryptSign Value="" />
<Android_PackageName Value="org.cocos.NewCocosProject" />
<AndroidkeyStore Value="" />
<AndroidVersion Value="" />
<iOS_Target Value="NewCocosProject-mobile" />
<iOS_BundleID Value="" />
<Platform Value="None" />
<RunPlatform Value="None" />
<FrameworkVersion Value="cocos2d-x-3.10" />
<IsDebugKeystore Value="True" />
<KeystorePassword Value="" />
<KeystoreAliasName Value="" />
<KeystoreAliasPassword Value="" />
<EnableSourceMap Value="False" />
<EnableHTML5Advanced Value="False" />
</Value>
</Item>
<Item Key="TabsParamsKey">
<Value ctype="TabsInfo">
<ActiveDocument />
</Value>
</Item>
</Properties>
</UserData>
- 打开
CocosStudio.exe
(这里不能使用Cocos.Launcher.Start
导入项目,会提示非法项目),打开「菜单」-「打开项目」,选择.ccs
文件。这个时候UI的初始工程已经算完成了。 - 导入资源。在CocosStudio里可以导入资源。
- 发布资源。选择菜单:项目 - 发布与打包。资源会生成在对应目录下,后面cocos工程就可以访问到了。
- vs2022打开
proj.win32\NewCocosProject.sln
,编译运行。
推荐做法
- 如果是团队组织,推荐CocosStudio的UI项目与cocos2dx工程项目分开,UI项目完工后发布资源给cocos2dx工程项目即可,将资源放到对应的目录下即可。
- 如果是个人,UI项目可以跟cocos2dx工程项目是否放一起倒无所谓了。
首次编译
官方配置方式编译
以3.17.2为例说明,该版本编译Java端默认使用的是gradle-4.6 (查看生成的工程里面的配置文件可以得知,\frameworks\runtime-src\proj.android\gradle\wrapper\gradle-wrapper.properties
),不过gradle-4.6 也是只能使用Java8编译的。
不要修改电脑的工具配置,直接使用项目frameworks\runtime-src\proj.android
根目录下的gradlew
工具进行编译。
如果电脑安装了多个Java版本,但默认版本并不是Java8,则可以直接修改gradlew.bat
的内容,手动设定下JAVA_HOME
:
# 增加下面这句
set JAVA_HOME=D:\Java\jdk1.8.0_191
使用如下gradlew
命令编译:
# 清理工程
./gradlew clean
# 编译 Debug 版 APK
./gradlew assembleDebug
# 编译 Release 版 APK
./gradlew assembleRelease
# 清理并编译 Debug 版
./gradlew clean assembleDebug
# 清理并编译 Release 版
./gradlew clean assembleRelease
# 安装 Debug 版到设备
./gradlew installDebug
# 生成 .aab 文件(Google Play 用)
./gradlew bundleRelease
编译完成后,在app\build\outputs\apk
目录下生成相应的debug
和release
(未签名)的APK
,其中debug
版本的lua
脚本是明文的,release
版本的lua
脚本被编译为luajit
,文件扩展名为.luac
。Lua编译工具使用的是tools\cocos2d-console\plugins\plugin_luacompile
,cocos2d-x-3.17.2
使用的 LuaJIT 版本信息为:LuaJIT 2.1.0-beta2
,GitHub镜像:https://github.com/LuaJIT/LuaJIT
AndroidStudio新版+Gradle新版(推荐)
其实使用新版本的AndroidStudio和gradle也是可以编译的,无非是Java的版本需要高一些,截止目前为止gradle需要Java17。
笔者使用的AndroidStudio
版本信息如下:
Android Studio Meerkat | 2024.3.1
Build #AI-243.22562.218.2431.13114758, built on February 25, 2025
Runtime version: 21.0.5+-12932927-b750.29 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Toolkit: sun.awt.windows.WToolkit
Windows 11.0
GC: G1 Young Generation, G1 Concurrent GC, G1 Old Generation
Memory: 8192M
Cores: 16
Registry:
ide.instant.shutdown=false
ide.experimental.ui=true
i18n.locale=
配置gradle-wrapper.properties
使用新版gradle:
#Mon Mar 24 14:00:20 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
build.gradle
(project):
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
// classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.android.tools.build:gradle:8.9.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register('clean', Delete) {
delete rootProject.buildDir
}
在frameworks\runtime-src\proj.android
目录下新配置一个local.properties
,主要是指定下sdk目录:
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed Dec 06 10:53:55 CST 2017
sdk.dir=D\:\\android\\sdk
下面主要修改build.gradle
内容。
增加namespace
。
配置NDK版本:
// 如果后面so文件通过外部工具进行编译,则该可以不用该配置
// 使用该配置后,可以无须再设置 ndk.dir 配置,使用AndroidStudio的SDKManager下载的NDK版本
android {
ndkVersion "21.4.7075529"
}
配置lib的cpu架构:
// 可以这样配置,也可以设置 PROP_APP_ABI
android {
defaultConfig {
ndk {
abiFilters = ["arm64-v8a"]
//abiFilters.addAll(PROP_APP_ABI.split(':').collect { it as String })
}
}
}
设置cmake版本(后经测试发现,不设定也可以编译通过):
cmake {
path "../../../../CMakeLists.txt"
version "3.10.2"
}
配置res资源文件和lua脚本源码文件的中间目录,这里同时兼容了gradle4.x和8.x:
def mergedAssetsDir = variant.mergeAssetsProvider.get().outputDir.get().asFile
if (gradle.gradleVersion.startsWith("4.")) {
mergedAssetsDir = layout.buildDirectory.get().asFile.toPath().resolve("intermediates/assets/${variant.dirName}")
}
println "Merged assets path: ${mergedAssetsDir}"
def srcTempDir = new File(mergedAssetsDir, "src").getPath()
def resTempDir = new File(mergedAssetsDir, "res").getPath()
配置cocos.bat为全路径,因为不希望影响全局系统变量:
def getCocosCommandPath() {
if (OperatingSystem.current().isWindows()) {
return 'D:\\Cocos\\Cocos2d-x\\cocos2d-x-3.17.2\\tools\\cocos2d-console\\bin\\cocos.bat'
}
gradle.properties
增加:
# 开发阶段设置为 0,表示不编译不加密脚本,提高开发速度,发布阶段设置一下
PROP_COMPILE_SCRIPT = 0
PROP_LUA_ENCRYPT = 0
最后修改一些其他因为迁移到高版本gradle出现的警告提示等,完整版的gradle代码见下。
Gradle完整代码
模板创建的build.gradle
太复杂了,简单优化了下:
import org.gradle.internal.os.OperatingSystem
apply plugin: 'com.android.application'
android {
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
namespace 'com.test.mygame'
ndkVersion "21.4.7075529"
defaultConfig {
applicationId "com.test.mygame"
minSdkVersion PROP_MIN_SDK_VERSION
targetSdkVersion PROP_TARGET_SDK_VERSION
versionCode 1
versionName "1.0"
externalNativeBuild {
if (PROP_BUILD_TYPE == 'ndk-build') {
ndkBuild {
targets 'cocos2dlua'
arguments 'NDK_TOOLCHAIN_VERSION=clang'
arguments '-j' + Runtime.runtime.availableProcessors()
def module_paths = [project.file("../../../cocos2d-x").absolutePath,
project.file("../../../cocos2d-x/cocos").absolutePath,
project.file("../../../cocos2d-x/external").absolutePath]
if (OperatingSystem.current().isWindows()) {
module_paths = module_paths.collect {it.replaceAll('\\\\', '/')}
arguments 'NDK_MODULE_PATH=' + module_paths.join(";")
}
else {
arguments 'NDK_MODULE_PATH=' + module_paths.join(':')
}
}
}
else if (PROP_BUILD_TYPE == 'cmake') {
cmake {
arguments "-DCMAKE_FIND_ROOT_PATH=", "-DANDROID_STL=c++_static", "-DANDROID_TOOLCHAIN=clang", "-DANDROID_ARM_NEON=TRUE"
cppFlags "-frtti -fexceptions -fsigned-char"
}
}
}
ndk {
abiFilters = []
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"
}
externalNativeBuild {
if (PROP_BUILD_TYPE == 'ndk-build') {
ndkBuild {
path "jni/Android.mk"
}
}
else if (PROP_BUILD_TYPE == 'cmake') {
cmake {
path "../../../../CMakeLists.txt"
version "3.10.2"
}
}
}
signingConfigs {
release {
if (project.hasProperty("RELEASE_STORE_FILE")) {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
debuggable false
jniDebuggable false
renderscriptDebuggable false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.release
}
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=0'
}
}
}
debug {
debuggable true
jniDebuggable true
renderscriptDebuggable true
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=1'
}
}
}
}
}
def getCocosCommandPath() {
if (OperatingSystem.current().isWindows()) {
return 'D:\\Cocos\\Cocos2d-x\\cocos2d-x-3.17.2\\tools\\cocos2d-console\\bin\\cocos.bat'
}
else {
// on unix like system, can not get environments variables easily
// so run a shell script to get environment variable sets by cocos2d-x setup.py
new ByteArrayOutputStream().withStream { os ->
def result = exec {
executable = project.file('get_environment.sh')
standardOutput = os
}
ext.console_path = os.toString().trim()
}
return new File(console_path + '/cocos').absolutePath;
}
}
// a method used to invoke the cocos luaompile command
def compileLua(srcDir, dstDir, doCompile, is64bit, doEncrypt) {
def compileArgs = ['luacompile', '-s', srcDir, '-d', dstDir]
if (!doCompile) {
compileArgs << '--disable-compile'
} else if (is64bit) {
compileArgs << '--bytecode-64bit'
}
if (doEncrypt) {
compileArgs << '-e'
compileArgs << '-k'
compileArgs << project.property('PROP_LUA_ENCRYPT_KEY')
compileArgs << '-b'
compileArgs << project.property('PROP_LUA_ENCRYPT_SIGN')
}
// commandLine compileArgs
println 'running command : ' + 'cocos ' + compileArgs.join(' ')
exec {
// if you meet problem, just replace `getCocosCommandPath()` to the path of cocos command
executable getCocosCommandPath()
args compileArgs
}
// remove the lua files in dstDir
delete fileTree(dstDir) {
include '**/*.lua'
}
}
android.applicationVariants.configureEach { variant ->
// 删除旧文件
delete layout.buildDirectory.get().asFile.toPath().resolve("intermediates/assets/${variant.dirName}")
variant.mergeAssetsProvider.get().doLast {
println "Current Gradle version: ${gradle.gradleVersion}"
def mergedAssetsDir = variant.mergeAssetsProvider.get().outputDir.get().asFile
if (gradle.gradleVersion.startsWith("4.")) {
mergedAssetsDir = layout.buildDirectory.get().asFile.toPath().resolve("intermediates/assets/${variant.dirName}")
}
println "Merged assets path: ${mergedAssetsDir}"
def srcTempDir = new File(mergedAssetsDir, "src").getPath()
def resTempDir = new File(mergedAssetsDir, "res").getPath()
copy {
def fromPath = layout.projectDirectory.dir("../../../../src").toString()
println "Copying from: $fromPath -> $srcTempDir"
from fromPath
into srcTempDir
}
copy {
def fromPath = layout.projectDirectory.dir("../../../../res").toString()
println "Copying from: $fromPath -> $resTempDir"
from fromPath
into resTempDir
}
// 处理编译和加密逻辑
def compileScript = variant.name == 'release'
if (project.hasProperty('PROP_COMPILE_SCRIPT')) {
compileScript = (PROP_COMPILE_SCRIPT.compareTo('1') == 0)
}
def encryptLua = project.hasProperty('PROP_LUA_ENCRYPT') && (PROP_LUA_ENCRYPT.compareTo('1') == 0)
if (compileScript || encryptLua) {
def buildType = -1
if (compileScript) {
def need64 = false
def need32 = false
android.defaultConfig.ndk.getAbiFilters().each { abi ->
if (abi == 'arm64-v8a') {
need64 = true
} else {
need32 = true
}
}
if (need64 && need32) {
buildType = 2
} else if (need64) {
buildType = 1
} else {
buildType = 0
}
}
println "buildType is ${buildType} "
switch (buildType) {
case -1:
compileLua(srcTempDir, srcTempDir, false, false, encryptLua)
break
case 0:
compileLua(srcTempDir, srcTempDir, true, false, encryptLua)
break
case 1:
compileLua(srcTempDir, srcTempDir, true, true, encryptLua)
break
case 2:
compileLua(srcTempDir, "${srcTempDir}/64bit", true, true, encryptLua)
compileLua(srcTempDir, srcTempDir, true, true, encryptLua)
break
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':libcocos2dx')
}
缺失的类
LocalStorage
https://github.com/yangc999/jfclient/tree/master/frameworks/cocos2d-x/cocos/scripting/lua-bindings/manual/localstorage
打开VisualStudio工程,为libluacocos2d
项目添加头文件和源文件,然后在 frameworks/cocos2d-x/cocos/scripting/lua-bindings/manual/lua_module_register.cpp
中添加:
#include "scripting/lua-bindings/manual/localstorage/lua_cocos2dx_localstorage_manual.h"
register_localstorage_module(L);
还缺少对Init函数的注册,需要添加:
int lua_cocos2dx_localstorage_Init(lua_State* L) {
int argc = 0;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(L, 1, "cc.LocalStorage", 0, &tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(L) - 1;
if (argc == 1) {
#if COCOS2D_DEBUG >= 1
if (!tolua_isstring(L, 2, 0, &tolua_err))
goto tolua_lerror;
#endif
std::string fullpath = tolua_tostring(L, 2, "");
localStorageInit(fullpath);
return 0;
}
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L, "#ferror in function 'lua_cocos2dx_localstorage_removeItem'.", &tolua_err);
return 0;
#endif
}
// 在 lua_register_cocos2dx_localstorage 函数中添加注册:
tolua_function(L, "init", lua_cocos2dx_localstorage_Init);
并修复lua_cocos2dx_localstorage_getItem
一个BUG:
if (localStorageGetItem(key, &value)) {
lua_pushlstring(L, value.c_str(), value.length());
return 1;
}
完整代码如下。
//lua_cocos2dx_localstorage_manual.h
#ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_LOCALSTORAGE_MANUAL_H
#define COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_LOCALSTORAGE_MANUAL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif
TOLUA_API int register_localstorage_module(lua_State* L);
#endif // #ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_CONTROLLER_MANUAL_H
//lua_cocos2dx_localstorage_manual.cpp
#include "scripting/lua-bindings/manual/localstorage/lua_cocos2dx_localstorage_manual.h"
#include "scripting/lua-bindings/manual/tolua_fix.h"
#include "scripting/lua-bindings/manual/LuaBasicConversions.h"
#include "storage/local-storage/LocalStorage.h"
int lua_cocos2dx_localstorage_setItem(lua_State* L)
{
int argc = 0;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(L,1,"cc.LocalStorage",0,&tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(L) - 1;
if (argc == 2)
{
#if COCOS2D_DEBUG >= 1
if (!tolua_isstring(L, 2, 0, &tolua_err) ||
!tolua_isstring(L, 3, 0, &tolua_err))
goto tolua_lerror;
#endif
std::string key = tolua_tostring(L, 2, "");
//std::string value = tolua_tostring(L, 3, "");
size_t size = 0;
const char* data = (const char*)lua_tolstring(L, 3, &size);
std::string value(data, size);
localStorageSetItem(key, value);
return 0;
}
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L,"#ferror in function 'lua_cocos2dx_localstorage_setItem'.",&tolua_err);
return 0;
#endif
}
int lua_cocos2dx_localstorage_getItem(lua_State* L)
{
int argc = 0;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(L,1,"cc.LocalStorage",0,&tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(L) - 1;
if (argc == 1)
{
#if COCOS2D_DEBUG >= 1
if (!tolua_isstring(L, 2, 0, &tolua_err))
goto tolua_lerror;
#endif
std::string key = tolua_tostring(L, 2, "");
std::string value;
if (localStorageGetItem(key, &value)) {
lua_pushlstring(L, value.c_str(), value.length());
return 1;
}
}
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L,"#ferror in function 'lua_cocos2dx_localstorage_getItem'.",&tolua_err);
return 0;
#endif
}
int lua_cocos2dx_localstorage_removeItem(lua_State* L)
{
int argc = 0;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(L,1,"cc.LocalStorage",0,&tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(L) - 1;
if (argc == 1)
{
#if COCOS2D_DEBUG >= 1
if (!tolua_isstring(L, 2, 0, &tolua_err))
goto tolua_lerror;
#endif
std::string key = tolua_tostring(L, 2, "");
localStorageRemoveItem(key);
return 0;
}
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L,"#ferror in function 'lua_cocos2dx_localstorage_removeItem'.",&tolua_err);
return 0;
#endif
}
int lua_cocos2dx_localstorage_clear(lua_State* L)
{
int argc = 0;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(L, 1, "cc.LocalStorage", 0, &tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(L) - 1;
if (argc == 0)
{
localStorageClear();
return 0;
}
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L, "#ferror in function 'lua_cocos2dx_localstorage_removeItem'.", &tolua_err);
return 0;
#endif
}
int lua_cocos2dx_localstorage_Init(lua_State* L) {
int argc = 0;
#if COCOS2D_DEBUG >= 1
tolua_Error tolua_err;
if (!tolua_isusertable(L, 1, "cc.LocalStorage", 0, &tolua_err)) goto tolua_lerror;
#endif
argc = lua_gettop(L) - 1;
if (argc == 1) {
#if COCOS2D_DEBUG >= 1
if (!tolua_isstring(L, 2, 0, &tolua_err))
goto tolua_lerror;
#endif
std::string fullpath = tolua_tostring(L, 2, "");
localStorageInit(fullpath);
return 0;
}
return 0;
#if COCOS2D_DEBUG >= 1
tolua_lerror:
tolua_error(L, "#ferror in function 'lua_cocos2dx_localstorage_removeItem'.", &tolua_err);
return 0;
#endif
}
int lua_register_cocos2dx_localstorage(lua_State* L)
{
tolua_usertype(L,"cc.LocalStorage");
tolua_cclass(L,"LocalStorage","cc.LocalStorage","",nullptr);
tolua_beginmodule(L,"LocalStorage");
tolua_function(L, "init", lua_cocos2dx_localstorage_Init);
tolua_function(L, "setItem", lua_cocos2dx_localstorage_setItem);
tolua_function(L, "getItem", lua_cocos2dx_localstorage_getItem);
tolua_function(L, "removeItem", lua_cocos2dx_localstorage_removeItem);
tolua_function(L, "clear", lua_cocos2dx_localstorage_clear);
tolua_endmodule(L);
std::string typeName = "localStorage";
g_luaType[typeName] = "cc.LocalStorage";
g_typeCast["LocalStorage"] = "cc.LocalStorage";
return 1;
}
int register_all_cocos2dx_localstorage(lua_State* L)
{
tolua_open(L);
tolua_module(L,"cc",0);
tolua_beginmodule(L,"cc");
lua_register_cocos2dx_localstorage(L);
tolua_endmodule(L);
return 1;
}
int register_localstorage_module(lua_State* L)
{
lua_getglobal(L, "_G");
if (lua_istable(L,-1))//stack:...,_G,
{
register_all_cocos2dx_localstorage(L);
}
lua_pop(L, 1);
return 1;
}
还要为Android工程添加这俩文件,步骤如下:
找到文件 /frameworks/cocos2d-x/cocos/scripting/lua-bindings/CMakeLists.txt
添加上述的头文件和源文件:
set(
...
manual/localstorage/lua_cocos2dx_localstorage_manual.h
)
set(
...
manual/localstorage/lua_cocos2dx_localstorage_manual.cpp
)
com.enhance.gameservice
IGameTuningService.java 将这个文件下载下来配置到对应目录下即可,备份一下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/junaid/Documents/IphoneX/cocos2dx/Cocos2d-x-3.6/cocos2d-x/Countly/CountlyX/cocos2d/cocos/platform/android/java/src/com/enhance/gameservice/IGameTuningService.aidl
*/
package com.enhance.gameservice;
public interface IGameTuningService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.enhance.gameservice.IGameTuningService {
private static final java.lang.String DESCRIPTOR = "com.enhance.gameservice.IGameTuningService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.enhance.gameservice.IGameTuningService interface,
* generating a proxy if needed.
*/
public static com.enhance.gameservice.IGameTuningService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.enhance.gameservice.IGameTuningService))) {
return ((com.enhance.gameservice.IGameTuningService) iin);
}
return new com.enhance.gameservice.IGameTuningService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setPreferredResolution: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.setPreferredResolution(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_setFramePerSecond: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.setFramePerSecond(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_boostUp: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.boostUp(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getAbstractTemperature: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getAbstractTemperature();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_setGamePowerSaving: {
data.enforceInterface(DESCRIPTOR);
boolean _arg0;
_arg0 = (0 != data.readInt());
int _result = this.setGamePowerSaving(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.enhance.gameservice.IGameTuningService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public int setPreferredResolution(int resolution) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(resolution);
mRemote.transact(Stub.TRANSACTION_setPreferredResolution, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int setFramePerSecond(int fps) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(fps);
mRemote.transact(Stub.TRANSACTION_setFramePerSecond, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int boostUp(int seconds) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(seconds);
mRemote.transact(Stub.TRANSACTION_boostUp, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int getAbstractTemperature() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAbstractTemperature, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int setGamePowerSaving(boolean enable) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(((enable) ? (1) : (0)));
mRemote.transact(Stub.TRANSACTION_setGamePowerSaving, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setPreferredResolution = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setFramePerSecond = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_boostUp = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getAbstractTemperature = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_setGamePowerSaving = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
}
public int setPreferredResolution(int resolution) throws android.os.RemoteException;
public int setFramePerSecond(int fps) throws android.os.RemoteException;
public int boostUp(int seconds) throws android.os.RemoteException;
public int getAbstractTemperature() throws android.os.RemoteException;
public int setGamePowerSaving(boolean enable) throws android.os.RemoteException;
}
常见错误
Invalid revision: 3.18.1-g262b901-dirty
解决:这个是因为使用了较高版本CMake导致,手动修改使用较低版本即可,步骤如下:
- 打开AndroidStudio菜单的Tools - SDK Manager,选择 SDK Tools , 勾选「Show Package Details」,向下滑动一直找到CMake,把CMake高版本的都去掉,只保留一个3.6版本的。
- 这个时候可能还不行,还需要删除安卓SDK目录(例如:D:\Android\Sdk)下的临时文件夹(.temp),清除缓存后才能生效。
gradle Task :Demo:lint FAILED
解决:这个是因为编译的时候把lint警告也当做错误,从而终止了编译。解决办法就是不把警告当错误即可,在app/build.gradle
文件中添加配置:
android {
//...
lintOptions {
abortOnError false
}
}
参考: Gradle build: Execution failed for task :app:lint
错误:cvc-complex-type.2.4.a: 发现了以元素 'base-extension' 开头的无效内容。应以 '{layoutlib}' 之一开头。
解决:升级Gradle。
Unable to start the daemon process.
The project uses Gradle 4.6 which is incompatible with Java 11 or newer.
Possible solution:
- Upgrade Gradle wrapper to 7.2 version and re-import the project
// 解决办法:settings搜索gradle,修改gradle sdk版本为1.8
No version of NDK matched the requested version 20.0.5594570. Versions available locally: 23.1.7779620, 25.1.8937393, 25.2.9519653, 26.1.10909125
// 解决办法:local.properties添加:
ndk.dir=D\:\\NDK\\android-ndk-r19c
// 新版本在gradle里配置:
android {
ndkVersion = "19.2.5345600" //"major.minor.build"
}
ld: error: lib/libluacocos2d.a(CCLuaEngine.cpp.o): unable to find library from dependent library specifier: lua51.lib
// 解决办法:
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#pragma comment(lib,"lua51.lib")
#endif
Cocos 4.0新建lua项目安卓打包报错,CCLuaEngine,lua51.lib
API 'variant.getMergeAssets()' is obsolete and has been replaced with 'variant.getMergeAssetsProvider()'.
// 解决办法:修改build.gradle,将:
variant.mergeAssets.doLast {
}
// 修改为:
variant.mergeAssetsProvider.get().doLast {
}
This app only has 32-bit [armeabi-v7a] native libraries. Beginning August 1, 2019 Google Play store requires that all apps that include native libraries must provide 64-bit versions. For more information, visit https://g.co/64-bit-requirement
Affected Modules: Bricks
// 解决办法:修改gradle.properties
// PROP_APP_ABI=armeabi-v7a
PROP_APP_ABI=arm64-v8a
进阶技巧
剥离libcocos2dx模块
移除jni
工程,因为AndroidStudio
加载cpp太卡了,修改调试c++
代码还是用VisualStudio
比较丝滑。直接注释下面的代码即可:
// 临时注释:工程不再包含cpp项目,否则太卡了
// externalNativeBuild {
// if (project.hasProperty('PROP_REBUILD_NDK') && PROP_REBUILD_NDK=='true') {
// if (PROP_BUILD_TYPE == 'ndk-build') {
// ndkBuild {
// path "jni/Android.mk"
// }
// } else if (PROP_BUILD_TYPE == 'cmake') {
// cmake {
// path "../../../../CMakeLists.txt"
// }
// }
// }
// }
为了灵活期间,可增加一个配置开关来控制(build.gradle
):
// 通过修改 gradle.properties 中的 PROP_BUILD_NDK 开关(true/false),来决定是否导入cocos的jni工程
externalNativeBuild {
if (project.hasProperty('PROP_BUILD_NDK') && PROP_BUILD_NDK=='true') {
if (PROP_BUILD_TYPE == 'ndk-build') {
ndkBuild {
path "jni/Android.mk"
}
} else if (PROP_BUILD_TYPE == 'cmake') {
cmake {
path "../../../../CMakeLists.txt"
}
}
}
}
gradle.properties
文件中增加一个 PROP_BUILD_NDK
默认设置为false
即可:
PROP_REBUILD_NDK=true
# uncomment it and fill in sign information for release mode
RELEASE_STORE_FILE=../sign.keystore
RELEASE_STORE_PASSWORD=xxxxx
RELEASE_KEY_ALIAS=xxx
RELEASE_KEY_PASSWORD=xxxxx
##
单独编译SO
单独编译so库,这样后面在AndroidStudio里可以直接使用库文件,可以大大降低对电脑性能的依赖。
以3.17.2为例说明,该版本编译lib库文件所需的cmake版本是3.6,ndk是16,但是稍微高点也没关系,如下就是使用的NDK19.2.5345600(NDK21.4.7075529也可以)以及CMake3.10.2.4988404进行编译的。
最好通过手动指定目录的方式来去编译,这样不影响电脑的整体环境设置,也就不会影响其他项目的编译,也就是本着谁的项目谁自己管理自己所需的工具。
BuilSO.bat
:
@REM set abi=armeabi-v7a
set abi=arm64-v8a
set ANDROID_SDK_HOME=D:/Android/Sdk
set ANDROID_NDK_HOME=D:/Android/Sdk/ndk/19.2.5345600
set CMAKE_VERSION=3.10.2.4988404
set CMAKE=%ANDROID_SDK_HOME%/cmake/%CMAKE_VERSION%/bin/cmake
set NINJA=%ANDROID_SDK_HOME%/cmake/%CMAKE_VERSION%/bin/ninja
if not exist %abi% md %abi%
cd %abi%
%CMAKE% ^
-DANDROID_ABI=%abi% ^
-DANDROID_NDK=%ANDROID_NDK_HOME% ^
@REM -DCMAKE_BUILD_TYPE=Debug ^
-DCMAKE_BUILD_TYPE=Release^
-DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%/build/cmake/android.toolchain.cmake ^
-DANDROID_NATIVE_API_LEVEL=16 ^
-DANDROID_STL=c++_static ^
-DANDROID_TOOLCHAIN=clang -DCMAKE_GENERATOR="Ninja" ^
-DCMAKE_MAKE_PROGRAM=%NINJA% ^
..
%NINJA%
cd ..
把该批处理文件放置在和CMakeLists.txt
同级目录下,运行后会创建一个临时目录,目录下会有生成的so文件,复制出来使用即可。为了优化so的编译体积,可以在CMakeLists.txt
中增加如下设置:
# 优化编译的so体积
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Os -Wall -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -Wall -s")
##
日常开发
- 文件目录结构组织如下:
WinAppRun.bat # 运行Win版可执行程序
libs # 存放lib文件的
arm64-v8a
libcocos2dlua.so
frameworks
res # 游戏资源目录
src # 游戏脚本目录
runtime
simulator
.cocos-project.json
.project
CMakeLists.txt
config.json
README.md
UserDefault.xml
维持
cocos2dx
的命令行创建的项目工程目录不变,需要做的主要是修改目录res
和src
下的文件。使用
CocosStudio
设计的UI素材发布后直接复制到res
目录下即可。src
下就是编辑脚本代码。在Windows上使用VisualStudio编译的可执行程序运行,方便直接查看运行效果,效率会高很多,一旦将要发布的时候再进行编译打包。
注意:有些游戏是竖屏玩法,这个时候需要修改项目根目录下的配置文件:
config.json
, 将isLandscape
修改为false
(默认是true
)。-
运行测试:在项目的根目录下(res所在目录)创建一个
WinAppRun.bat
,日常测试就运行它就可以了,非常方便。call .\simulator\win32\AppName.exe
如果有少量的代码需要编写Java代码时,可以用AndroidStudio打开项目进行Java代码的编写。如果是C++代码,仍然建议使用VisualStudio进行编写,效率会高很多。
打包发布
需要注意的是,发布阶段需要对源码进行编译加密,这个时候会用到cocos.bat,会用到Python,需要先切换下Python的版本为2.7:
pyenv local 2.7.18
Windows
VisualStudio直接编译,也可以使用VisualStudio的命令行编译。
- 可以稍微修改下代码,让
_defaultResRootPath
、s_resourcePath
在release模式下分别为当前运行目录、Resources
- 复制res和src到Resources目录下即可。
Android
1、命令行(推荐)
直接进入项目目录下,使用
如果需要使用不同的gradle版本,修改frameworks\runtime-src\proj.android\gradle\wrapper\gradle-wrapper.properties
即可,例如:
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-bin.zip
打开命令行窗口,先设置下Python版本为2.7:
pyenv local 2.7.18
然后继续执行命令:
.\gradlew assembleRelease
如果需要设定Java版本时,直接修改gradlew.bat
的内容,手动设定下JAVA_HOME
:
# 增加下面这句
set JAVA_HOME=D:\Java\jdk1.8.0_191
# 或
set JAVA_HOME=D:\Java\jdk-17.0.13+11
2、AndroidStudio
略。
三方SDK
SDK | 功能作用简介 | 官方链接 |
---|---|---|
Google Play Services | 提供了广泛的 Google API 功能,包括广告服务、地图、身份验证等。 | Google Play Services SDK |
提供了 Facebook 社交平台集成,包括登录、分享、分析等功能。 | Facebook SDK for Android | |
Facebook Audience Network | 将 Audience Network SDK 添加到您的 Android 应用 | |
AppLovin | 提供了移动广告解决方案,包括广告展示、收益最大化等功能。others-AppLovin广告接入_applovin 拉取applovin官方广告 | AppLovin SDK |
Unity Ads | 提供了 Unity 游戏开发平台的广告服务,用于在游戏中展示广告并获取收益。 | Unity Ads SDK |
Firebase | 提供了一系列的移动应用开发工具,包括分析、远程配置、消息推送等功能,全面支持应用开发和增长。将 Firebase 添加到您的 Android 项目 Firebase for Android | Firebase SDK |
appsflyer | ||
使用入门:在 Android 项目中使用 AdMob Firebase with Google AdMob |
参考资料
文档信息
- 本文作者:zhupite
- 本文链接:https://zhupite.com/cocos/cocos2dx.html
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)