声明:文章代码参考自子龙山人的撞球小游戏,链接地址在文章结尾
4月1日接触了Box2d,开始了物理引擎学习之路,这次要求的实现比较简单,就是实现多个(例子中2个)物体的堆叠,鼠标拖动和简单碰撞。
init函数
首先是类中添加成员函数及数据,世界,边框,2个paddle,鼠标关节,响应触屏事件
- b2World *_world;
- b2Body *_groundBody;
- b2Fixture *_bottomFixture;
- b2Body *_paddleBody;
- b2Fixture *_paddleFixture;
- b2Body *_paddleBody2;
- b2Fixture *_paddleFixture2;
- b2MouseJoint *_mouseJoint;
- void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
- void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
- void ccTouchesEnded(CCSet* pTouches, CCEvent* pEvent);
- 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参是旋转角度。
- CCSize screenSize = CCDirector::sharedDirector()->getWinSize();
- b2Vec2 gravity = b2Vec2(0.0f, -10.0f); //老师要求是实现物体堆叠 那么还是加上重力吧
- bool doSleep = true;
- _world = new b2World(gravity); //新版本很多方法都改参数了
- _world->SetAllowSleeping(doSleep);
- _world->SetContinuousPhysics(true);
- b2BodyDef groundBodyDef;
- groundBodyDef.position.Set(0.0f, 0.0f); //设置为左下角的点
- _groundBody = _world->CreateBody(&groundBodyDef);
- b2PolygonShape groundBox;
- groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(screenSize.width/2/PTM_RATIO, 0), 0);
- _groundBody->CreateFixture(&groundBox, 0);
- // top
- groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(screenSize.width/2/PTM_RATIO, screenSize.height/PTM_RATIO), 0);
- _groundBody->CreateFixture(&groundBox, 0);
- // left
- groundBox.SetAsBox(0, screenSize.height/2/PTM_RATIO, b2Vec2(0, screenSize.height/2/PTM_RATIO), 0);
- _groundBody->CreateFixture(&groundBox, 0);
- // right
- groundBox.SetAsBox(0, screenSize.height/2/PTM_RATIO, b2Vec2(screenSize.width/PTM_RATIO, screenSize.height/2/PTM_RATIO), 0);
- _groundBody->CreateFixture(&groundBox, 0);
然后是对paddle的初始化了
- b2BodyDef paddleBodyDef;
- paddleBodyDef.type = b2_dynamicBody;
- paddleBodyDef.position.Set(screenSize.width/5/PTM_RATIO, 50/PTM_RATIO);
- paddleBodyDef.userData = paddle;
- _paddleBody = _world->CreateBody(&paddleBodyDef);
- b2PolygonShape paddleShape;
- paddleShape.SetAsBox(paddle->getContentSize().width/PTM_RATIO/2, paddle->getContentSize().height/PTM_RATIO/2);
- b2FixtureDef paddleShapeDef;
- paddleShapeDef.shape = &paddleShape;
- paddleShapeDef.density = 10.0f;
- paddleShapeDef.friction = 0.4f;
- paddleShapeDef.restitution = 0.1f;
- paddleShapeDef.isSensor = true;
- _paddleFixture = _paddleBody->CreateFixture(&paddleShapeDef);
paddle2同paddle1,只是改下坐标即可
init中最后先允许触摸,在调用更新
- setIsTouchEnabled(true);
- scheduleUpdate();
更新函数
和各种教程中代码相同。。。
- void HelloWorld::update(ccTime dt)
- {
- int velocityIterations = 8;
- int positionIterations = 1;
- _world->Step(0.03f, velocityIterations, positionIterations);
- for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext())
- {
- if (b->GetUserData() != NULL)
- {
- CCSprite *sprite = (CCSprite*)b->GetUserData();
- sprite->setPosition( CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO) );
- sprite->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) );
- }
- }
- }
触摸开始函数
容我吐槽下,这是翻译自子龙山人大神的代码,貌似他在写博客时候粘贴复制错位置了些代码,比如注释掉的第一句,问题下面会说
- void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
- {
- // if (_mouseJoint == NULL) return;
- CCTouch* touch = (CCTouch*)( pTouches->anyObject() );
- CCPoint location = touch->locationInView( touch->view() );
- location = CCDirector::sharedDirector()->convertToGL(location);
- b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
- if(_paddleFixture->TestPoint(locationWorld))
- {
- b2MouseJointDef md;
- md.bodyA = _groundBody;
- md.bodyB = _paddleBody;
- md.target = locationWorld;
- md.collideConnected = true;
- md.maxForce = 1000.0f * _paddleBody->GetMass();
- _mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md);
- _paddleBody->SetAwake(true);
- flag = true;
- }
- else if (_paddleFixture2->TestPoint(locationWorld))
- {
- b2MouseJointDef md;
- md.bodyA = _groundBody;
- md.bodyB = _paddleBody2;
- md.target = locationWorld;
- md.maxForce = 1000.0f * _paddleBody2->GetMass();
- _mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md);
- _paddleBody2->SetAwake(true);
- flag = true;
- }
- }
触摸移动方法
说明:我在cpp文件头部加了个bool型的flag,初始为false
- void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
- {
- if (_mouseJoint == NULL || flag == false) return;
- CCTouch* touch = (CCTouch*)( pTouches->anyObject() );
- CCPoint location = touch->locationInView( touch->view() );
- location = CCDirector::sharedDirector()->convertToGL(location);
- b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
- _mouseJoint->SetTarget(locationWorld);
- }
触摸结束方法
- void HelloWorld::ccTouchesEnded(CCSet* pTouches, CCEvent* pEvent)
- {
- if(_mouseJoint != NULL && flag == true)
- {
- _world->DestroyJoint(_mouseJoint);
- _mouseJoint = NULL;
- }
- }
最后析构函数,要不就内存泄露了
- HelloWorld::~HelloWorld()
- {
- delete _world;
- _groundBody = NULL;
- // 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学习