【英文论坛获奖教程】基于Cocos2d-x3.0集成物理特性的精确点击测试

基于Cocos2d-x3.0集成物理特性的精确点击测试

原作者: xhcnb
英文论坛原帖:
http://discuss.cocos2d-x.org/t/cocos3-0-tutorial-accurate-hit-testing-with-cocos2d-x-3-0-integrated-physics-feature/13393

Cocos2d-x 3.0版本有一个基于chipmunk的集成物理特效。这非常容易用来作为Cocos2d-x游戏的精确点击测试。
源代码(需要有github账号)
https://github.com/xhcnb/accurate_hit_testing_cocos2d_x_3_0

如何运行起来?

克隆源代码,android的复制cocos2d文件夹下的cocos2d0-x 3.0版本,文档看起来是这样的:

打开proj.ios_mac/PhysicsDemo.xcodeproj 来创建iOS或Mac
运行proj.android/build_native.py 来建立android项目

我已经把编译好的文件放置在“prebuilt编译好的”文件夹中,请检查并测试。

基本原则

   这里是一个开源物体躯干编辑器https://code.google.com/p/box2d-editor/ ,

有了这个开放并强有力的工具我们就能得到一个关于精灵物理躯干的多边形边框。然后我们导入多边形边框的数据到游戏中,
让物理引擎处理剩下的事情。

所有我们该做的事就是写一个解析函数,一个能够读取导出的文件并载入到cocos2d-x的物理引擎的解析函数。

我们开始吧!
  1. 创建新项目:
    cocos new -p com.example.phy -l cpp -d ~/Documents PhysicsDemo

  2. 在Xcode里打开新项目(我是用的是Xcode,你可以选择你自己喜欢的编译环境)
    open ~/Documents/PhysicsDemo/proj.ios_mac/PhysicsDemo.xcodeproj

  3. 制作一张测试用的图像,我是下载了cocos2d-x 的商标,从
    http://www.cocos2d-x.org/wiki/Logo_Resources_of_Cocos2d-x
    将其命名为 2dx.png,把图像添加在项目里。

  4. 下载box2d-editor
    https://code.google.com/p/box2d-editor/
    找到并打开jar类型文件
    注意:请使用JRE 6 来运行它。

  5. 获得躯干数据


    打开编辑器,创建一个新的项目,命名为bodies.json并将其保存在项目的Resource文件夹里。

别忘了添加把bodies.json添加到项目。

创建一个新的躯干定义,并命名为2dx。




使用Auto-trace按钮获得图像的边缘。


保存bodies.json

接下来,我们应该写一些代码来带入bodies.json。

  1. 才修改你的场景

编辑方法HelloWorld::createScene 以此启用物理特效。

auto scene = Scene::createWithPhysics();
//enable debug draw
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
```

把我们的精灵添加到层里。
   
 // add "2dx.png"
    sp_2dx = Sprite::create("2dx.png");
    // position the sprite on the center of the screen
    sp_2dx->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    //load
    MyBodyParser::getInstance()->parseJsonFile("bodies.json");
    //bind physicsbody to sprite
    auto _body = MyBodyParser::getInstance()->bodyFormJson(sp_2dx, "2dx");
    if (_body != nullptr) {
        _body->setDynamic(false); //set it static body.
        _body->setCollisionBitmask(0x000000); //don't collision with anybody.
        sp_2dx->setPhysicsBody(_body);
    }
    // add the sprite as a child to this layer
    this->addChild(sp_2dx, 0);
```

MyBodyParser is the helper class to load bodies.json, use rapid json.
MyBodyParser 就是用来读取bodies.json的类,使用快速json。


你能在源码中查询想要的细节,关键函数是MyBodyParser::bodyFormJson。之后添加touchListener,这个过程较为简单并且没有什么值得多费口舌来讲解。

7. 确认是否我们的精灵已经被点击

在函数HelloWorld::nodeUnderTouch中:

Node* HelloWorld::nodeUnderTouch(cocos2d::Touch *touch)
{
    Node* node = nullptr;
    //translate the touch to layer coordinate.
    auto location = this->convertTouchToNodeSpace(touch);

    //retrive all physics shapes under the touch location though cocos2d-x.
    auto scene = Director::getInstance()->getRunningScene();
    auto arr = scene->getPhysicsWorld()->getShapes(location);

    //iterate the shapes, find our sprite!
    for (auto& obj : arr)
    {
        //find it!!!!
        if ( obj->getBody()->getNode() == sp_2dx)
        {
            node = obj->getBody()->getNode();
            break;
        }
    }
    return node;
}
```

如果这个函数返回nullptr,这意味着你并没有碰触到精灵物理躯干的内部。
这些就是这篇教程所有的奇妙之处了,确认源代码并阅读更多的信息。

好文章,顶起来

mark一记 支持楼主:2::2::2:

这个不是老徐嘛

不错,顶起来

顶起来…

我用的是3.2,在win7下编译,程序执行到doc"rigidBodies"] 这里出现了 assert 错误。
请问这是什么原因呢?

妹的,弄错个地方,没事了 :3:

MyBodyParser::bodyFormJson
这个函数有段代码不太理解,卤煮可否出来解释一下,:904:
主要是不明白offx, offy 的意义, 还有下面的points.x,points.y 为什么都是用width去乘系数,而不是用height?
当然我自己用height测试了,结果会偏移,但是原因不太理解。

float width = pNode->getContentSize().width;
float height = pNode->getContentSize().height;
float offx = - pNode->getAnchorPoint().xpNode->getContentSize().width;
float offy = - pNode->getAnchorPoint().y
pNode->getContentSize().height;

                //Point origin( bd"origin"]"x"].GetDouble(), bd"origin"]"y"].GetDouble());
                rapidjson::Value &polygons = bd"polygons"];
                for (int i = 0; i<polygons.Size(); ++i)
                {
                    int pcount = polygons*.Size();
                    Point* points = new Point;
                    for (int pi = 0; pi<pcount; ++pi)
                    {
                        points.x = offx + width * polygons*"x"].GetDouble();
                        points.y = offy + width * polygons*"y"].GetDouble();
                    }
                    body->addShape(PhysicsShapePolygon::create(points, pcount, PHYSICSBODY_MATERIAL_DEFAULT));
                    delete ] points;
                }***

学习了
厉害:2::2::2:

这样的!学习了!

:14: 不错 顶一下

:14::14::14:

:2: :2: :2: :2:

auto arr = scene->getPhysicsWorld()->getShapes(location); 原来这么用。。。学习了