## 一、环境准备
### 1\. Cocos2d\-x
- 版本:3\.17\.2
- 仓库地址:[https://github\.com/WuJiayiSH/cocos2d\-x](https://github.com/WuJiayiSH/cocos2d-x)
### 2\. Emscripten SDK \(emsdk\)
用于将 C\+\+ 编译为 WASM \+ JS
使用版本:3\.1\.10
```bash
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
# 安装并激活指定版本
./emsdk install 3.1.10
./emsdk activate 3.1.10
```
### 3\. H5 游戏包转微信小游戏包工具
工具地址:[https://developers\.weixin\.qq\.com/minigame/dev/guide/game\-engine/common\-adaptation\.html](https://developers.weixin.qq.com/minigame/dev/guide/game-engine/common-adaptation.html)
页面向下滚动即可找到对应转换工具。
### 4\. Cocos2d\-x 常见问题官方文档
[https://developers\.weixin\.qq\.com/minigame/dev/guide/game\-engine/common\-adaptation/Design/Cocos2dxExport\.html](https://developers.weixin.qq.com/minigame/dev/guide/game-engine/common-adaptation/Design/Cocos2dxExport.html)
---
## 二、踩坑记录
2024 年底开始尝试将 Cocos2d\-x 游戏转为微信小游戏包,因业余时间有限,历时一年多完成。以下为实际遇到的问题与修改方案。
### 1\. 可写目录权限问题
```cpp
// 修改前
bool FileUtilsEmscripten::init()
{
_defaultResRootPath = "/";
return FileUtils::init();
}
// 修改后
bool FileUtilsEmscripten::init()
{
_defaultResRootPath = "/";
auto pDir = getWritablePath();
if(0 != access(pDir.c_str(), 0)) {
mkdir(pDir.c_str(), 0777);
}
return FileUtils::init();
}
```
### 2\. 去掉引擎中的 CCASSERT
iOS 平台触发 CCASSERT 会导致崩溃,直接注释相关断言。
```cpp
void Node::addChild(Node *child, int localZOrder, int tag)
{
//CCASSERT( child != nullptr, "Argument must be non-nil");
//CCASSERT( child->_parent == nullptr, "child already added. It can't be added again");
addChildHelper(child, localZOrder, tag, "", true);
}
void Node::addChild(Node* child, int localZOrder, const std::string &name)
{
// CCASSERT(child != nullptr, "Argument must be non-nil");
// CCASSERT(child->_parent == nullptr, "child already added. It can't be added again");
addChildHelper(child, localZOrder, INVALID_TAG, name, false);
}
```
### 3\. Spine 版本兼容问题
项目使用较老 Spine 版本,需将引擎内 Spine 替换为 2\.x;
若项目本身使用 3\.x 则无需替换。
### 4\. 界面变形问题
由默认固定宽高导致,改为由 JS 传入实际屏幕宽高。
```cpp
// 修改前
GLViewImpl* GLViewImpl::create(const std::string& viewName, bool resizable)
{
auto ret = new (std::nothrow) GLViewImpl;
if(ret && ret->initWithRect(viewName, Rect(0, 0, 960, 640), 1.0f, resizable)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
// 修改后,s_width 和 s_height 由 JS 传入
/*
extern "C" {
EMSCRIPTEN_KEEPALIVE
void cocos_set_window_size( int width, int height)
{
s_width = width;
s_height = height;
}
}
*/
GLViewImpl* GLViewImpl::create(const std::string& viewName, bool resizable)
{
auto ret = new (std::nothrow) GLViewImpl;
if(ret && ret->initWithRect(viewName, Rect(0, 0, s_width, s_height), 1.0f, resizable)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
```
### 5\. 声音问题
改用 JS 播放音频,C\+\+ 仅将声音路径传递给 JS 层,由 JS 实现播放逻辑。
### 6\. 多触摸问题
JS 层接收触摸事件,透传给 C\+\+ 处理。
**JS 层触摸转发**
```javascript
var Module = WXGameKit.gameInstance.Module || (GameGlobal.Module = GameGlobal.Module || {});
const touchHandler = {
onTouchStart: function(event) {
event.touches.forEach(function(touch){
let percentX = touch.clientX / window.innerWidth;
let percentY = 1.0 - touch.clientY / window.innerHeight;
Module._cocos_onTouchStart(touch.identifier, percentX, percentY);
});
},
onTouchMove: function(event) {
event.touches.forEach(function(touch){
let percentX = touch.clientX / window.innerWidth;
let percentY = 1.0 - touch.clientY / window.innerHeight;
Module._cocos_onTouchMove(touch.identifier, percentX, percentY);
});
},
onTouchEnd: function(event) {
event.changedTouches.forEach(function(touch){
let percentX = touch.clientX / window.innerWidth;
let percentY = 1.0 - touch.clientY / window.innerHeight;
Module._cocos_onTouchEnd(touch.identifier, percentX, percentY);
});
},
onTouchCancel: function(event) {
Module._cocos_onTouchCancel(0, 0, 0);
},
};
wx.onTouchStart(touchHandler.onTouchStart.bind(touchHandler));
wx.onTouchMove(touchHandler.onTouchMove.bind(touchHandler));
wx.onTouchEnd(touchHandler.onTouchEnd.bind(touchHandler));
wx.onTouchCancel(touchHandler.onTouchCancel.bind(touchHandler));
```
**C\+\+ 层导出函数**
```cpp
extern "C" {
EMSCRIPTEN_KEEPALIVE
void cocos_onTouchStart(int touchId, float x, float y)
{
}
EMSCRIPTEN_KEEPALIVE
void cocos_onTouchMove(int touchId, float x, float y)
{
}
EMSCRIPTEN_KEEPALIVE
void cocos_onTouchEnd(int touchId, float x, float y)
{
}
EMSCRIPTEN_KEEPALIVE
void cocos_onTouchCancel(int touchId, float x, float y)
{
}
}
```
### 7\. Tilemap 路径问题
```cpp
// 修改前
void TMXMapInfo::internalInit(const std::string& tmxFileName, const std::string& resourcePath)
{
if (!tmxFileName.empty())
{
_TMXFileName = FileUtils::getInstance()->fullPathForFilename(tmxFileName);
}
if (!resourcePath.empty())
{
_resources = resourcePath;
}
_objectGroups.reserve(4);
// tmp vars
_currentString = "";
_storingCharacters = false;
_layerAttribs = TMXLayerAttribNone;
_parentElement = TMXPropertyNone;
_currentFirstGID = -1;
}
// 修改后
void TMXMapInfo::internalInit(const std::string& tmxFileName, const std::string& resourcePath)
{
if (!tmxFileName.empty())
{
if (tmxFileName.find_last_of("/") != string::npos)
{
string dir = tmxFileName.substr(0, tmxFileName.find_last_of("/") + 1);
_relativeTMXFileDir = dir;
}
_TMXFileName = FileUtils::getInstance()->fullPathForFilename(tmxFileName);
}
if (!resourcePath.empty())
{
_resources = resourcePath;
}
_objectGroups.reserve(4);
// tmp vars
_currentString = "";
_storingCharacters = false;
_layerAttribs = TMXLayerAttribNone;
_parentElement = TMXPropertyNone;
_currentFirstGID = -1;
}
```
```cpp
// 修改前
else if (elementName == "image")
{
TMXTilesetInfo* tileset = tmxMapInfo->getTilesets().back();
// build full path
std::string imagename = attributeDict["source"].asString();
tileset->_originSourceImage = imagename;
if (!_externalTilesetFullPath.empty())
{
string dir = _externalTilesetFullPath.substr(0, _externalTilesetFullPath.find_last_of('/') + 1);
tileset->_sourceImage = dir + imagename;
}
else if (_TMXFileName.find_last_of('/') != string::npos)
{
string dir = _TMXFileName.substr(0, _TMXFileName.find_last_of('/') + 1);
tileset->_sourceImage = dir + imagename;
}
else
{
tileset->_sourceImage = _resources + (!_resources.empty() ? "/" : "") + imagename;
}
}
// 修改后
else if (elementName == "image")
{
TMXTilesetInfo* tileset = tmxMapInfo->getTilesets().back();
// build full path
std::string imagename = attributeDict["source"].asString();
tileset->_originSourceImage = imagename;
if (!_externalTilesetFullPath.empty())
{
string dir = _externalTilesetFullPath.substr(0, _externalTilesetFullPath.find_last_of('/') + 1);
tileset->_sourceImage = dir + imagename;
}
else if (_TMXFileName.find_last_of('/') != string::npos)
{
string dir = _relativeTMXFileDir;
tileset->_sourceImage = dir + imagename;
}
else
{
tileset->_sourceImage = _resources + (!_resources.empty() ? "/" : "") + imagename;
}
}
```
### 8\. 可写路径持久化问题
默认可写路径重启后丢失,修改自定义可写路径。
```cpp
// 修改前
string FileUtilsEmscripten::getWritablePath() const
{
return "/cocos2dxWritablePath/";
}
// 修改后
string FileUtilsEmscripten::getWritablePath() const
{
return "/CustomWritablePath/";
}
```
游戏效果
感兴趣的同学,可以看看2dx开发的微信小游戏效果


224

被折叠的 条评论
为什么被折叠?



