瘸腿蛤蟆原创笔记,欢迎转载,转载请标明出处: http://blog.csdn.net/notbaron/article/details/39161771
源码下载: http://download.csdn.net/detail/notbaron/7886717
上篇回顾
本篇名言:有两种东西,我们对它们的思考愈是深沉和持久,它们所唤起的那种愈来愈大的惊奇和敬畏就会充溢我们的心灵,这就是繁星密布的苍穹和我心中的道德律。[康德]
上篇中,蛤蟆学习了Box2d物理引擎中的自由落体实现,接下去蛤蟆继续学习其他的Box2d引擎的使用。这次我们使用Box2d物理引擎来学习作用力的情况。
理论介绍
本次的理论知识基本和上次基本一致,需要补充如下内容:
b2QueryCallback这个类是用于AABB查询的。就一个虚函数virtualbool ReportFixture(b2Fixture* fixture) = 0; 在查询AABB的时候,一有发现Fixture就会被调用,通过返回FALSE来结束查询 AABB。AABB类可以理解为一个方形的区域。
EventDispatcher _eventDispatcher事件分发器,这个变量在CCNode.h源文件中定义。继承于CCNode的类可以直接使用,用于指定事件处理的监听器。用于分发各种事件。
EventListener事件监听器类,该类继承于ref类。是事件监听的基本类。我们通过不同的回调函数来自定义监听。
b2MouseJoint类,这个鼠标接口关节类。在Body上指定一个点用于跟踪世界的点。这是个软约束有最大的力。允许约束进行拉伸,不需要巨大的力。这个类并没有在手册中出现。该类继承与base joint类,用于通过不同的方式约束两个Body。可以设置限制和电机。
b2Transform类,在b2Math.h头文件中定义,实现平移和旋转,用来表示钢架的位置和方向。其中有两个变量p和q。其中p表示位置有x和y坐标,q表示角度,q中有两个变量s和c分别是sin和cos值。
b2FrictionJointDef,该类定义在b2FrictionJoint.h头文件中,定义摩擦关节。包含一个结构体如下:b2FrictionJointDef()
{
type= e_frictionJoint;
localAnchorA.SetZero();
localAnchorB.SetZero();
maxForce= 0.0f;
maxTorque= 0.0f;
}
Type表示 关节类型,Box2d支持如下关节类型:
e_unknownJoint,
e_revoluteJoint,
e_prismaticJoint,
e_distanceJoint,
e_pulleyJoint,
e_mouseJoint,
e_gearJoint,
e_wheelJoint,
e_weldJoint,
e_frictionJoint,
e_ropeJoint,
e_motorJoint
localAnchorA设置BodyA的锚点
localAnchorB设置BodyB的锚点
maxForce设置最大摩擦力
maxTorque设置最大力矩
具体步骤
我们接着使用上次学习的模板,大家可以在
中下载这段代码。
1、修改RayCastNew的构造函数如下
RayCastNew()
{
b2MouseJoint* m_mouseJoint; //定义一个b2MouseJoint变量,鼠标接口类。
b2Vec2 m_mouseWorld;
b2Body* m_body;
b2Vec2 gravity;
gravity.Set(0.0f,0.0f);
m_world= new b2World(gravity);
constfloat32k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f,20.0f);
ground= m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape= &shape;
sd.density= 0.0f;
sd.restitution= k_restitution;
// Left vertical
shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.Set(b2Vec2(20.0f, -20.0f),b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.Set(b2Vec2(-20.0f, 20.0f),b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f* b2_pi);
xf1.p= xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0]= b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1]= b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2]= b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices,3);
b2FixtureDef sd1;
sd1.shape= &poly1;
sd1.density= 4.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f* b2_pi);
xf2.p= -xf2.q.GetXAxis();
vertices[0]= b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1]= b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2]= b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices,3);
b2FixtureDef sd2;
sd2.shape= &poly2;
sd2.density= 2.0f;
b2BodyDef bd;
bd.type= b2_dynamicBody;
bd.angularDamping= 2.0f;
bd.linearDamping= 0.5f;
bd.position.Set(0.0f,2.0);
bd.angle= b2_pi;
bd.allowSleep= false;
m_body= m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f,0.5f);
b2FixtureDef fd;
fd.shape= &shape;
fd.density= 1.0f;
fd.friction= 0.3f;
for (int i =0; i < 10; ++i)
{
b2BodyDef bd;
bd.type= b2_dynamicBody;
bd.position.Set(0.0f,5.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float32 gravity = 10.0f;
float32 I = body->GetInertia();
float32 mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f* I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA= ground;
jd.bodyB= body;
jd.collideConnected= true;
jd.maxForce= mass * gravity;
jd.maxTorque= mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
GLESDebugDraw * _debugDraw =newGLESDebugDraw(1.5);
m_world->SetDebugDraw(_debugDraw);
uint32 flags = 0;
flags+=b2Draw::e_shapeBit;
_debugDraw->SetFlags(flags);
m_world->DrawDebugData();
}
2、添加鼠标回调函数
在文件Box2dh中定义,Box2d.cpp中实现。
bool Box2D::onTouchBegan(Touch*touch, Event* event)
{
auto touchLocation =touch->getLocation();
auto nodePosition = convertToNodeSpace( touchLocation );
return m_test->MouseDown(b2Vec2(nodePosition.x,nodePosition.y));
}
void Box2D::onTouchMoved(Touch*touch, Event* event)
{
auto touchLocation =touch->getLocation();
auto nodePosition = convertToNodeSpace( touchLocation );
m_test->MouseMove(b2Vec2(nodePosition.x,nodePosition.y));
}
void Box2D::onTouchEnded(Touch*touch, Event* event)
{
auto touchLocation =touch->getLocation();
auto nodePosition = convertToNodeSpace( touchLocation );
m_test->MouseUp(b2Vec2(nodePosition.x,nodePosition.y));
}
3、真正处理的鼠标回调函数
这些函数放在RayCastNew.h文件中。
virtual bool MouseDown(const b2Vec2& p){
m_mouseWorld= p;
if (m_mouseJoint !=nullptr)
{
return false;
}
// Make a small box.
b2AABB aabb;
b2Vec2 d;
d.Set(0.001f,0.001f);
aabb.lowerBound= p - d;
aabb.upperBound= p + d;
// Query the world for overlapping shapes.
QueryCallback callback(p);
m_world->QueryAABB(&callback,aabb);
if (callback.m_fixture)
{
b2Body* body = callback.m_fixture->GetBody();
b2MouseJointDef md;
md.bodyA= ground;
md.bodyB= body;
md.target= p;
md.maxForce= 1000.0f * body->GetMass();
m_mouseJoint= (b2MouseJoint*)m_world->CreateJoint(&md);
body->SetAwake(true);
return true;
}
returnfalse;
}
virtualvoidMouseUp(constb2Vec2& p)
{
if (m_mouseJoint)
{
m_world->DestroyJoint(m_mouseJoint);
m_mouseJoint= nullptr;
}
}
void MouseMove(constb2Vec2& p)
{
m_mouseWorld= p;
if (m_mouseJoint)
{
m_mouseJoint->SetTarget(p);
}
}
4、HelloWorld.cpp文件中增加监听
auto listener =EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
_eventDispatcher->addEventListenerWithFixedPriority(listener, 1);
_touchListener= listener;
具体移动函数如下
void HelloWorld::onTouchMoved(Touch*touch, Event* event)
{
auto diff =touch->getDelta();
auto node = getChildByTag( 2 );
auto currentPos = node->getPosition();
node->setPosition(currentPos + diff);
}
代码解释
代码总体思路是这样的,
HelloWorld.cpp中的Layer,会调用Box2d这个Layer。然后在Box2d这个Layer中,调用RayCastNew类,该类继承于b2ContactListener。RayCastNew中的构造函数完成所有Body的定义及创建。该类中刚还定义了几个处理鼠标移动的函数MouseUp, MouseMove, MouseDown. 在Box2d的L ayer中注册了鼠标函数,会调用RayCastNew中的MouseUp, MouseMove, MouseDown函数。而HelloWorld这个 Layer也注册了鼠标函数,他们实现的是Box2d这个Layer的移动,方便显示。其他物理碰撞都是有Box2d源代码实现的哈,蛤蟆这里就先不管他们了。
1、构造函数介绍
RayCastNew()
{
b2MouseJoint* m_mouseJoint; //定义一个b2MouseJoint变量,鼠标接口类。
b2Vec2 m_mouseWorld; //定义一个变量
b2Body* m_body;//定义一个Body变量
b2Vec2 gravity;//定义一个重力向量
gravity.Set(0.0f,0.0f);//设置重力向量变量值
m_world= new b2World(gravity);//通过重力变量创建物理世界。
constfloat32k_restitution = 0.4f; //定义一个回复变量0.4
b2Body* ground;//定义一个地面的Body
{
b2BodyDef bd;//定义Body参数变量
bd.position.Set(0.0f,20.0f);//设置该Body位置
ground= m_world->CreateBody(&bd);//创建该Body,其他参数默认,默认是静态Body
b2EdgeShape shape;//定义边缘形状shape
b2FixtureDef sd;//定义fixture变量
sd.shape= &shape;//设置FIXTURE的shape
sd.density= 0.0f;//设置fixture密度
sd.restitution= k_restitution;//设置fixture回复力
shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(-20.0f, 20.0f));//设置shape的形状的左边线
ground->CreateFixture(&sd);//将Body和fixture绑定
shape.Set(b2Vec2(20.0f, -20.0f),b2Vec2(20.0f, 20.0f)); //设置shape的形状的右边边线
ground->CreateFixture(&sd); //将Body和fixture绑定
shape.Set(b2Vec2(-20.0f, 20.0f),b2Vec2(20.0f, 20.0f)); //设置shape的形状的上边线
ground->CreateFixture(&sd); //将Body和fixture绑定
shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(20.0f, -20.0f)); //设置shape的形状的下边线
ground->CreateFixture(&sd); //将Body和fixture绑定
}
{
b2Transform xf1; //定义一个b2Transform变量
xf1.q.Set(0.3524f* b2_pi);//设置弧度为0.352*3.14=1.1
xf1.p= xf1.q.GetXAxis();//设置p
b2Vec2 vertices[3];//定义数组
vertices[0]= b2Mul(xf1, b2Vec2(-1.0f, 0.0f)); //这里只要知道,是两个点处理之后,得到一个坐标点即可
vertices[1]= b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2]= b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;//定义多边形变量
poly1.Set(vertices,3); //设置多边形变量
b2FixtureDef sd1;//定义一个Fixture定义变量
sd1.shape= &poly1;//设置Fixture的shape为多边形
sd1.density= 4.0f; //设置Fixture的密度
b2Transform xf2; //定义另一个b2Transform变量
xf2.q.Set(-0.3524f* b2_pi); //设置弧度为-0.352*3.14=-1.1
xf2.p= -xf2.q.GetXAxis();//设置p
vertices[0]= b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1]= b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2]= b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2; //定义多边形变量
poly2.Set(vertices,3); //设置多边形变量
b2FixtureDef sd2; //定义一个Fixture定义变量
sd2.shape= &poly2; //设置Fixture的shape为多边形
sd2.density= 2.0f; //设置Fixture的密度
b2BodyDef bd;//定义一个Body定义的变量
bd.type= b2_dynamicBody; //设置Body为动态Body
bd.angularDamping= 2.0f; //设置Body为角度阻尼为2.0
bd.linearDamping= 0.5f; //设置Body为线性阻尼为0.5.
bd.position.Set(0.0f,2.0); //设置Body位置
bd.angle= b2_pi; //设置Body的角度为π(就是180°)
bd.allowSleep= false; //设置Body为不能睡眠
m_body= m_world->CreateBody(&bd);//创建该Body
m_body->CreateFixture(&sd1);//绑定body和fixture.
m_body->CreateFixture(&sd2); //绑定body和fixture.
}
{
b2PolygonShape shape; //定义一个多边形变量
shape.SetAsBox(0.5f,0.5f);//设置为正方形
b2FixtureDef fd; //定义一个Body定义的变量
fd.shape= &shape; //设置Fixture的shape为多边形
fd.density= 1.0f; //设置Fixture的密度
fd.friction= 0.3f; //设置Fixture的摩擦力
for (int i =0; i < 10; ++i) //循环,创建10个Body
{
b2BodyDef bd; 定义一个Body定义的变量
bd.type= b2_dynamicBody; //定义一个Body为动态Body
bd.position.Set(0.0f,5.0f + 1.54f * i); //设置Body位置
b2Body* body = m_world->CreateBody(&bd); //更具定义创建Body
body->CreateFixture(&fd);// //绑定body和fixture.
float32 gravity = 10.0f;
float32 I = body->GetInertia();//获得 Body的转动惯性,物理中我们知道
float32 mass = body->GetMass();//获取Body质量
// 对于一个圆圈: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f* I / mass);//通过转动惯量,获取半径。
b2FrictionJointDef jd; //定义一个摩擦关节
jd.localAnchorA.SetZero();//设置BodyA的锚点为0
jd.localAnchorB.SetZero();//设置BodyB的锚点为0
jd.bodyA= ground; //设置BodyA为地面
jd.bodyB= body; //设置BodyB的Body
jd.collideConnected= true;//设置关节允许碰撞
jd.maxForce= mass * gravity;//设置最大力是质量乘以重力
jd.maxTorque= mass * radius * gravity; //设置最大力是质量乘以半径和重力
m_world->CreateJoint(&jd);//根据关节定义创建这个关节(循环中每个Body都和Groud连接摩擦关节)
}
}
GLESDebugDraw * _debugDraw =newGLESDebugDraw(1.5);//定义GLESDebugDraw类,用于画底面
m_world->SetDebugDraw(_debugDraw);//注册GLESDebugDraw
uint32 flags = 0;
flags+=b2Draw::e_shapeBit;//设置需要画的内容
_debugDraw->SetFlags(flags);//加载需要画内容的位
m_world->DrawDebugData();//画出地面Body.
}
2、鼠标回调函数介绍
bool Box2D::onTouchBegan(Touch*touch, Event* event)
{
auto touchLocation =touch->getLocation(); //获取触摸位置坐标
auto nodePosition = convertToNodeSpace( touchLocation ); //转化坐标系
return m_test->MouseDown(b2Vec2(nodePosition.x,nodePosition.y));//调用MouseDown函数
}
void Box2D::onTouchMoved(Touch*touch, Event* event)
{
auto touchLocation =touch->getLocation(); //获取触摸位置坐标
auto nodePosition = convertToNodeSpace( touchLocation ); //转化坐标系
m_test->MouseMove(b2Vec2(nodePosition.x,nodePosition.y)); //调用MouseMove函数
}
void Box2D::onTouchEnded(Touch*touch, Event* event)
{
auto touchLocation =touch->getLocation(); //获取触摸位置坐标
auto nodePosition = convertToNodeSpace( touchLocation ); //转化坐标系
m_test->MouseUp(b2Vec2(nodePosition.x,nodePosition.y)); //调用MouseMove函数
}
3、真正处理的鼠标回调函数
virtual bool MouseDown(const b2Vec2& p){
m_mouseWorld= p; //传递按下鼠标的点,给变量m_mouseWorld
if (m_mouseJoint !=nullptr) //如果m_mouseJoint变量不为空,就退出
{
return false;
}
// Make a small box.
b2AABB aabb;//定义变量b2AABB,b2AABB是一个轴对齐的方形。
b2Vec2 d; //定义一个变量
d.Set(0.001f,0.001f);//设置变量的值
aabb.lowerBound= p - d;//根据数据按下的点,设置一个方形,设置aabb 的下限
aabb.upperBound= p + d;// 根据数据按下的点,设置一个方形,设置aabb 的上限。
// Query the world for overlapping shapes.
QueryCallback callback(p); //定义QueryCallback类,如果鼠标点上有Fixture就会调用ReportFixture函数并返回
m_world->QueryAABB(&callback,aabb);//调用QueryAABB,该函数在b2world.cpp中定义
if (callback.m_fixture) //如果点击的有fixture,这个变量在callback中
{
b2Body* body = callback.m_fixture->GetBody(); //通过Fixture获取body.
b2MouseJointDef md;//定义鼠标关节变量
md.bodyA= ground; //关节的一个BodyA是地面
md.bodyB= body; //关节的一个BodyB是Body
md.target= p;//定义关节的目标是鼠标点
md.maxForce= 1000.0f * body->GetMass();//定义关节的最大力
m_mouseJoint= (b2MouseJoint*)m_world->CreateJoint(&md); //根据关节定义创建一个鼠标关节。
body->SetAwake(true);//唤醒这个Body,我们可以看到唤醒和睡眠的时候Body颜色是不一样的。
return true;
}
returnfalse;
}
virtualvoidMouseUp(constb2Vec2& p)
{
if (m_mouseJoint) //如果关节变量不为空
{
m_world->DestroyJoint(m_mouseJoint);//毁掉该关节
m_mouseJoint= nullptr;//并赋值为null
}
}
void MouseMove(constb2Vec2& p)
{
m_mouseWorld= p;//获取鼠标移动到的点
if (m_mouseJoint)//如果鼠标关节不为空
{
m_mouseJoint->SetTarget(p);//重新设置鼠标关节的目标为新的点。
}
}
4、HelloWorld.cpp中的监听
如下代码主要是实现移动b2BOD 这个Layer的,当然大家可以去掉试试,去掉的话就不能移动了。
auto listener =EventListenerTouchOneByOne::create(); //创建一个监听变量
listener->setSwallowTouches(true);//设置触摸容许传递
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);//设置具体监听函数
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this); //设置具体监听函数
_eventDispatcher->addEventListenerWithFixedPriority(listener, 1);//分发监听设置
_touchListener= listener;//设置监听器
具体移动函数如下
void HelloWorld::onTouchMoved(Touch*touch, Event* event)
{
auto diff =touch->getDelta(); //获取移动的差异距离
auto node = getChildByTag( 2 );//获取tag为2 的 L ayer,这里就是box2d这个Layer
auto currentPos = node->getPosition();//获取该node的位置
node->setPosition(currentPos + diff);//设置该node的新位置
}