zoukankan      html  css  js  c++  java
  • Cocos2d-x3.0游戏实例之《别救我》第七篇——物理世界的碰撞检測


    事实上我也非常吃惊…居然写到第七篇了,我估计也就是四篇的内容,感觉非常奇妙,我也不会非常唠叨什么吖(小若:32个喷!

    ),怎么都到第七篇了。

     

    笨木头花心贡献,啥?花心?不呢,是用心~

    转载请注明,原文地址: http://www.benmutou.com/blog/archives/920

    文章来源:笨木头与游戏开发

     

    碰撞监听

    首先,确保我们创建物理对象的时候,给对象设置了碰撞条件(假设你是一步步按着教程来写的代码,那就是设置好了):

    1.     body->setCategoryBitmask(1);    // 0001
    2.     body->setCollisionBitmask(1);   // 0001
    3.     body->setContactTestBitmask(1); // 0001

    这样我们才干监听到它们的碰撞事件,至于原理。就不说了。以我的唠叨程度,不是一两篇内容能说完的。

     

    然后。我们给TollgateScene加入一个函数声明:

    1.     /* 碰撞检測 */
    2.     bool onContactBegin(PhysicsContact& contact);

    这是碰撞事件開始时的回调函数,监听碰撞事件非常easy。我们修改一下TollgateScene的init函数:

    1. bool TollgateScene::init()
    2. {
    3.     if (!Layer::init())
    4.     {
    5.         return false;
    6.     }
    7.     /* 创建主角 */
    8.     Size visibleSize = Director::getInstance()->getVisibleSize();
    9.  
    10.     m_player = Player::create();
    11.     m_player->setPosition(Point(visibleSize.width * 0.5f, visibleSize.height * 0.85f));
    12.     this->addChild(m_player, 5);
    13.  
    14.     /* 创建操作UI */
    15.     createOprUI();
    16.  
    17.     /* 碰撞监听 */
    18.     auto contactListener = EventListenerPhysicsContact::create();
    19.     contactListener->onContactBegin = CC_CALLBACK_1(TollgateScene::onContactBeginthis);
    20.     _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
    21.  
    22.  
    23.     this->schedule(schedule_selector(TollgateScene::logic));
    24.     return true;
    25. }

     

    流程就是这样:

    1. 创建EventListenerPhysicsContact对象,能够看做是碰撞监听回调接口

    2. 绑定onContactBegin事件的回调函数

    3. 将监听接口加入到统一的事件派发器里(addEventListenerWithSceneGraphPriority)

     

    Cocos2d-x3.0的监听事件修改非常大(但非常好用)。这里就不多解释了,网上非常多文章有介绍。

     

    假设大家有去看看EventListenerPhysicsContact的源代码的话,会发现,碰撞事件不仅仅仅仅有onContactBegin一个,其它的事件我就不说了。这里仅仅是要onContactBegin。作用是在两个物理对象開始发生碰撞的时候调用。

     

    好,最后看看onContactBegin函数实现:

    1. bool TollgateScene::onContactBegin(PhysicsContact& contact)
    2. {
    3.     auto nodeA = (Sprite*)contact.getShapeA()->getBody()->getNode();
    4.     auto nodeB = (Sprite*)contact.getShapeB()->getBody()->getNode();
    5.  
    6.     return true;
    7. }

    当中获取到的nodeA和nodeB就是发生碰撞的两个节点对象。

    如今。看看碰撞检測是否正常吧。用调试模式执行游戏。然后在onContactBegin函数里打个断点,看看主角碰到墙的时候有没有进入这个断点吧~

    假设有,那就代表成功了。

     

    然后,另一点别忘了,在TollgateScene的onExit函数里,把监听事件给取消了:

    1. void TollgateScene::onExit()
    2. {
    3.     Layer::onExit();
    4.  
    5.     _eventDispatcher->removeEventListenersForTarget(this);
    6. }

     

    怎么知道发生碰撞的两个节点各自是谁?

    如今我们仅仅知道有两个节点碰撞了。也能取到两个节点对象,可是,它们都是谁啊?根本不认识啊。那怎么进行下一步操作呢?我想给Player对象加血,那怎么办呢?

     

    没关系,节点有一个非常万能的函数:setTag。

    给节点设置Tag可不是仅仅用来从Layer里获取子节点对象。我们还能够用来区分这些节点是谁,应该是。区分这些节点是属于哪一类东西。

     

    创建一个生物分类表

    好。是时候新建一个头文件了,为了迎合这份高大上的教程,我们就称之为生物分类表吧~

    创建一个头文件,命名为ObjectTag.h,内容例如以下:

    1. #ifndef ObjectTag_H
    2. #define ObjectTag_H
    3.  
    4. #define ObjectTag_Player 1
    5. #define ObjectTag_Border 2
    6. #define ObjectTag_Monster 3
    7.  
    8. #endif

    这大实用处,别着急~

     

    给各种物体设置Tag吧

    好了,如今我们要给主角和墙(或者称之为锯齿)设定生物类别了。在Player的init函数的最后加上一句代码:

    1. bool Player::init()
    2. {
    3.     /* 这里省略了非常多代码 */
    4.  
    5.     this->setTag(ObjectTag_Player);
    6.     return true;
    7. }

    当然,ObjectTag.h头文件也别忘了加上。

     

    然后。给BackgroundLayer的createBorder函数最后也加上一句代码:

    1. Sprite* BackgroundLayer::createBorder(Point pos)
    2. {
    3.     /* 这里省略了非常多代码 */
    4.  
    5.     border->setTag(ObjectTag_Border);
    6.     return border;
    7. }
    8.  

    開始区分谁是谁

    OK了,主角和墙都有了各自的生物类型了~如今我们能够区分碰撞的两个对象各自是谁了。

     

    我们要在TollgateScene的onContactBegin函数里做处理:

    1. bool TollgateScene::onContactBegin(PhysicsContact& contact)
    2. {
    3.     auto nodeA = (Sprite*)contact.getShapeA()->getBody()->getNode();
    4.     auto nodeB = (Sprite*)contact.getShapeB()->getBody()->getNode();
    5.  
    6.     if (nodeA == NULL || nodeB == NULL)
    7.     {
    8.         return true;
    9.     }
    10.  
    11.     Node* playerNode = NULL;    /* 玩家对象 */
    12.     Node* other = NULL;         /* 怪物或墙等其它对象 */
    13.  
    14.     if (nodeA->getTag() == ObjectTag_Player)
    15.     {
    16.         playerNode = nodeA;
    17.         other = nodeB;
    18.     }
    19.     else if (nodeB->getTag() == ObjectTag_Player)
    20.     {
    21.         playerNode = nodeB;
    22.         other = nodeA;
    23.     }
    24.     else
    25.     {
    26.         /* 假设两个碰撞的物体中,不存在玩家对象,就忽略,不做处理 */
    27.         return true;
    28.     }
    29.  
    30.     Player* player = (Player*)playerNode;
    31.  
    32.     /* 碰撞到边缘锯齿(墙),+1血 */
    33.     if (other->getTag() == ObjectTag_Border)
    34.     {
    35.         /* 扣-1血。就相当于加1血 */
    36.         player->beAtked(-1);
    37.  
    38.         log("player cur HP:%d", player->getiHP());
    39.     }
    40.     return true;
    41. }

    (小若:这么长的代码。打死我我也不看~!

     

    这段代码要做的事情事实上非常easy,萝莉一下,萝莉、罗莉。罗列,嗯(这输入法坏了,一定是):

    1.推断nodeA的Tag是不是ObjectTag_Player,假设是。那么nodeA就是Player对象了,同一时候。nodeB仅仅能是墙或者是怪物对象了(由于游戏里仅仅有一个Player对象)

    2.假设nodeA不是Player,那就继续推断nodeB

    3.假设nodeA和nodeB都不是Player对象,那我们就不做处理。由于怪物和怪物之间的碰撞不须要做处理

    4.假设找到Player对象,那就推断other对象是不是墙,是的话。那就让Player加1滴血

    5.由于这个实例缺少非常多功能,比方UI、数据绑定、碰撞时产生的动画效果之类的。所以没法直观地看到Player加血的动作,仅仅好用打印日志的方式来查看了。

     

    有朋友提醒我漏了解说Player的beAtked函数,这里补充一下。如代码:

    1. void Player::beAtked(int iValue)
    2. {
    3.     if (iValue < 0)
    4.     {
    5.         cure(-iValue);
    6.     }
    7.     else
    8.     {
    9.         hurt(std::abs(iValue));
    10.     }
    11. }
    12. void Player::hurt(int iValue)
    13. {
    14.     setiHP(getiHP() - iValue);
    15. }
    16. void Player::cure(int iValue)
    17. {
    18.     setiHP(getiHP() + iValue);
    19. }


    好了,如今用调试模式执行游戏(键盘F5),使劲让主角撞墙吧。然后看看日志输出:

    player cur HP:101

    player cur HP:102

    player cur HP:103

    player cur HP:104

    player cur HP:105

    player cur HP:106

    player cur HP:107

    player cur HP:108

    player cur HP:109

     

    假设有相似以上的日志输出。那就证明我们成功了~

     

  • 相关阅读:
    OK335xS-Android mkmmc-android-ubifs.sh hacking
    OK335xS-Android pack-ubi-256M.sh hacking
    OK335xS Ubuntu 12.04.1 版本 Android 开发环境搭建
    Qt Quick Hello World hacking
    Qt QML referenceexamples attached Demo hacking
    QT 5.4.1 for Android Ubuntu QtWebView Demo
    I.MX6 working note for high efficiency
    QT 5.4.1 for Android Windows环境搭建
    mkbootimg hacking
    Generate And Play A Tone In Android hacking
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6755037.html
Copyright © 2011-2022 走看看