Google Play 对 APK 大小限制是 100 M,但是游戏稍微重度一点,资源就会很多,包体很容易就超过了这个限制;Google Play 提供了 obb 分包方案,来解决包体问题。
OBB 是 Opaque Binary Blob 的缩写,是一种类型 zip 文件格式,作为安卓应用的扩展数据包。
参考:安卓开发指南和百度百科
上面打不开的话,试试这个:https://developer.android.google.cn/google/play/expansion-files
游戏多数资源都不需要在启动游戏时,就加载到内存,通常都是在游戏运行期间,需要时再动态去加载;因此可以:
/Android/obb/包名/obb文件
路径;根据上面的思路,这里写了一个 demo,供大家参数,cocos creator 版本 2.2.0,过程如下:
编辑器中设置如图:
4. 打开 HelloWorld 脚本,新建一个 Sprite 变量,关联到刚刚新建的精灵
5. 在 onLoad 方法动动态加载图片
onLoad: function () {
this.label.string = this.text;
// 动态加载资源代码
cc.loader.loadRes("icon", cc.SpriteFrame, ((err, spf)=>{
if (err) {
console.error(err.message || err);
return;
}
this.sp.spriteFrame = spf;
}));
},
这时候可以用网页运行一下:
这一步就是我们平时正常的项目开发流程,前期是不用管obb分包和打包的,只管开发功能就行。等功能开发好之后,才需要进一步的对接渠道,打包,分包等。
制作 obb 分包,用压缩软件将 raw-assets 压缩成 zip 文件,然后改名为 main.1.org.cocos2d.helloworld.obb
,命名规则是 [main/patch].[versionCode].[packageName].obb
,到这一步 obb 分包就制作完成了。
打包 APK,将分离 obb 文件之后工程,打包成 apk。此时如果运行 apk,图片资源是加载不出来的。
找资源,分离资源,打包重命名,过程都很繁琐,建议通过脚本自动化完成
经过上一步构建和分离资源得到了一个 obb 文件,如果是正式上线,需要将分离资源后的工程打包成 apk,然后将 obb 文件 和 apk 一起,提交到 Google Play,玩家下载 apk 时,就会将 obb文件一起下载到手机中的 /Android/obb/包名
中。为了测试,直接把生成的 apk 直接拷贝到这个目录中:
obb 文件已经下载到手机之后,游戏首次启动需要先将 obb 文件解压到指定目录。为了能找到资源,还需要将解压的目录,加入到搜索路径中。
onLoad: function () {
// 将解压目录添加到资源搜索路径中
if (window.jsb) {
let obbDir = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "/") + "obb")
jsb.fileUtils.addSearchPath(obbDir, true);
console.log(obbDir);
}
this.label.string = this.text;
cc.loader.loadRes("icon", cc.SpriteFrame, ((err, spf)=>{
if (err) {
console.error(err.message || err);
return;
}
this.sp.spriteFrame = spf;
}));
},
String obbFilepath = Utils.getInstance().getObbFilepath();
String outPath = "/data/data/org.cocos2d.helloworld/files/obb/res";
Utils.unzipFile(obbFilepath, outPath);
// 获取 obb 文件路径
public String getObbFilepath() {
try {
Cocos2dxActivity context = (Cocos2dxActivity) AppActivity.getContext();
String packageName = context.getPackageName();
int versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode;
return context.getObbDir().getPath()
+ File.separator
+ "main."
+ versionCode
+ "."
+ packageName
+ ".obb";
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "getFilepath fail");
return "";
}
}
// 解压 zip 文件
public static boolean unzipFile(String zipFilepath, String unzipPath) {
try {
File zipFile = new File(zipFilepath);
if (!zipFile.exists()) {
Log.e(TAG, "no zip file " + zipFilepath);
return false;
}
File outDir = new File(unzipPath);
if (!outDir.exists()) {
if (!outDir.mkdirs()) {
Log.e(TAG, "create unzip dir fail");
return false;
}
}
ZipInputStream zipStream = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry zipEntry;
String entryName;
while ((zipEntry = zipStream.getNextEntry()) != null) {
entryName = zipEntry.getName();
if (zipEntry.isDirectory()) {
entryName = entryName.substring(0, entryName.length() - 1);
File subDir = new File(unzipPath + File.separator + entryName);
if (!subDir.exists() && !subDir.mkdirs()) {
Log.e(TAG, "create unzip sub dir fail " + subDir.getName());
return false;
}
} else {
File subFile = new File(unzipPath + File.separator + entryName);
String parentPath = subFile.getParent();
if (parentPath == null) {
Log.e(TAG, "get sub file parent dir fail" + subFile.getName());
return false;
}
File parentDir = new File(parentPath);
if (!parentDir.exists() || !parentDir.isDirectory()) {
if (!parentDir.mkdirs()) {
Log.e(TAG, "create sub file parent dir fail" + subFile.getName());
return false;
}
}
if (!subFile.createNewFile()) {
Log.e(TAG, "create sub file fail" + subFile.getName());
return false;
}
FileOutputStream out = new FileOutputStream(subFile);
int len;
byte[] buffer = new byte[1024];
while ((len = zipStream.read(buffer)) != -1) {
out.write(buffer, 0, len);
out.flush();
}
out.close();
}
}
zipStream.close();
return true;
} catch (Exception e) {
Log.e(TAG, "unzipFile fail exception " + zipFilepath + " " + unzipPath);
e.printStackTrace();
return false;
}
}
*** 若是压缩包内容很大,可以考虑用C++实现解压 ***
obb 分包制作流程就是这样的,但是有几点内容还需要进一步的完成:
最后,有问题可以加Q群(830756115)讨论。