离线下载
PDF版 ePub版

偶尔e网事 · 更新于 2018-11-28 11:00:43

碰撞检测

碰撞检测是整个游戏的重头戏,没有它,子弹和敌机只能是路人。。。虽然很重要,但实现起来确是很简单。

1.update函数

每个从CCObject继承的类都包含了一个update函数,它是一个内联虚函数,执行频率为每帧调用一次。

原型:

    virtual CCObject::update(float dt);

开启方式:

    this->scheduleUpdate();

关闭方式:

    this->unscheduleUpdate();

有了这个update函数,我们就可以在GameLayer中对它进行重载,把我们每帧内要执行的动作放到update中,进行调用就可以了。

2.碰撞检测原理

那我们每帧内要做什么事呢?

我们先看一下游戏是什么?动画?其实不过是一张张快速变换的图片。比如我们设置60FPS,那么游戏就是一秒钟闪过60张图片,由于人眼的视觉残留效应导致看起来像是动画,因此游戏中的每一个动作我们可以认为是高速变化的静态的图片,而这个图片是以帧为单位。

所以我们可以在每一帧内进行碰撞检测。

原理:

主角飞机是一个精灵,也就是一张带alpha通道的PNG图,子弹也是,敌机同样也是,它们都是一块矩形区域,即便有的地方是透明的。所以碰撞检测就是在每一帧内判断多个不同类型矩形区域是否有相交,有即碰撞。

3.碰撞检测示例

这里以生命值最大的Enemy3来做示例。而敌机和主角的碰撞是一样的道理。主角的爆炸效果也是采用帧动画,和敌机爆炸是一致的,但是要注意,主角飞机一旦爆炸,要记得StopShoot。

    CCArray* bulletsToDelete=CCArray::create();//创建一个CCArray,用以存放待删除的子弹,也就是此帧中被检测到碰撞的子弹
    bulletsToDelete->retain();//必须调用retain,CCArray内部调用了autoRelease
    CCObject* bt,*et;

    //enemy3 & bullet CheckCollosion
    CCARRAY_FOREACH(this->bulletLayer->m_pAllBullet,bt)//遍历所有子弹
    {
        CCSprite* bullet=(CCSprite*)bt;

        CCArray* enemy3sToDelete=CCArray::create();//创建一个CCArray,用以存放待删除的敌机,也就是此子弹击中的敌机
        enemy3sToDelete->retain();//调用retain
        CCARRAY_FOREACH(this->enemyLayer->m_pAllEnemy3,et)//遍历所有敌机
        {
            Enemy* enemy3=(Enemy*)et;
            if (CCRect::CCRectIntersectsRect(bullet->boundingBox(),enemy3->getBoundingBox()))//检测碰撞,即矩形区域是否相交
            {
                //如果life>1,移除bullet
                if (enemy3->getLife()>1)//因为Enemy3设置了生命值为5
                {
                    enemy3->loseLife();
                    bulletsToDelete->addObject(bullet);//把待删除子弹放入CCArray
                }
                //如果life==1,移除enemy3
                else if(enemy3->getLife()==1)//只剩一条命的时候,再碰撞就挂掉了
                {
                    enemy3->loseLife();
                    bulletsToDelete->addObject(bullet);//把待删除子弹放入CCArray
                    enemy3sToDelete->addObject(enemy3);//把待删除敌机放入CCArray
                    score+=ENEMY3_SCORE;
                    this->controlLayer->updateScore(score);
                }
                //此时处在敌机爆炸动画阶段,敌机未消失,子弹还有打到的机会,所以不进行检测
                else ;
            }
        }
        CCARRAY_FOREACH(enemy3sToDelete,et)//遍历所有此帧中碰撞死亡的敌机,必须是死亡
        {
            Enemy* enemy3=(Enemy*)et;
            this->enemyLayer->enemy3Blowup(enemy3);//执行爆炸
        }
        enemy3sToDelete->release();//release
    }
    CCARRAY_FOREACH(bulletsToDelete,bt)//遍历所有此帧中碰撞的子弹
    {
        CCSprite* bullet=(CCSprite*)bt;
        this->bulletLayer->RemoveBullet(bullet);//执行移除
    }
    bulletsToDelete->removeAllObjects();
    bulletsToDelete->release();//release

不过要注意的是,实际游戏如果只是简单的检测矩形区域,那么可能会出现子弹从敌机身边掠过,而敌机却挂了的情况,这不科学啊!这是因为敌机两侧的透明区域过大了,碰撞检测的是整个PNG图的大小,不知道这个图上哪些点有代表着飞机。

所以要根据游戏的实际情况来调整精灵矩形区域的大小以达到较好的游戏体验。

效果图