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

     

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

     

  • 相关阅读:
    第 12 章 Docker Swarm
    第 1 章 虚拟化
    第 0 章 写在最前面
    第 11 章 日志管理
    第 11 章 日志管理
    第 11 章 日志管理
    第 11 章 日志管理
    第 11 章 日志管理
    第 11 章 日志管理
    第 11 章 日志管理
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6755037.html
Copyright © 2011-2022 走看看