您的位置:首页 > 移动开发 > Cocos引擎

cocos2dx 3.x使用Box2d

2017-04-09 16:23 246 查看
众所周知cocs2dx 3.x中有个Physics的封装,本意可能是想同时封装chipmunk和box2d,但为什么只实现了chipmunk就不管box2d了呢。。

最近尝试了一下,chipmunk问题很多,只要PhysicsBody速度够快,即便是和staticBody或Edge碰撞都可能发生穿透,而且即使没有发生穿透,如果持续给一个body设置速度,它就会无视碰撞,堂而皇之的穿透出去。。

然后看了一下TestCpp中box2d相关的代码,发现box2d的表现明显更好。

cocos2d实现了cocos2d::PhysicsSprite,但它的实现却很有问题。

一是在每个PhysicsSprite中都监听Director::EVENT_AFTER_UPDATE事件,在其中同步Box2d世界和cocos世界中的坐标,但其实完全可以在cocos2d::Node::visit中进行同步
二是同步坐标时根本没有考虑父子结点的关系,只有在所有PhysicsSprite都直接挂接在Scene上时,才能正常工作
如果一个游戏场景中有ui、main、background等存在相对位移的层级结点,进行坐标同步时的基准结点应该是main结点,在绘制box2d的debug模式时,modelView矩阵也必须基于main结点

所以自己实现了一个简单的对box2d的封装 Box2dNode : public Node

它可以用来初始化Box2d b2World,并被设置为基准结点,基准结点监听Director::EVENT_AFTER_UPDATE以便进行b2World::Step,在draw时向渲染队列插入自定义事件,以便绘制box2d的debug信息。
它可以用来创建Box2d b2Body,在setPosition和setRotation时把cocos坐标系同步到box2d坐标系,在visit时把box2d坐标系同步到cocos坐标系。

关键代码如下:
基准结点对b2World进行Step
void Box2dNode::onEnter()
{
Node::onEnter();

if (m_body)
{
m_body->SetActive(true);
}
if (m_is_box2d_root)
{
//根结点要监听afterUpdate事件,用来step box2d
s_after_update_listener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(Director::EVENT_AFTER_UPDATE, std::bind(&Box2dNode::stepBox2d, this, std::placeholders::_1));
s_after_update_listener->retain();
}

}
void Box2dNode::stepBox2d(EventCustom *event)
{
Director* director = Director::getInstance();
s_world->Step(1 / 60.0f, 1, 1);
}
基准结点绘制box2d debug信息

void Box2dNode::draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags)
{
Node::draw(renderer, transform, flags);

//只有作为世界结点的结点才需要添加renderCommand
if (m_is_box2d_root != 0)
{
if (s_draw_command == 0)
{
s_draw_command = new CustomCommand();
}
s_draw_command->init(10, transform, flags);
s_draw_command->func = CC_CALLBACK_0(Box2dNode::onDraw, this, transform, flags);
renderer->addCommand(s_draw_command);
}
}

void Box2dNode::onDraw(const Mat4 &transform, uint32_t flags)
{
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);

GL::enableVertexAttribs(cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION);

s_world->DrawDebugData();

CHECK_GL_ERROR_DEBUG();

director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

}


坐标系同步:
void Box2dNode::setPosition(float x, float y)
{
if (m_body)
{
_position.x = x;
_position.y = y;

//把坐标转换到世界空间再设置给box2d
Node* parent = this->getParent();
if (parent)
{
AffineTransform mat = getAffineTransformToWorldRoot(parent);
mat = AffineTransformTranslate(mat, x, y);

x = mat.tx;
y = mat.ty;
}
float angle = m_body->GetAngle();
m_body->SetTransform(b2Vec2(x / PTM_RATIO, y / PTM_RATIO), angle);
}
else
{
Node::setPosition(x, y);
}
}

void Box2dNode::setRotation(float fRotation)
{
if (m_body)
{
_rotationZ_X = _rotationZ_Y = fRotation;
updateRotationQuat();

//把旋转转换到世界空间再设置给boxbody
b2Vec2 p = m_body->GetPosition();
float radians = CC_DEGREES_TO_RADIANS(fRotation);
Node* parent = this->getParent();
if (parent)
{
AffineTransform mat = getAffineTransformToWorldRoot(parent);
mat = AffineTransformRotate(mat, radians);

radians = acosf(mat.a);
}
m_body->SetTransform(p, radians);
}
else
{
Node::setRotation(fRotation);
}
}

void Box2dNode::visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags)
{
//通过box2d中的世界坐标信息计算transform
if (m_body)
{
b2Vec2 pos = m_body->GetPosition();
float radians = m_body->GetAngle();
AffineTransform world_xy = AffineTransformTranslate(AffineTransform::IDENTITY, pos.x * PTM_RATIO, pos.y * PTM_RATIO);
AffineTransform world_rot = AffineTransformRotate(AffineTransform::IDENTITY, radians);
AffineTransform world_trans = AffineTransformConcat(world_rot, world_xy);
Node* parent = this->getParent();
if (parent)
{
AffineTransform parent_world = AffineTransformInvert(getAffineTransformToWorldRoot(parent));
AffineTransform local_trans = AffineTransformConcat(world_trans, parent_world);

if (!_anchorPointInPoints.isZero())
{
local_trans = AffineTransformTranslate(local_trans, -_anchorPointInPoints.x, -_anchorPointInPoints.y);
}

_position.x = local_trans.tx;
_position.y = local_trans.ty;
_rotationZ_X = _rotationZ_Y = CC_RADIANS_TO_DEGREES(acosf(local_trans.a));

CGAffineToGL(local_trans, _transform.m);
_transformDirty = false;
_inverseDirty = true;
_transformUpdated = true;
}
}

//---------------------------------------------------------------
Node::visit(renderer, parentTransform, parentFlags);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: