古法编程:cocos2dx开发微信小游戏,聊聊踩过的坑

## 一、环境准备

### 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开发的微信小游戏效果

内容概要:本文围绕可变桨叶四旋翼无人机的规范控制与点对点运动模拟展开,重点研究优化推力分配策略在翻转动作中的应用与性能比较。通过Matlab代码实现,构建了四旋翼动力学模型,并设计了多种控制算法以实现精确的姿态调整与轨迹跟踪。研究对比了不同推力分配方案在执行高机动性翻转动作时的稳定性、能耗效率与响应速度,旨在提升无人机在复杂飞行任务中的动态性能与控制精度。该仿真研究为无人机飞控系统的设计与优化提供了理论依据和技术支持。; 适合人群:具备一定自动控制理论基础和Matlab编程能力,从事无人机控制、飞行器动力学或机器人系统研究的科研人员及研究生。; 使用场景及目标:① 实现四旋翼无人机在三维空间中的精确点对点运动控制;② 对比分析不同推力分配策略在执行翻转等高难度动作时的控制效果与能耗表现,优化飞行性能;③ 为无人机自主飞行、特技飞行及复杂环境下的机动控制提供算法验证平台。; 阅读建议:此资源以Matlab仿真为核心,建议读者结合相关控制理论知识,深入理解代码实现细节,重点关注动力学建模、控制律设计与推力分配模块。在学习过程中,应动手调试参数,复现文中翻转动作的仿真结果,并尝试拓展至其他复杂飞行任务,以加深对无人机控制机理的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值