zoukankan      html  css  js  c++  java
  • 实习小白::(转) 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的初始化。

    1. Scene* HelloWorld::createScene()  
    2. {  
    3.     // 'scene' is an autorelease object  
    4.     auto scene = Scene::createWithPhysics();  
    5.     scene->getPhysicsWorld()->setDebugDraw(true); //此句仅3.0 alpha0 有效  
    6.     scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);  
    7.   
    8.       
    9.     // 'layer' is an autorelease object  
    10.     auto layer = HelloWorld::create();  
    11.   
    12.     // add layer as a child to scene  
    13.     scene->addChild(layer);  
    14.   
    15.     // return the scene  
    16.     return scene;  
    17. }  


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

        打开debugDrawMask,两行就可以搞定了。DrawMask参数可以选择打开绘制哪些部分比如,Joint、Shape等等。    

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

    1. //……  
    2.      void setPhyWorld(PhysicsWorld* world){m_world = world;}  
    3. private:  
    4.     PhysicsWorld* m_world;  
    5. }  


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

    1. // ……  
    2.    auto layer = HelloWorld::create();  
    3.    layer->setPhyWorld(scene->getPhysicsWorld());  
    4. / ……  

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

     

      此函数已在3.0 alpha1中失效

    1. <del>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. }  
    12. </del>  


        使用如下函数

    1. void HelloWorld::menuCloseCallback(Object* pSender)  
    2. {  
    3.     if(m_world->getDebugDrawMask() != PhysicsWorld::DEBUGDRAW_NONE)  
    4.     {  
    5.          m_world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_NONE);  
    6.     }  
    7.     else  
    8.     {  
    9.         m_world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);  
    10.     }  
    11.   
    12. }  

        

    4、创建边界

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

        在init中进行更改: 

    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); //此句仅3.0 alpha0 有效  
    18.         auto body = PhysicsBody::createEdgeBox(visibleSize,PHYSICSBODY_MATERIAL_DEFAULT,3);  
    19.         edgeSp->setPosition(Point(visibleSize.width/2,visibleSize.height/2));  
    20.         edgeSp->setPhysicsBody(body);this->addChild(edgeSp);edgeSp->setTag(0);  
    21.         return true;  
    22. }  


        其中,PHYSICSBODY_MATERIAL_DEFAULT宏表示的是创建的Body的默认材质,3是边线宽度。编译运行我们会看到场景边上有红色的边界。

    5、添加元素

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

    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即可。 

    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这个函数。增加最后一行。此bug已修复

    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方法。

        碰撞检测的回调是在Scene中注册Listener来实现的。当有碰撞发生时,就会调用对应的Listener。所有的碰撞都使用EventListenerPhysicsContact类。我们可以通过重写它的onContactBegin、onContactPreSolve、onContactPostSolve、onContactSeperate方法来更改它的行为。

        下面函数已失效

    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. }  


        应使用:

    1. void HelloWorld::onEnter()  
    2. {  
    3.     Layer::onEnter();  
    4.     auto listener = EventListenerPhysicsContact::create();  
    5.     listener->onContactBegin = [=](EventCustom* event, const PhysicsContact& contact)  
    6.     {  
    7.         auto sp = (Sprite*)contact.getShapeA()->getBody()->getNode();  
    8.         int tag = sp->getTag();  
    9.         if(tag == 1)  
    10.         {  
    11.             Texture2D *texture = TextureCache::getInstance()->addImage("2.png");  
    12.             sp->setTexture(texture);  
    13.         }  
    14.   
    15.         sp = (Sprite*)contact.getShapeB()->getBody()->getNode();  
    16.         tag = sp->getTag();  
    17.         if(tag == 1)  
    18.         {  
    19.             Texture2D *texture = TextureCache::getInstance()->addImage("1.png");  
    20.             sp->setTexture(texture);  
    21.         }  
    22.         return true;  
    23.     };    
    24.     Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listener,10); //第二个参数是优先级,10是随意写的  
    25. }  



        其中,我们将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

        Demo 下载(3.0 alpha1版本):http://download.csdn.net/detail/fansongy/6652089

        本篇博客出自阿修罗道,转载请注明出处,禁止用于商业用途:http://blog.csdn.net/fansongy/article/details/14142323 

  • 相关阅读:
    数据库应用-java+sqlserver(十)StuInfo
    数据库应用-java+sqlserver(九)StudentMain和StudentManager
    数据库应用-java+sqlserver(八)StoreScore
    数据库应用-java+sqlserver(七)SqlManager
    小学期学习总结二
    小学期学习总结一
    四则运算小程序
    第二小组第七周学习心得
    需求分析
    MSF
  • 原文地址:https://www.cnblogs.com/dudu580231/p/4983716.html
Copyright © 2011-2022 走看看