cocos2dx 3.x使用Box2d

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

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

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

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

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

  1. 它可以用来初始化Box2d b2World,并被设置为基准结点,基准结点监听Director::EVENT_AFTER_UPDATE以便进行b2World::Step,在draw时向渲染队列插入自定义事件,以便绘制box2d的debug信息。
  2. 它可以用来创建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);
	}



你可能感兴趣的:(C++基础,cocos2d-x,Box2d)