Cocos2d-x 3.0 开发(九)使用Physicals代替Box2D和chipmunk

1、   概述

    游戏中模拟真实的世界是个比较麻烦的事情,通常这种事情都是交给物理引擎来做。首屈一指的是Box2D了,它几乎能模拟所有的物理效果。而chipmunk则是个更轻量的引擎,能够满足简单的物理需求,比如最常用的的碰撞检测等。这些引擎在使用的过程中有个令人讨厌的地方,它们参数太多了。通常为了初始化一个简单的场景要写很多代码。在cocos2d-x 3.0版本中,出现了一个新类族——physicals。它将Box2D或者chipmunk做了一层封装,使我们的上层调用有更友好的接口。它通过宏来切换使用哪种物理引擎,目前的版本只有chipmunk的实现,Box2D的实现没有写,所以手动将宏切换的话是不行的。另外,当前版本还是有bug的,下面会提到,先看效果图吧:

 

 

2、 原理分析

    相信大家都对物理引擎的使用有所了解,篇幅有限,一些基本概念就不复述了。如果你曾经用过Box2D或者chipmunk,再使用这套封装,你只会有一种爽到爆的感觉。

    在这个版本中,物理世界的概念被加入到Scene中,即当创建一个场景时,就可以指定这个场景是否使用物理引擎。相对应的,每一个Sprite中也有body的概念。可以直接将body关联到Sprite上。Listener当然也不需要再弄一套东西来监听,只要注册到场景中就可以了。

    不知你听到这个改动有和感想,反正我是震惊了。

    接下来我们动手做一个吧。      

3、创建场景

    首先,运行脚本创建一个新工程:testNewPhy,编译运行确保一切正常。 

    找到 CreateScene函数,更改scene的初始化。

[cpp] view plaincopyprint?

1.   Scene* HelloWorld::createScene()  

2.   {  

3.       // 'scene' is an autorelease object  

4.       auto scene = Scene::createWithPhysics();  

5.       scene->getPhysicsWorld()->setDebugDraw(true);  

6.         

7.       // 'layer' is an autorelease object  

8.       auto layer = HelloWorld::create();  

9.     

10.      // add layer as a child to scene  

11.      scene->addChild(layer);  

12.    

13.      // return the scene  

14.      return scene;  

15.  }  


    更改create,创建一个支持物理的世界,打开debugDraw。两行就可以搞定了。

    接下来,我们要将这个World传到Layer中。所以我们在HelloWorld类中加入一个函数。将这个world存起来。 

[cpp] view plaincopyprint?

1.   //……  

2.        void setPhyWorld(PhysicsWorld* world){m_world = world;}  

3.   private:  

4.       PhysicsWorld* m_world;  

5.   }  



    同时在creatScene创建layer完成后,将这个值设定上。

[cpp] view plaincopyprint?

1.   // ……  

2.      auto layer = HelloWorld::create();  

3.      layer->setPhyWorld(scene->getPhysicsWorld());  

4.   / ……  

 

    另外,我们更改一下menuItem的响应,来控制debugDraw的绘制:

[cpp] view plaincopyprint?

1.   void HelloWorld::menuCloseCallback(Object* pSender)  

2.   {  

3.       if(m_world->isDebugDraw())  

4.       {  

5.            m_world->setDebugDraw(false);  

6.       }  

7.       else  

8.       {  

9.           m_world->setDebugDraw(true);  

10.      }  

11.  }  



4、创建边界

    创建了物理世界,还要有东西才行。接下来,我们着手创建一个边界。我们可以方便的使用PhysicalsBody的create方法创建自己想要的物体。 

    在init中进行更改: 

[cpp] view plaincopyprint?

1.   // on "init" you need to initialize your instance  

2.   bool HelloWorld::init()  

3.   {  

4.       //////////////////////////////  

5.       // 1. super init first  

6.       if ( !Layer::init() )  

7.       {  

8.           return false;  

9.       }  

10.        

11.      Size visibleSize = Director::getInstance()->getVisibleSize();  

12.      Point origin = Director::getInstance()->getVisibleOrigin();  

13.    

14.      /////////////////////////////  

15.       

16.      auto edgeSp = Sprite::create();  

17.      auto body = PhysicsBody::createEdgeBox(visibleSize,3);  

18.      edgeSp->setPosition(Point(visibleSize.width/2,visibleSize.height/2));  

19.      edgeSp->setPhysicsBody(body);  

20.      this->addChild(edgeSp);  

21.      edgeSp->setTag(0);  

22.    

23.      return true;  

24.  }  



    编译运行我们会看到场景边上有红色的边界。

 

 

5、添加元素

    我们先将点击响应搭建起来,在init中将touchEnable设置为true,重新onTouchesEnd方法:

[cpp] view plaincopyprint?

1.   void HelloWorld::onTouchesEnded(const std::vector<Touch*>& touches, Event *event)  

2.   {  

3.       for(auto touch:touches)  

4.       {  

5.           auto location = touch->getLocation();  

6.           addNewSpriteAtPosition(location);  

7.       }  

8.   }  



    然后我们来实现addNewSpriteAtPosition函数。关联body与sprite从未如此简单,我们只需创建一个body,创建一个sprite然后将body设置为sprite的body即可。 

[cpp] view plaincopyprint?

1.   void HelloWorld::addNewSpriteAtPosition(Point p)  

2.   {      

3.       auto sp = Sprite::create("1.png");  

4.       sp->setTag(1);  

5.       auto body = PhysicsBody::createBox(Size(80, 40));  

6.       sp->setPhysicsBody(body);      

7.       sp->setPosition(p);  

8.       this->addChild(sp);  

9.   }  



    在这其中,当前版本的cocos2d-x 3.0有一个小问题。关联的时候,并未将body相应的owner设置为对应的sprite,我们需要修改sprite.cpp中的setPhysicsBody这个函数。增加最后一行。

[cpp] view plaincopyprint?

1.   void Sprite::setPhysicsBody(PhysicsBody* body)  

2.   {  

3.       _physicsBody = body;  

4.       _physicsBody->retain();  

5.       _physicsBody->setPosition(getPosition());  

6.       _physicsBody->setRotation(getRotation());  

7.       _physicsBody->_owner = this;  

8.   }  



     编译运行,我们点击屏幕即可动态创建元素了。





6、碰撞检测

    碰撞检测的回调是在world中注册函数来实现的。首先我们在HelloWorld中声明一个变量。并重写OnEnter方法。

[cpp] view plaincopyprint?

1.   //声明  

2.   PhysicsContactListener m_listener;  

3.     

4.   //实现  

5.     

6.   void HelloWorld::onEnter()  

7.   {  

8.       Layer::onEnter();  

9.     

10.      m_listener.onContactBegin = [=](const PhysicsContact& contact)  

11.      {  

12.          auto cnt = const_cast<PhysicsContact*>(&contact);  

13.    

14.          auto sp = cnt->getShapeA()->getBody()->getOwner();  

15.          int tag = sp->getTag();  

16.          if(tag == 1)  

17.          {  

18.              Texture2D *texture = TextureCache::getInstance()->addImage("2.png");  

19.              sp->setTexture(texture);  

20.          }  

21.    

22.          sp = cnt->getShapeB()->getBody()->getOwner();  

23.          tag = sp->getTag();  

24.          if(tag == 1)  

25.          {  

26.              Texture2D *texture = TextureCache::getInstance()->addImage("1.png");  

27.              sp->setTexture(texture);  

28.          }  

29.          return true;  

30.      };  

31.      m_world->registerContactListener(&m_listener);  

32.  }  



    其中,我们将listener的onContactBegin方法重写。并通过shape->body->owner的方式来取到sprite。更改它的显示。最后将listener注册到m_world中。

    编译运行,然后点击menuItem,将debugDrow关闭,即可。

 

7、总结

    通过创建一个支持Physicals的场景,来创建物理系统。将body创建出来,并调用sprite的setPhysicsBody来为一个sprite设定body。通过PhysicsContactListener来创建一个Listener并通过registerContactListener将其注册,来处理碰撞。

 

    Demo下载:http://download.csdn.net/detail/fansongy/6502401

 

你可能感兴趣的:(Cocos2d-x 3.0 开发(九)使用Physicals代替Box2D和chipmunk)