Cocos2d-x碰撞檢測原理與英雄要打死怪物--之遊戲開發《趙雲要格鬥》(7),cocos2d碰撞檢測
這裡是Evankaka的部落格,歡迎大家前面討論與交流~~~~~~
轉載請註明出處http://blog.csdn.net/evankaka/article/details/42689689
本文將詳細講述cocos2dx中英雄與怪物的碰撞檢測原理,其實就是精靈和精靈碰撞檢測哈。本文主要從矩形碰撞入手,自己編寫了一個矩形碰撞檢測的函數,並且在遊戲中來進行應用。另一方面,當英雄出動攻擊後,如果英雄和怪物碰撞到的話,怪物就要掉血,並且當怪物血量為0時,怪物死亡,死亡之前它還會倒在地上閃爍幾下。下面,開始吧
cocos2d-x版本:2.2.5
工程環境:windows7+VS2010
開啟檔案:將工程放在cocos2d-x安裝目錄下的project檔案夾下用VS開啟
源碼下載(博主決定本系列資源全部免費~)
先來看看效果:
目錄
一、精靈碰撞檢測原理
二、自訂碰撞檢測函數
三、英雄要打死怪物
四、思路總結
一、精靈碰撞檢測原理
碰撞檢測網上有很多人在講,但是一般都只講怎麼用,也都沒具體的講講原理,自己下來就摸索了下,發現其實這個確實很簡單。
首先,我們來看看兩個矩形,我們定義如下兩個矩形,矩形1:紅色;矩形2:黑色
如果我們把它們所有的不碰撞的情形列出來,那麼其它的不就是碰撞的麼,想到這一點,我就從這個出發,然後它們不碰撞的情形我們可以分為四種
矩形1:紅色;矩形2:黑色
1.矩形1在矩形2左方,兩者無碰撞
成立條件:x1+w1*0.5<x2-w2*0.5
2.矩形1在矩形2右方,兩者無碰撞
成立條件::x1-w1*0.5>x2+w2*0.5
3.矩形1在矩形2下方,兩者無碰撞
成立條件::y1+h1*0.5<y2-h2*0.5
4.矩形1在矩形2上方,兩者無碰撞
成立條件:y1-h1*0.5>y2+h2*0.5
上面四種就是所有的不碰撞的情況了,然後我們弄個判斷,依次檢測上面四種情形,一旦發現有一種情況成立,就返回無碰撞,如果四種情況都不成立,那恭喜你了,碰撞成功了!
二、自訂碰撞檢測函數
碰撞檢測對於精靈類可以用
sprite1->boundingBox().intersectsRect(sprite1->boundingBox())
只不過我這個遊戲中的英雄和怪物都是自己定義的類,所以直接調用上面的函數就出點兒問題,所以自己就把前面碰撞檢測的原理寫了個函數,可以直接調用了,不用管你是什麼對像。
首先,在用到碰撞檢測的地方#include "HelloWorldScene.h"
定義函數
//矩形碰撞檢測
bool isRectCollision (CCRect rect1, CCRect rect2);
然後在其實現函數裡HelloWorldScene.cpp裡:
///碰撞檢測bool HelloWorld::isRectCollision (CCRect rect1, CCRect rect2){float x1 = rect1.origin.x;//矩形1中心點的橫座標float y1 = rect1.origin.y;//矩形1中心點的縱座標float w1 = rect1.size.width;//矩形1的寬度float h1 = rect1.size.height;//矩形1的高度float x2 = rect2.origin.x;float y2 = rect2.origin.y;float w2 = rect2.size.width;float h2 = rect2.size.height;if (x1+w1*0.5<x2-w2*0.5) return false;//矩形1在矩形2左方,兩者無碰撞else if (x1-w1*0.5>x2+w2*0.5)return false;//矩形1在矩形2右方,兩者無碰撞else if (y1+h1*0.5<y2-h2*0.5)return false;//矩形1在矩形2下方,兩者無碰撞else if (y1-h1*0.5>y2+h2*0.5)return false;//矩形1在矩形2上方,兩者無碰撞return true;}這個代碼的原理就是我們上面所講的東西,很簡單吧!
三、英雄要打死怪物
現在我們要調用二中的函數,我們先來看看英雄和怪物的碰撞範圍吧
(我把背景弄透明了,實際是這樣的)
(我把背景弄透明了,實際是這樣的)
所以,這裡要注意下。這裡就是要小心,最好不要把整個圖片的寬度和高度都包含進去;
碰撞檢測的一個簡單流程
然後就是實現了啦~
void HelloWorld::update(float delta)函數中添加
if(hero->IsAttack)//英雄正在攻擊{ if(!monster1->Isdead)//怪物還沒死 { if(abs(hero->getPositionY()-monster1->getPositionY())<30)//怪物和英雄應該在一個差不多的水平高度上,攻擊才有效 { //檢測是否碰撞到怪物,這裡要注意要減去一些邊框值 if (this->isRectCollision(CCRectMake(hero->getPositionX(), hero->getPositionY(),hero->GetSprite()->getContentSize().width-70, hero->GetSprite()->getContentSize().height-30), CCRectMake(monster1->getPositionX(), monster1->getPositionY(), monster1->GetSprite()->getContentSize().width-30,monster1->GetSprite()->getContentSize().height-20))) { monster1->HurtAnimation("monster_hurt",2,monster1->MonsterDirecton);//受傷 } } }}
好了,這裡得來講講怪物受傷死亡動畫了了
接著上一篇講的Monster.h增加函數
//受傷動畫void HurtAnimation(const char *name_each,const unsigned int num,bool run_directon);//受傷動畫結束void HurtEnd();//判斷是否在受傷動畫bool IsHurt;//死亡動畫void DeadAnimation(const char *name_each,const unsigned int num,bool run_directon);//死亡動畫結束void DeadEnd();//判斷是否死亡bool Isdead;//怪物死亡閃爍結束void BlinkEnd();
然後在實現函數Monster.cpp
//受傷動畫void Monster::HurtAnimation(const char *name_each,const unsigned int num,bool run_directon){if(IsHurt||Isdead)return;//受傷優先if(IsRunning||IsAttack){m_MonsterSprite->stopAllActions();//當前精靈停止所有動畫 //恢複精靈原來的初始化貼圖 this->removeChild(m_MonsterSprite,TRUE);//把原來的精靈刪除掉 m_MonsterSprite=CCSprite::create(Monster_name);//恢複精靈原來的貼圖樣子 m_MonsterSprite->setFlipX(MonsterDirecton); this->addChild(m_MonsterSprite); IsRunning=false; IsAttack=false;}CCAnimation* animation = CCAnimation::create(); for( int i=1;i<=num;i++) { char szName[100] = {0}; sprintf(szName,"%s%d.png",name_each,i); animation->addSpriteFrameWithFileName(szName); //載入動畫的幀 } animation->setDelayPerUnit(2.8f/14.0f); animation->setRestoreOriginalFrame(true); animation->setLoops(1); //動畫迴圈1次 //將動畫封裝成一個動作CCAnimate* act=CCAnimate::create(animation);//建立回調動作,受傷動畫結束調用HurtEnd()CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(Monster::HurtEnd));//建立連續動作CCActionInterval* hurtackact=CCSequence::create(act,callFunc,NULL);m_MonsterSprite->runAction(hurtackact); IsHurt=true;}//受傷動畫結束void Monster::HurtEnd(){IsHurt=false;Monster_xue->setCurrentProgress(Monster_xue->getCurrentProgress()-10);if(Monster_xue->getCurrentProgress()==0){//播放怪物死亡動畫DeadAnimation("monster_dead",2,MonsterDirecton);}}//死亡動畫void Monster::DeadAnimation(const char *name_each,const unsigned int num,bool run_directon){Isdead=true;CCAnimation* animation = CCAnimation::create(); for( int i=1;i<=num;i++) { char szName[100] = {0}; sprintf(szName,"%s%d.png",name_each,i); animation->addSpriteFrameWithFileName(szName); //載入動畫的幀 } animation->setDelayPerUnit(2.8f/14.0f); animation->setRestoreOriginalFrame(true); animation->setLoops(1); //動畫迴圈1次 //將動畫封裝成一個動作CCAnimate* act=CCAnimate::create(animation);//建立回調動作,死亡結束後調用deadact()CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(Monster::DeadEnd));//建立連續動作CCActionInterval* deadact=CCSequence::create(act,callFunc,NULL);m_MonsterSprite->runAction(deadact); }//死亡動畫結束void Monster::DeadEnd(){//恢複死亡的樣子this->removeChild(m_MonsterSprite,TRUE);//把原來的精靈刪除掉m_MonsterSprite=CCSprite::create("monster_dead2.png");//恢複死亡的樣子m_MonsterSprite->setFlipX(MonsterDirecton);this->addChild(m_MonsterSprite);//存在血條if(Monster_xue!=NULL){if(MonsterDirecton)//因為怪物中心不在圖片中心,所以只能根據怪物的臉朝向,設定血條的橫座標Monster_xue->setPosition(ccp(m_MonsterSprite->getPositionX()+60, m_MonsterSprite->getPositionY()));//設定在怪物上頭 elseMonster_xue->setPosition(ccp(m_MonsterSprite->getPositionX()-60, m_MonsterSprite->getPositionY())); }//怪物閃兩下再死亡CCBlink* blinkact=CCBlink::create(3,6);//3是期間,6是閃的次數//建立回調動作,閃爍結束後調用BlinkEnd()CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(Monster::BlinkEnd));//建立連續動作CCActionInterval* deadact=CCSequence::create(blinkact,callFunc,NULL);m_MonsterSprite->runAction(deadact);}//閃爍結束void Monster::BlinkEnd(){this->removeAllChildren();//把怪物和血條都刪除掉;}
怪物死亡的一個過程,在每次受傷掉血後,立馬檢測當前血量,如果血量為0,馬上播放死亡動畫,接著再播放閃爍動畫,然後就可以把怪物刪除掉了啦~~就這麼簡單
效果:
1、怪物在巡邏,這時攻擊沒有檢測到碰撞
2、英雄在攻擊,檢測到碰撞,怪物受傷並掉血
3、怪物血量為0,怪物死亡,並播放閃爍動畫
四、思路總結
這裡碰撞檢測我是反其它道而行,把所有不碰撞的可能都列出來,其它的不就是碰撞的了嗎?然後再來自己編程,另一方面,怪物就是受傷、死亡的動畫,以及閃爍,這些都是很基礎的,基本上都是相同的函數,只要用一次你就會了。就是這裡要記得這是按順序的動作,要記得這點就行了