Box2d 学习笔记 2个物体堆叠拖动及简单碰撞检测

声明:文章代码参考自子龙山人的撞球小游戏,链接地址在文章结尾

4月1日接触了Box2d,开始了物理引擎学习之路,这次要求的实现比较简单,就是实现多个(例子中2个)物体的堆叠,鼠标拖动和简单碰撞。

 

init函数

首先是类中添加成员函数及数据,世界,边框,2个paddle,鼠标关节,响应触屏事件

  
  
  
  
  1. b2World *_world; 
  2. b2Body *_groundBody; 
  3. b2Fixture *_bottomFixture; 
  4.  
  5. b2Body *_paddleBody; 
  6. b2Fixture *_paddleFixture; 
  7.  
  8. b2Body *_paddleBody2; 
  9. b2Fixture *_paddleFixture2; 
  10.  
  11. b2MouseJoint *_mouseJoint; 
  12.  
  13. void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent); 
  14. void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent); 
  15. void ccTouchesEnded(CCSet* pTouches, CCEvent* pEvent); 
  16.  
  17. void update(ccTime dt); 

 

然后是init函数,前面和HelloWorld中的一样,只不过去掉了背景图和label,

需要说明的是:1.我的cocos2d-x是3月初下的版本,还算比较新,和网上啊书中的教程中的很多接口已经不同了,比如生成世界的方法。

2.小满的教程中有对Box2d的研究,我有参考他的实现,书上的setAsEdge方法已经被删除了,只能用setAsBox,此函数参数说明:1,2参数分别是要生成的对象的长度和宽的的1半,拿bottom来说我要生成长度为程序窗口的长度,所以用窗口长度除以2,

3参是生成的对象的位置中心,我的边界的参考点是(0,0),所以bottom设置在如下位置,4参是旋转角度。

  
  
  
  
  1. CCSize screenSize = CCDirector::sharedDirector()->getWinSize(); 
  2. b2Vec2 gravity = b2Vec2(0.0f, -10.0f);  //老师要求是实现物体堆叠 那么还是加上重力吧 
  3. bool doSleep = true
  4. _world = new b2World(gravity);      //新版本很多方法都改参数了 
  5. _world->SetAllowSleeping(doSleep); 
  6. _world->SetContinuousPhysics(true); 
  7.  
  8. b2BodyDef groundBodyDef; 
  9. groundBodyDef.position.Set(0.0f, 0.0f);     //设置为左下角的点 
  10. _groundBody = _world->CreateBody(&groundBodyDef); 
  11. b2PolygonShape groundBox; 
  12.  
  13. groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(screenSize.width/2/PTM_RATIO, 0), 0); 
  14. _groundBody->CreateFixture(&groundBox, 0); 
  15. // top 
  16. groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(screenSize.width/2/PTM_RATIO, screenSize.height/PTM_RATIO), 0); 
  17. _groundBody->CreateFixture(&groundBox, 0); 
  18. // left 
  19. groundBox.SetAsBox(0, screenSize.height/2/PTM_RATIO, b2Vec2(0, screenSize.height/2/PTM_RATIO), 0); 
  20. _groundBody->CreateFixture(&groundBox, 0); 
  21. // right 
  22. groundBox.SetAsBox(0, screenSize.height/2/PTM_RATIO, b2Vec2(screenSize.width/PTM_RATIO, screenSize.height/2/PTM_RATIO), 0); 
  23. _groundBody->CreateFixture(&groundBox, 0); 

 

然后是对paddle的初始化了

  
  
  
  
  1. b2BodyDef paddleBodyDef; 
  2.        paddleBodyDef.type = b2_dynamicBody; 
  3.         paddleBodyDef.position.Set(screenSize.width/5/PTM_RATIO, 50/PTM_RATIO); 
  4.         paddleBodyDef.userData = paddle; 
  5.         _paddleBody = _world->CreateBody(&paddleBodyDef); 
  6.          
  7.         b2PolygonShape paddleShape; 
  8.         paddleShape.SetAsBox(paddle->getContentSize().width/PTM_RATIO/2, paddle->getContentSize().height/PTM_RATIO/2); 
  9.         b2FixtureDef paddleShapeDef; 
  10.         paddleShapeDef.shape = &paddleShape; 
  11.         paddleShapeDef.density = 10.0f; 
  12.         paddleShapeDef.friction = 0.4f; 
  13.         paddleShapeDef.restitution = 0.1f; 
  14.         paddleShapeDef.isSensor = true
  15.         _paddleFixture = _paddleBody->CreateFixture(&paddleShapeDef); 

paddle2同paddle1,只是改下坐标即可

 

init中最后先允许触摸,在调用更新

  
  
  
  
  1. setIsTouchEnabled(true); 
  2. scheduleUpdate(); 

 

更新函数

和各种教程中代码相同。。。

  
  
  
  
  1. void HelloWorld::update(ccTime dt) 
  2.     int velocityIterations = 8; 
  3.     int positionIterations = 1; 
  4.  
  5.     _world->Step(0.03f, velocityIterations, positionIterations); 
  6.     for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext()) 
  7.     { 
  8.         if (b->GetUserData() != NULL) 
  9.         { 
  10.             CCSprite *sprite = (CCSprite*)b->GetUserData(); 
  11.             sprite->setPosition( CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO) ); 
  12.             sprite->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) ); 

 

触摸开始函数

容我吐槽下,这是翻译自子龙山人大神的代码,貌似他在写博客时候粘贴复制错位置了些代码,比如注释掉的第一句,问题下面会说

  
  
  
  
  1. void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) 
  2. //  if (_mouseJoint == NULL)    return; 
  3.     CCTouch* touch = (CCTouch*)( pTouches->anyObject() ); 
  4.     CCPoint location = touch->locationInView( touch->view() ); 
  5.     location = CCDirector::sharedDirector()->convertToGL(location); 
  6.     b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO); 
  7.  
  8.     if(_paddleFixture->TestPoint(locationWorld)) 
  9.     { 
  10.         b2MouseJointDef md; 
  11.         md.bodyA = _groundBody; 
  12.         md.bodyB = _paddleBody; 
  13.         md.target = locationWorld; 
  14.         md.collideConnected = true
  15.         md.maxForce = 1000.0f * _paddleBody->GetMass(); 
  16.  
  17.         _mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md); 
  18.         _paddleBody->SetAwake(true); 
  19.         flag = true
  20.     } 
  21.     else if (_paddleFixture2->TestPoint(locationWorld)) 
  22.     { 
  23.         b2MouseJointDef md; 
  24.         md.bodyA = _groundBody; 
  25.         md.bodyB = _paddleBody2; 
  26.         md.target = locationWorld; 
  27.         md.maxForce = 1000.0f * _paddleBody2->GetMass(); 
  28.  
  29.         _mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md); 
  30.         _paddleBody2->SetAwake(true); 
  31.         flag = true
  32.     } 

 

触摸移动方法

说明:我在cpp文件头部加了个bool型的flag,初始为false

  
  
  
  
  1. void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) 
  2.     if (_mouseJoint == NULL || flag == false)   return
  3.  
  4.     CCTouch* touch = (CCTouch*)( pTouches->anyObject() ); 
  5.     CCPoint location = touch->locationInView( touch->view() ); 
  6.     location = CCDirector::sharedDirector()->convertToGL(location); 
  7.     b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO); 
  8.  
  9.     _mouseJoint->SetTarget(locationWorld); 

 

触摸结束方法

  
  
  
  
  1. void HelloWorld::ccTouchesEnded(CCSet* pTouches, CCEvent* pEvent) 
  2.     if(_mouseJoint != NULL && flag == true
  3.     { 
  4.         _world->DestroyJoint(_mouseJoint); 
  5.         _mouseJoint = NULL; 
  6.     } 

最后析构函数,要不就内存泄露了

  
  
  
  
  1. HelloWorld::~HelloWorld() 
  2.     delete _world; 
  3.     _groundBody = NULL; 
  4. //  delete _debugDraw;  

 

效果图

吐槽:1.触摸开始方法中第一个注释若是没删,并且没加flag,那么当我仅点击在2个paddle外面的区域时候,_mouseJoint非空,一直运行到09行用TestPoint方法判断为假,跳出进入触摸结束函数,按照子龙大神的代码直接删除,之后你的点击或拖动就全部无效了,因为mouseJoint设置代码永远不会被执行了

2.若是点击区域有效那么拖动都是正常的,但是当你的触摸结束后,鼠标关节又被删除了,之后触屏又无效了。。。

最大可能只是子龙大神的小疏忽,当然对object-c的代码实现上可能有差。。。

最后附上子龙山人博客地址

http://www.cnblogs.com/andyque/archive/2011/05/27/2059460.html

小满博客地址(好参考)

http://blog.csdn.net/bill_man/article/details/7237760

 

下一篇b2debugDraw学习

你可能感兴趣的:(学习,cocos2d-x,休闲,box2D,游戏编程)