【cocos3.x+box2d+tileMap】制作马里奥游戏(四)碰撞检测

【cocos3.x+box2d+tileMap】制作马里奥游戏(四)碰撞检测,第1张

概述转载请注明来源:http://blog.csdn.net/pur_e/article/details/50599344        Box2d物理引擎还提供一个很重要的功能:碰撞检测。如马里奥游戏中,需要检测马里奥与怪物、蘑菇、金币等的碰撞,通过判断不同的碰撞点、碰撞对象做出不同的处理。 我们要在马里奥中实现的碰撞效果如下: 一、理论        Box2d通过设置碰撞监听来处理碰撞,在创建世

转载请注明来源:http://www.jb51.cc/article/p-dxevjywf-ya.html

Box2d物理引擎还提供一个很重要的功能:碰撞检测。如马里奥游戏中,需要检测马里奥与怪物、蘑菇、金币等的碰撞,通过判断不同的碰撞点、碰撞对象做出不同的处理。

我们要在马里奥中实现的碰撞效果如下:


一、理论

Box2d通过设置碰撞监听来处理碰撞,在创建世界后,可以进行设置监听:

_b2World->SetContactListener(this);

设置的监听对象需要继承b2ContactListener类,实现函数

virtual voID BeginContact(b2Contact* contact):碰撞开始时的回调函数,一般简单的碰撞检测使用;virtual voID EndContact(b2Contact* contact):碰撞发生后的回调函数,一般简单的碰撞检测使用;virtual voID PreSolve(b2Contact* contact,const b2Manifold* oldManifold):碰撞求解前的回调函数,求解就是指计算碰撞产生的冲击力,需要计算碰撞冲击力造成的破坏等效果时,需要使用此回调函数;virtual voID postsolve(b2Contact* contact,const b2ContactImpulse* impulse):碰撞求解后的回调函数,需要计算碰撞冲击力造成的破坏等效果时,需要使用此回调函数。

这四个回调方法中,前两个功能有限但使用起来简单,后两个提供的信息量大,但使用起来比较复杂,这个要根据游戏的具体要求而定,如果我们的游戏过程对物理要求不高,仅仅是实现碰撞检测功能,那么我们主要使用BeginContact(b2Contact* contact)这个回调函数就足够了,如果我们要处理碰撞之前和碰撞之后的效果,根据碰撞中产生的相互作用力来计算物理碰撞后的移动,则我们必须好好的利用全部这四个函数,它们联合作用起来,可以模拟出比较真实而复杂的物理碰撞效果。

发生碰撞后,Box2d会自动调用对应函数,开发者进行具体实现。

二、实践

1.规划

设置一个调度者,所有碰撞由其进行分发 所有需要进行碰撞处理的类,继承自相同的基类,方便调度者处理 各自的碰撞事件各自处理

2.碰撞基类实现

其实就是一个虚类,定义碰撞处理函数,后面为了方便处理,还加了一个碰撞方向检测函数

class BaseContactNode : public Node{public:    typedef enum{        DIRECTION_UP = 1,DIRECTION_DOWN = -1,DIRECTION_RIGHT = 2,DIRECTION_left = -2    }DIRECTION;public:    //碰撞处理函数    virtual voID beginContact(Node*,b2Contact*) = 0;    //获取碰撞方向,这里只简单判断上下左右    virtual int getContactDirection(Node* node,b2Contact* contact){        int isReverse = 1;        auto manifold = contact->GetManifold();        Node* other = static_cast<Node*>(contact->GetFixtureB()->Getbody()->GetUserData());        //判断碰撞参考者        if((node != other && manifold->type == b2Manifold::e_faceB)           || (node == other && manifold->type == b2Manifold::e_faceA)){            isReverse = -1;        }        int ret = 0;        if (manifold->localnormal.y == -1) {            ret = DIRECTION_UP ;        }else if(manifold->localnormal.y == 1){            ret = DIRECTION_DOWN;        }else if(manifold->localnormal.x == 1){            ret = DIRECTION_RIGHT;        }else if(manifold->localnormal.x == -1){            ret = DIRECTION_left;        }        return ret * isReverse;    }};
简单说一下其中用到的属性,Box2d会将碰撞的大量信息保存下来,包括但不限于 contact->GetFixtureB()->Getbody()获取碰撞者body contact->GetManifold()获取碰撞相关信息,返回的b2Manifold中包括 localnormal法向量,可以判断受力方向 points碰撞位置 type,碰撞参考者类型,可能是相对物体A——e_faceA,或B——e_faceB 可以通过contact->GetWorldManifold(&worldManifold)获取碰撞点相对于世界坐标的信息

3.调度者实现

实现很简单,直接分发^_^

voID MarioScene::BeginContact(b2Contact *contact){    if (contact && contact->Istouching())    {        auto A = static_cast<Node*>(contact->GetFixtureA()->Getbody()->GetUserData());        auto B = static_cast<Node*>(contact->GetFixtureB()->Getbody()->GetUserData());        auto pBaseBoxSprite = static_cast<BaseContactNode*>(A);        if(pBaseBoxSprite){            pBaseBoxSprite->beginContact(B,contact);        }        pBaseBoxSprite = static_cast<BaseContactNode*>(B);        if(pBaseBoxSprite){            pBaseBoxSprite->beginContact(A,contact);        }    }}

4.碰撞处理

不同对象的碰撞处理不相同,这里拿马里奥和怪物两个做例子。

4.1 马里奥碰撞处理

注意:

下面的代码中可以看到,在碰到问号后,我的逻辑是生成一个蘑菇,本来我是想直接生成,同时添加body到Box2d世界中,但直接在一个Assert上报错了b2Assert(m_world->IsLocked() == false),这是因为在Box2d的碰撞处理中(世界步中in the mIDdle of a time step),会将世界锁定,不允许修改,所以我们只能将状态缓存下来,在后续的update中进行添加

然后直接上代码:

voID MarioPlayer::beginContact(Node* node,b2Contact* contact){        int direction =getContactDirection(this,contact);    if(direction == DIRECTION_UP){        //受力方向是上,说明已经碰到了地面,可以重新起跳        delStatus(MarioPlayer::STATUS_JUMP);    }    if (node && node->getTag() == MarioScene::CONTACT_MONSTER) {        //如果碰到了怪物        auto monster = static_cast<MarioMonster*>(node);        if(direction != DIRECTION_UP && monster->getStatus() != MarioMonster::STATUS_DIED){            //如果不是踩到怪物头上且怪物不是死亡状态,则Game Over            MarioScene::dIE();        }else if(direction == DIRECTION_UP && monster->getStatus() == MarioMonster::STATUS_DIED){            //碰撞死后的乌龟顶端,则乌龟会以更快的速度移动,当然,这样写是因为我当前场景里只有乌龟            if(this->getposition().x >= monster->getposition().x){                monster->dIEdImpluse(DIRECTION_RIGHT);            }else{                monster->dIEdImpluse(DIRECTION_left);            }        }    }else if(node && node->getTag() == MarioScene::CONTACT_BONUS){       //如果碰到问号,则生出一个蘑菇        auto marioScene = static_cast<MarioScene*>(this->getParent());        if(marioScene){            marioScene->addBonusList(node);        }    }}

4.2 怪物碰撞处理

相对来说比较简单,直接上代码:

voID MarioMonster::beginContact(Node* node,b2Contact* contact){    int direction = getContactDirection(this,contact);    if(node && node->getTag() == MarioScene::CONTACT_PLAYER){        if(direction == DIRECTION_DOWN){            //如果被马里奥踩到,死亡            dIE();        }    }else if(node && node->getTag() == MarioScene::CONTACT_MONSTER){        //如果被其他怪物碰到,死亡(其他乌龟是可以被用来做炮d的)        dIE(false);    }}

三、结语

至此,马里奥世界中,和怪物等的碰撞交互也实现了,已经很接近真实的马里奥世界,剩余的就是继续丰满,添加不同的怪物、增加不同的马里奥状态如开q等、设计更大的地图、增加更多的地图原素了。

总结

以上是内存溢出为你收集整理的【cocos3.x+box2d+tileMap】制作马里奥游戏(四)碰撞检测全部内容,希望文章能够帮你解决【cocos3.x+box2d+tileMap】制作马里奥游戏(四)碰撞检测所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/web/1077111.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存