COCOS2DX,cocostudio::ColliderDetector 简单介绍 骨骼动画绑定碰撞区域进行碰撞检测

/

#ifndef __CCCOLLIDERDETECTOR_H__

#define __CCCOLLIDERDETECTOR_H__

#include "cocostudio/CCArmatureDefine.h"

#include "cocostudio/CCDatas.h"

#ifndef PT_RATIO

#define PT_RATIO 32

#endif

#if ENABLE_PHYSICS_CHIPMUNK_DETECT

#include "chipmunk.h"

#elif ENABLE_PHYSICS_BOX2D_DETECT

#include "Box2D/Box2D.h"

#endif

namespace cocostudio {

class Bone;

/**

 *  @js NA

 *  @lua NA

 */

class ColliderFilter

{

public:

    virtual ~ColliderFilter() { }

#if ENABLE_PHYSICS_BOX2D_DETECT

public:

    ColliderFilter(uint16 categoryBits = 0x0001, uint16 maskBits = 0xFFFF, int16 groupIndex = 0);

    void updateShape(b2Fixture *fixture);

    virtual void setCategoryBits(uint16 categoryBits) { _categoryBits = categoryBits; }

    virtual uint16 getCategoryBits() const { return _categoryBits; }

    virtual void setMaskBits(uint16 maskBits) { _maskBits = maskBits; }

    virtual uint16 getMaskBits() const { return _maskBits; }

    virtual void setGroupIndex(int16 groupIndex) { _groupIndex = groupIndex; }

    virtual int16 getGroupIndex() const { return _groupIndex; }

protected:

    uint16 _categoryBits;

    uint16 _maskBits;

    int16 _groupIndex;

#elif ENABLE_PHYSICS_CHIPMUNK_DETECT

public:

    ColliderFilter(cpCollisionType collisionType = 0, cpGroup group = 0);

    void updateShape(cpShape *shape);

    virtual void setCollisionType(cpCollisionType collisionType) { _collisionType = collisionType; }

    virtual cpCollisionType getCollisionType() const { return _collisionType; }

    virtual void setGroup(cpGroup group) { _group = group; }

    virtual cpGroup getGroup() const { return _group; }

protected:

    cpCollisionType _collisionType;

    cpGroup _group;

#endif

};

//碰撞框
class ColliderBody : public cocos2d::Ref
{
public:
  ColliderBody(ContourData *contourData);
  ~ColliderBody();

  inline ContourData *getContourData() { return _contourData; }

#if ENABLE_PHYSICS_BOX2D_DETECT || ENABLE_PHYSICS_CHIPMUNK_DETECT 
  void setColliderFilter(ColliderFilter *filter);
  ColliderFilter *getColliderFilter();
#endif

#if ENABLE_PHYSICS_BOX2D_DETECT
  virtual void setB2Fixture(b2Fixture *fixture) { _fixture = fixture; }
  virtual b2Fixture *getB2Fixture() const { return _fixture; }
#elif ENABLE_PHYSICS_CHIPMUNK_DETECT
  virtual void setShape(cpShape *shape) { _shape = shape; }
  virtual cpShape *getShape() const { return _shape; }
#elif ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX
  virtual const std::vector<cocos2d::Point> &getCalculatedVertexList() const { return _calculatedVertexList; }
#endif

private:

#if ENABLE_PHYSICS_BOX2D_DETECT
  b2Fixture *_fixture;
  ColliderFilter *_filter;
#elif ENABLE_PHYSICS_CHIPMUNK_DETECT
  cpShape *_shape;
  ColliderFilter *_filter;
#elif ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX
  std::vector<cocos2d::Point> _calculatedVertexList;//所有顶点
#endif

  ContourData *_contourData;//轮廓数据

  friend class ColliderDetector;
};

/*
 *  @brief  ContourSprite used to draw the contour of the display
 *  @js NA
 *  @lua NA
 */
class ColliderDetector : public cocos2d::Ref
{
public:
  static ColliderDetector *create();
  static ColliderDetector *create(Bone *bone);
public:
  /**
  * @js ctor
  */
  ColliderDetector();
  /**
  * @js NA
  * @lua NA
  */
  ~ColliderDetector(void);

  virtual bool init();
  virtual bool init(Bone *bone);

  void addContourData(ContourData *contourData);//手动添加 一个 轮廓顶点信息数据
  void addContourDataList(cocos2d::Vector<ContourData*> &contourDataList);//手动添加 一组 轮廓顶点信息数据

  void removeContourData(ContourData *contourData);//手动移除 一个 轮廓顶点信息数据
  void removeAll();//手动添加 所有 轮廓顶点信息数据

  void updateTransform(kmMat4 &t);//旋转更新

  void setActive(bool active);//设置激活状态
  bool getActive();

  const cocos2d::Vector<ColliderBody*>& getColliderBodyList();//得到所有轮廓框信息

#if ENABLE_PHYSICS_BOX2D_DETECT || ENABLE_PHYSICS_CHIPMUNK_DETECT 
  virtual void setColliderFilter(ColliderFilter *filter);
  virtual ColliderFilter *getColliderFilter();
#endif

  virtual void setBone(Bone *bone) { _bone = bone; }
  virtual Bone *getBone() const { return _bone; }

#if ENABLE_PHYSICS_BOX2D_DETECT
  virtual void setBody(b2Body *body);
  virtual b2Body *getBody() const;
#elif ENABLE_PHYSICS_CHIPMUNK_DETECT
  virtual void setBody(cpBody *body);
  virtual cpBody *getBody() const;
#endif
 protected:
  cocos2d::Vector<ColliderBody*> _colliderBodyList;//碰撞body

  Bone *_bone;//骨骼

#if ENABLE_PHYSICS_BOX2D_DETECT
  b2Body *_body;
  ColliderFilter *_filter;
#elif ENABLE_PHYSICS_CHIPMUNK_DETECT
  cpBody *_body;
  ColliderFilter *_filter;
#endif

protected:
  bool _active;//是否激活
};

}

#endif /*__CCCOLLIDERDETECTOR_H__*/
class TestColliderDetector : public ArmatureTestLayer
{
public:
  ~TestColliderDetector();
  
  virtual void onEnter() override;
  virtual std::string title() const override;
  virtual void update(float delta);
  virtual void draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) override;
  void onDraw(const kmMat4 &transform, bool transformUpdated);
  
  void onFrameEvent(cocostudio::Bone *bone, const std::string& evt, int originFrameIndex, int currentFrameIndex);
  
  void initWorld() {};
  cocostudio::Armature *armature;
  cocostudio::Armature *armature2;
  
  CustomCommand _customCommand; //new render needed this for drawing primitives
  cocos2d::Sprite *bullet;
};
void TestColliderDetector::update(float delta)
{
  armature2->setVisible(true);
//
//	CCNode中两个个方法方法:
//	getContentSize用来获得节点原始大小。返回CGSize类型
//	getBoundingBox获得节点当前大小,即如果经过缩放那么就是缩放后的大小。返回CGRect类型。
//	有个问题最近才遇到,父精灵进行缩放处理,会对子精灵进行标记(boolean值),在实际绘制过程中会影响子精灵显示大小,但并不改变子精灵的getBoundingBox所获得的值,也就是只有直接setscale才会影响getBoundingBox数值。
//	CCSprite中有个方法:
//	getTextureRect返回精灵纹理大小,返回CGRect类型,并且是原始纹理大小,无关缩放。在一般情况下和getContentSize作用一样,但如果用TP处理过,还回值是实际纹理大小,留白部分会去除。这个在碰撞检测过程中经常用到。
  Rect rect = bullet->getBoundingBox();

  // This code is just telling how to get the vertex.
  // For a more accurate collider detection, you need to implemente yourself.
  const Map<std::string, Bone*>& map = armature2->getBoneDic();//得到骨骼字典
  for(const auto& element : map)//遍历字典
  {
    Bone *bone = element.second;
    ColliderDetector *detector = bone->getColliderDetector();

    if (!detector)//如否没有碰撞 跳过 继续遍历
      continue;
    //得到碰撞框列表
    const cocos2d::Vector<ColliderBody*>& bodyList = detector->getColliderBodyList();

    for (const auto& object : bodyList)
    {
      //得到一个碰撞框
      ColliderBody *body = static_cast<ColliderBody*>(object);
      //一个碰撞框包含的所有顶点
      const std::vector<Point> &vertexList = body->getCalculatedVertexList();

      float minx = 0, miny = 0, maxx = 0, maxy = 0;
       int length = (int)vertexList.size();//顶点个数
      for (int i = 0; i<length; i++)
      {
        Point vertex = vertexList.at(i);
        if (i == 0)
        {
          minx = maxx = vertex.x;//起点和终点 是 第一个点
          miny = maxy = vertex.y;
        }
        else//遍历所有点 得到4个最值
        {
          minx = vertex.x < minx ? vertex.x : minx;
          miny = vertex.y < miny ? vertex.y : miny;
          maxx = vertex.x > maxx ? vertex.x : maxx;
          maxy = vertex.y > maxy ? vertex.y : maxy;
        }
}
      Rect temp = Rect(minx, miny, maxx - minx, maxy - miny);//组成一个包含所有顶点的矩形

      if (temp.intersectsRect(rect))
      {
        //子弹矩形在碰撞区域内 碰撞框不可见
        armature2->setVisible(false);
      }
    }
  }
}
void TestColliderDetector::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)
{
  _customCommand.init(_globalZOrder);
  _customCommand.func = CC_CALLBACK_0(TestColliderDetector::onDraw, this, transform, transformUpdated);
  renderer->addCommand(&_customCommand);
}

void TestColliderDetector::onDraw(const kmMat4 &transform, bool transformUpdated)
{
  kmGLPushMatrix();
  kmGLLoadMatrix(&transform);
  
  armature2->drawContour();
  
  kmGLPopMatrix();
}
TestColliderDetector::~TestColliderDetector()
{
}
void TestColliderDetector::onEnter()
{
  ArmatureTestLayer::onEnter();

  scheduleUpdate();

  armature = Armature::create("Cowboy");
  armature->getAnimation()->play("FireWithoutBullet");
  armature->getAnimation()->setSpeedScale(0.2f);
  armature->setScaleX(-0.2f);
  armature->setScaleY(0.2f);
  armature->setPosition(Point(VisibleRect::left().x + 70, VisibleRect::left().y));

  /*
 * Set armature's frame event callback function
 * To disconnect this event, just setFrameEventCallFunc(nullptr);
 */
  armature->getAnimation()->setFrameEventCallFunc(CC_CALLBACK_0(TestColliderDetector::onFrameEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
  addChild(armature);

  armature2 = Armature::create("Cowboy");
  armature2->getAnimation()->play("Walk");
  armature2->setScaleX(-0.2f);
  armature2->setScaleY(0.2f);
  armature2->setPosition(Point(VisibleRect::right().x - 60, VisibleRect::left().y));
  addChild(armature2);

#if ENABLE_PHYSICS_BOX2D_DETECT || ENABLE_PHYSICS_CHIPMUNK_DETECT
  bullet = cocos2d::extension::PhysicsSprite::createWithSpriteFrameName("25.png");
#elif ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX
  bullet = Sprite::createWithSpriteFrameName("25.png");
#endif
  addChild(bullet);

  initWorld();
}
std::string TestColliderDetector::title() const
{
  return "Test Collider Detector";
}
void TestColliderDetector::onFrameEvent(Bone *bone, const std::string& evt, int originFrameIndex, int currentFrameIndex)
{
  CCLOG("(%s) emit a frame event (%s) at frame index (%d).", bone->getName().c_str(), evt.c_str(), currentFrameIndex);

  /*
 * originFrameIndex is the frame index editted in Action Editor
 * currentFrameIndex is the current index animation played to
 * frame event may be delay emit, so originFrameIndex may be different from currentFrameIndex.
 */

  Point p = armature->getBone("Layer126")->getDisplayRenderNode()->convertToWorldSpaceAR(Point(0, 0));
  bullet->setPosition(Point(p.x + 60, p.y));

  bullet->stopAllActions();
  bullet->runAction(CCMoveBy::create(1.5f, Point(350, 0)));
}
参考原地址:http://www.tuicool.com/articles/bUz6BrM

你可能感兴趣的:(animation,cocos2d-x,cocostudio,CCArmature)