在網上看到 鄭州|boy 這個博客,裏面有幾篇文章,記錄了其用cocos2d-x這個遊戲引擎編寫的一個遊戲,十分不錯,因此這段時間,依樣畫葫蘆,依次學習一下。node
因爲博主開發的平臺是在win32,並且屏幕分辨率也是800*480。個人目標是:按照博主的代碼思路,將這個遊戲在iphone這個平臺下進行代碼重構,其中在學習的過程當中也進行必定的修改。數組
個人開發環境:xcode4.6.3 ; cocos2d-x 2.1.4。xcode
本人會根據博主的介紹,加之理解,在代碼編寫的過程當中,寫幾篇博文記錄一下學習歷程,以饗讀者,也歡迎讀者指責。app
下面再次註明:博文中介紹到的遊戲是經過 點擊打開連接 獲得,尊重原著。iphone
下面第一篇介紹的內容是博主 系列介紹文章的(一)到(四)篇的內容。本人根據在學習過程當中的一些歷程分點記錄。異步
資源下載: 項目程序下載(1~4) 注意:其中的資源已經是最終版所須要的所有資源,因此在之後的新建文件中只需在代碼中直接使用便可,不用再添加資源了。函數
1、BaseLayer層基類學習
做者在這個項目中使用到一個BaseLayer層基類,這個類基礎自CCLayer,其中只是實現了幾個層類中經常使用的方法。之後層類都是繼承自這個BaseLayer類。測試
cocos2d::CCSize getWinSize();// cocos2d::CCPoint getWinOrigin();// cocos2d::CCPoint getWinCenter();// cocos2d::CCPoint ScreenAdaptation(float x, float y);//在位置設置的時候,進行的屏幕適配
這些方法看名字就知道是什麼內容了,在後面的層類中能夠方便使用。優化
2、LoadingLayer遊戲資源加載場景
在這個類中實現的是加載遊戲所須要的圖片等資源,而且加載的過程以進度條的方式進行顯示。
實現的效果以下:
關於在遊戲開始就進行遊戲資源的預加載是通常遊戲的一般作法,這樣能夠優化遊戲的速度。一般加載的大部份內容都是圖片資源,下面介紹一下這裏加載圖片資源的方法:
這裏加載圖片使用到的是 addImageAsync 方法,異步將圖片資源加載進 CCTextureCache 中,加載完畢後,調用回調方法,通知進度條向前顯示。
例如:
CCTextureCache::sharedTextureCache()->addImageAsync("welcomebg.png",this,callfuncO_selector(LoadingLayer::loadCallBack));
其中的loadCallBack方法介紹有關進度條的顯示的。
void LoadingLayer::loadCallBack(CCObject* ped){ loadingNum++; CCProgressTimer* pt=(CCProgressTimer*)this->getChildByTag(1); float now=pt->getPercentage(); pt->setPercentage(100/totalNum+now); if(loadingNum<totalNum){ }else{ goWelcomeLayer(); //資源加載完畢後,跳轉到welcome場景 } }
當資源加載完畢後,就要跳轉場景到welcome場景了。
3、WelComeGameLayer 遊戲歡迎場景
這個場景中主要處理的內容是背景音樂的播放和暫停;至於開始按鍵和開發者按鍵則是處理簡單的視圖跳轉,比較簡單。
首先,咱們得知道遊戲中背景音樂播放和禁止的邏輯。
一、採用 CCUserDefault 持久化保存用戶對背景音樂播放與否的設置;
二、使用 CCMenuItemToggle 開關控件處理背景音樂播放和禁止的選擇;
在建立背景音樂開關控件CCMenuItemToggle的時候要讀取CCUserDefault中保存的值,根據true或者false進行不一樣的處理 。
關鍵代碼以下:
CCMenuItemToggle* pVedioTo=NULL;
// 若是存檔中保存的是false if(!CCUserDefault::sharedUserDefault()->getBoolForKey("isplay")){ pVedioTo=CCMenuItemToggle::createWithTarget(this,menu_selector(WelComeGameLayer::vedioOnAndOffCallBack),pitemVoff,pitemVon,NULL); CCUserDefault::sharedUserDefault()->setBoolForKey("isplay",false); CCUserDefault::sharedUserDefault()->flush(); }else { pVedioTo=CCMenuItemToggle::createWithTarget(this,menu_selector(WelComeGameLayer::vedioOnAndOffCallBack),pitemVon,pitemVoff,NULL); CCUserDefault::sharedUserDefault()->setBoolForKey("isplay",true); CCUserDefault::sharedUserDefault()->flush(); }
三、當在場景跳轉的時候,應該是不會遊戲背景音樂的播放或者禁止的;(例如說:如今背景音樂播放,跳轉到另外一個場景,再跳轉回來,那麼背景音樂播放是一直持續的,沒有任何的變化。------我曾經在這個問題上糾結修改了很久才解決!!) 關於這個問題,我發現原做者的處理是和個人不同的,我在代碼中作了修改。
下面經過代碼簡單看看個人修改處理:
//播放背景音樂 if (CCUserDefault::sharedUserDefault()->getBoolForKey("isplay",true)) { if (!CocosDenshion::SimpleAudioEngine::sharedEngine()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("dt.mp3", true); CCUserDefault::sharedUserDefault()->setBoolForKey("isplay",true); //注意在set以後要調用flush進行保存 CCUserDefault::sharedUserDefault()->flush(); } } //若是不須要播放背景音樂,那麼也播放,只不過當即暫停 else { CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("dt.mp3", true); //當即暫停背景音樂 CocosDenshion::SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); CCUserDefault::sharedUserDefault()->setBoolForKey("isplay",false); //注意在set以後要調用flush進行保存 CCUserDefault::sharedUserDefault()->flush(); }
這部分代碼我是放在init中的,也就說每一次進入這個場景都會執行。對於if中的內容相信都應該能夠理解,對於else中的內容可能就有點難理解了,爲何當CCUserDefault存儲中爲false,即不播放背景音樂的時候,也要播放,只不過是當即中止呢?
沒錯,這就是關鍵的處理了!!
這樣作呢就把播放和暫停分紅了兩類處理resume和pause背景音樂,而沒必要考慮stop背景音樂,不然你會發現很難區分pause和stop,因此這樣巧妙的簡化的處理,使問題獲得解決。
4、屏幕適配的問題
在文章開始的時候我就說到原做者實現的屏幕分辨率是800*480,而我是在iphone下進行代碼的重構,因此不可避免的要進行一些屏幕的適配問題。
前面我有一篇文章是關於這個問題的:Cocos2d-x 關於在iOS平臺真機測試的一些注意
處理這個問題也比較簡單:
(1)在AppDelegate文件中的 applicationDidFinishLaunching 方法中添加下面的代碼(注意要放在 pDirector->setOpenGLView(pEGLView); 後面。):
CCSize designSize = CCSizeMake(480, 320); CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionShowAll);
(2)因爲使用到的圖片資源都是來自做者提供,因此咱們在使用的過程當中也要適配到當前的分辨率中,處理的方法就是在X和Y軸分別按須要進行比例調整。
譬如說,咱們要使用到的welcome場景的背景圖片,咱們能夠分別對X和Y軸的比例進行縮放。
CCTexture2D* texturebg= CCTextureCache::sharedTextureCache()->textureForKey("welcomebg.png"); CCSprite* pSpriteBg=CCSprite::createWithTexture(texturebg); //圖片適配 float scalex = 480/texturebg->getContentSize().width; float scaley = 320/texturebg->getContentSize().height; pSpriteBg->setScaleX(scalex); pSpriteBg->setScaleY(scaley);
5、AboutCoderLayer 開發者介紹場景
這個場景十分簡單,沒有什麼好說的。
不過,這裏就講一下做者在編寫這個項目過程當中編碼的一個習慣吧!做者喜歡將問題細化成一個個模塊,而後分別用一個個方法解決實現。咱們一這個AboutCoderLayer類爲例,在init方法中出現了一個 setUpdateView 方法,在這個方法中處理一些初始化的內容。(這樣的處理相信讀者已經在代碼中已經見到過了。)我的以爲這種處理不錯,分模塊實現層次感好,並且在debug中也比較方便。
--------------------------------------------------------------------------------/ 分割線 /-------------------------------------------------------------------------------------------------
前面的介紹相信都是很小兒科吧!下面開始進入遊戲界面,也就是在welcome歡迎場景中點擊開始進入的場景。
6、製做武器系統
在上面你們已經看到咱們的武器系統了,一把弓裏面裝了一些弓箭,它能夠旋轉調整角度,而後發射。
實現這樣的一個武器系統要用到兩個類:BulletSprite 和 WeaponSprite,第一個類就是處理弓箭的(武器系統中的主角),第二個類就是處理整個武器系統的。
(1)、關於BulletSprite 弓箭類,這個類繼承 CCSprite。
一、BulletSprite的靜態建立方法,顯然咱們自定義一個精靈類,天然也要實現其建立方法。不過咱們能夠參考CCSprite中的方法,依樣畫葫蘆來實現。咱們能夠在CCSprite類中看到:
CCSprite* CCSprite::createWithTexture(CCTexture2D *pTexture) { CCSprite *pobSprite = new CCSprite(); if (pobSprite && pobSprite->initWithTexture(pTexture)) { pobSprite->autorelease(); return pobSprite; } CC_SAFE_DELETE(pobSprite); return NULL; }
那麼咱們也能夠照樣實現,下面就是BulletSprite類中的建立方法實現(幾乎同樣吧!!):
BulletSprite* BulletSprite::createWithTexture(cocos2d::CCTexture2D *pTexture) { BulletSprite* pobSprite = new BulletSprite(); if (pobSprite && pobSprite->initWithTexture(pTexture)) { pobSprite->autorelease(); return pobSprite; } CC_SAFE_DELETE(pobSprite); return NULL; }
二、關於這個弓箭類,其實現中主要就是一個弓箭運動函數的實現---myMove()。其實現也是比較簡單的:根據武器系統的旋轉角度,用cos和sin函數計算旋轉角度再乘以速度得到x軸和y軸的移動距離,而後改變位置。
//弓箭運動 void BulletSprite::myMove() { CCPoint cp=getMovePoint(); float x=cp.x+this->getPositionX(); float y=cp.y+this->getPositionY(); this->setPosition(ccp(x,y)); } CCPoint BulletSprite::getMovePoint() { // 這裏使用了 三角函數 主要是爲了計算當前弓箭所移動的位置 float temhud= (this->getLastRoto()*M_PI)/180; //這裏使用到math.h中的M_PI float tex= std::cos(temhud)*this->moveSpeed; float tey= std::sin(temhud)*this->moveSpeed; tex=std::fabs(tex);// x 方向不存在負數 return ccp(tex,-tey); }
**注意:因爲武器系統會選擇,因此要涉及到角度和弧度的問題。
①當使用三角函數計算時,使用的是弧度;
②當武器系統或者弓箭旋轉rotate時,使用到的是角度。
關於兩者的轉換:
弧度=角度乘以π後再除以180
角度=弧度除以π再乘以180
(2)關於 WeaponSprite 武器系統類,這個類也是繼承自CCSprite。
關於這個類,代碼就不貼了,下面講一下這個類中一些方法的執行順序和做用:
一、咱們在 DefenderGameLayer 這個場景的初始化方法中,能夠看到場景好一個武器系統實例後,
pweapon->initIdleBulletSpool(this);
二、initIdleBulletSpool 這個方法初始化了50支弓箭(包括弓箭的一些屬性設置)供發射使用,初始化後,
this->schedule(schedule_selector(WeaponSprite::loade),1); 表示接着初始化武器系統上的5個弓箭,並且弓箭加載的時間間隔是1秒,那麼當連續發射弓箭時,弓箭發射的時間至少也是1秒(這裏的至少,後面會解釋)。
三、在loade方法中實際調用的是 loadedBullet 方法,這裏就是處理將5支弓箭添加到武器上。
3.1 要往武器上添加弓箭首先確定是要獲取到弓箭啦,getIdleBullet 這個方法就是用於獲取弓箭的。這個方法是比較重要的,代碼中我已經添加了註釋。
在做者提供的代碼中我的發現了一個問題:
//這裏彷佛有點問題 //batchNode->addChild(pbullet,2); //空閒弓箭不夠,那麼這裏就是新增長弓箭,那麼就是應該添加到空閒弓箭數組的,而非bathnode,其實這裏就是相似回收一個弓箭 //因此這裏作一些改動 this->pIdleBulletsPool->addObject(pbullet);
3.2 關於loadedBullet方法中,有這幾行代碼:
for(int i=0;i<this->loadedArray->count();i++){ this->loadedArray->removeObjectAtIndex(0); i--; }
咋一看,還不是很明白是神馬意思!其實就是要清除loadeArray這一個數組中的全部內容。其徹底等價於: this->loadedArray->removeAllObjects();
四、還有一個重要的方法是:
//讓弓箭隨着主武器旋轉 void WeaponSprite::rotateLoadedBullets() { // 獲取當前武器旋轉的角度 float mainWeapon= this->hudu*180/M_PI; if(this->loadedArray){ if(this->loadedArray->count()>0){ for(int i=0;i<this->loadedArray->count();i++){ BulletSprite* pbul=(BulletSprite*)loadedArray->objectAtIndex(i); float temr=pbul->getFirstRoto(); pbul->setRotation(temr+mainWeapon); // 設置最後當前武器最後一次的旋轉角度 pbul->setLastRoto(temr+mainWeapon); } } } }
五、弓箭的釋放
講到這裏武器系統差很少都準備好了,就差如何發射的問題了。那弓箭是在哪裏釋放發射的呢?咱們看到 DefenderGameLayer 文件中,在這個遊戲場景界面中,能夠接受觸摸事件,顯然在這裏處理有關弓箭的發射;咱們注意到在WeaponSprite類中有一個屬性canrun,
//這裏canrun變量表徵是否能夠往武器系統中的弓箭是否能夠加載到運行中的弓箭,這也決定了弓箭可否發射!! CC_SYNTHESIZE(bool, canrun, Canrun);
在DefenderGameLayer的 touchbegan 和 touchmoved 兩個方法中都有 pweapon->setCanrun(true); 而在WeaponSprite加載弓箭的時候,有這個canrun屬性的判斷:
void WeaponSprite::loadedBullet(int cout) { if (this->loadedArray->count()!=cout){ CCArray* tem=this->getIdleBullet(cout);//獲取指定數目cout的弓箭 for(int i=0;i<tem->count();i++){ BulletSprite* pbul=(BulletSprite*)tem->objectAtIndex(i); this->loadedArray->addObject(pbul); } } // 若是武器中止旋轉,表示能夠發射 須要把在槍膛中的弓箭添加到發射弓箭中 if(this->canrun){ //若是有上膛弓箭,則加載到運行弓箭數組上,而且清除上膛弓箭數組 if(this->loadedArray->count()>0){ this->pRunBulletsPool->addObjectsFromArray(this->loadedArray); //清除loadedArray數組 // for(int i=0;i<this->loadedArray->count();i++){ // this->loadedArray->removeObjectAtIndex(0); // i--; // } //以上註釋部分等價於 this->loadedArray->removeAllObjects(); } } }
原來,咱們touch不鬆開屏幕就能夠實現每間隔1秒的加載弓箭呢!!
可是仍是沒有說到弓箭的發射呀???別急!!!
咱們仍是看到DefenderGameLayer的init方法中:
this->schedule(schedule_selector(DefenderGameLayer::detectd),0.1f);//方法調用的間隔是0.1s
void DefenderGameLayer::detectd(float tim) { WeaponSprite* pweapon=(WeaponSprite*) this->getChildByTag(2); if(pweapon->getRunBulletsPool()){ CCArray* tem=CCArray::create(); for(int i=0;i<pweapon->getRunBulletsPool()->count();i++){ BulletSprite* pbu=(BulletSprite*) pweapon->getRunBulletsPool()->objectAtIndex(i); //判斷弓箭是否移動出屏幕範圍,若是是的話,進行回收;不然,讓弓箭移動 if (pbu->boundingBox().getMinX()>=getWinSize().width||pbu->boundingBox().getMinY()<0||pbu->boundingBox().getMinY()>=getWinSize().height){ pweapon->recoverIdleBullet(pbu); tem->addObject(pbu); }else { bool isp = this->monsterSystem->collisionDetection(pbu);//攻擊和怪獸的碰撞檢測 //發生碰撞,說明弓箭射到怪獸 if (isp) { pweapon->recoverIdleBullet(pbu); tem->addObject(pbu); }else//沒有發生碰撞,說明弓箭沒有射到怪獸 { pbu->myMove();// 調用移動方法 } } } //清除運行出屏幕的那些弓箭 for(int j=0;j<tem->count();j++){ pweapon->getRunBulletsPool()->removeObject(tem->objectAtIndex(j),false); } tem->removeAllObjects(); } }
咱們注意到中間部分的代碼,
bool isp = this->monsterSystem->collisionDetection(pbu);//攻擊和怪獸的碰撞檢測 //發生碰撞,說明弓箭射到怪獸 if (isp) { pweapon->recoverIdleBullet(pbu); tem->addObject(pbu); }else//沒有發生碰撞,說明弓箭沒有射到怪獸 { pbu->myMove();// 調用移動方法 }
沒錯,這裏就是發射弓箭,讓弓箭運動了。因爲是每0.1秒調用 detectd() ,因此前面說到弓箭至少1秒發射一次中的至少應該理解了吧! 就是有可能還要加上0.1秒的發射間隔。
差很少,須要講解注意的就是這些了,詳細的內容請參看代碼加以理解。
7、實現怪獸系統
怪獸系統能夠產生怪獸(賭徒),這些怪獸頭頂有一個血條,這些怪獸從右往左走,收到弓箭弓箭後,血條會減小,直到死亡。怪獸能夠執行奔跑動畫,攻擊動畫和死亡動畫。
這部份內容的實現包括兩個類: MonsterSprite 和 MonsterSystem 。
(1)MonsterSprite 怪獸類,這個類繼承CCSprite。其實很相似前面的弓箭類。具體就不介紹了,看看下面的類頭文件估計就知道是什麼內容了。
class MonsterSprite:public cocos2d::CCSprite{ public: MonsterSprite(void); ~MonsterSprite(void); void moveRun();// 移動函數 CC_SYNTHESIZE(float,hurt,Hurt);//傷害值 CC_SYNTHESIZE(float,defense,Defense);//防護值 CC_SYNTHESIZE(float,speed,Speed);//移動速度 CC_SYNTHESIZE(float,maxRemoving,maxRemoving);// 移動的最大距離 CC_SYNTHESIZE(float,blood,Blood);// 怪物氣血值 CC_SYNTHESIZE(int,monType,MonType);// 怪物類型 CC_SYNTHESIZE(int,monState,MonState);// 怪物狀態 1 靜止狀態 2 行動狀態 3 攻擊狀態 4 死亡狀態 CC_SYNTHESIZE(cocos2d::CCRect,attackRange,AttackRange);// 接受攻擊的範圍 CC_SYNTHESIZE(int, gold,Gold);// 怪物攜帶金幣數量 void runAnimation();// 執行奔跑動畫 void deathAnimation();// 執行死亡動畫 void attackAnimation();// 執行攻擊動畫 void fallBlood(float hurt);// 這個是接受攻擊 主要改變 該怪物的氣血值 和血條的顯示 // 第一個參數的意思是 加載的plist 文件的名字 第二個是 plist 對應的圖片紋理 第三個是圖片的通用名字 第四個走路動畫圖片數,第五個是攻擊動畫的圖片數 第六個是死亡動畫的圖片數 // 在這裏貼別說明一點爲了達到動畫的通用性 咱們規定 plist 中的圖片命名格式是這樣的 pic-1編號 是跑步圖片 pic-2編號是 攻擊圖片 pic-x編號是死亡圖片 static MonsterSprite* createWithMonsterRul(const char* filename,cocos2d::CCTexture2D* ccTexture2D,const char* pic,int runcount,int attackcount,int deathcout ); void setMonsterSystemUtils(MonsterSystem* monsterSystem); cocos2d::CCRect converNowRect();// 這個方法是把最初設計的攻擊範圍 轉化到當前 界面的座標系中的矩形 protected: cocos2d::CCArray* runArray;//奔跑動畫序列幀 cocos2d::CCArray* deathArray;//死亡動畫序列幀 cocos2d::CCArray* attackArray;//攻擊動畫序列幀 cocos2d::CCProgressTimer* bloodBwlid;// 這個是血條 virtual void deathAnimationCallBack(cocos2d::CCNode* pSed);// 死亡動畫回調函數 virtual void attackAnimationCallBack(cocos2d::CCNode* pSed);// 攻擊動畫回調函數 virtual void runAnimationCallBack(cocos2d::CCNode* pSed);//奔跑動畫回調函數 virtual bool setUpdateView(); static MonsterSprite* createWithSpriteFrame(cocos2d::CCSpriteFrame *pSpriteFrame); MonsterSystem* monsterSystem; void myload(float tim); };
注意:當兩個頭文件相互引用的時候,要注意頭文件的前置聲明。不然會遇到一些莫名其妙的問題的!!!例如在這個類,就須要: class MonsterSystem;
(2)MonsterSystem 怪獸系統類。
這個類主要就是根據MonsterSprite 怪獸類產生怪獸添加到遊戲界面中。
typedef struct MonsterUtils{ float initBlood;// 初始化氣血 float initSpeed;// 初始化速度 float defend;// 怪物的防護力 float hurt;// 怪物的傷害值 char* monsName;// 在設置怪物的時候的通用名字 char* picName;// 怪物的圖片 char* fileName;// 怪物所對應的plist 文件的名字 int type;// 怪物類型 int runCount;// 奔跑動畫張數 int actCount;// 攻擊動畫張數 int detCount;// 死亡動畫張數 float maxRun;// 最大移動距離 char* attackRangeRec;//是在怪物身上劃定一個受到的攻擊範圍 這樣可讓不規則的 圖片 看起來受到攻擊的時候更逼真一點 字符串的 格式是這樣的{{x,y},{w, h}} int gold;// 怪物攜帶金幣數量 當怪物死後 增長金幣 } Monster; class DefenderGameLayer; // 此類是生產和銷燬系統 class MonsterSystem{ public: MonsterSystem(); ~MonsterSystem(); cocos2d::CCArray* getIdleMonsterArry();// 用來保存空閒的怪物 cocos2d::CCArray* getRunMonsterArray();// 用來保存正在奔跑中的怪物 void addMonster(int type,int count);// 用於主線程調用來源源不斷的產生怪物 void setDefenderGameLayer(DefenderGameLayer* defenderGameLayer); bool collisionDetection(BulletSprite* bulletsSprite);// 傳入弓箭 檢測是否和怪物發生碰撞 void recoverMonster(MonsterSprite* monsterSprite);// 回收怪物 private: cocos2d::CCArray* idleMonsterArry;// 用來保存空閒的怪物 cocos2d::CCArray* runMonsterArray;// 用來保存正在奔跑中的怪物 MonsterSprite* productMonster(int type);//根據類型來產生響應的怪物的數量 DefenderGameLayer* defenderGameLayer;// 遊戲主類 void addDefenderGameLayer(MonsterSprite* monsterSprite);// 把奔跑中的怪物添加到 主界面裏面 Monster dutu;// 每次添加新的怪物都須要在這裏添加一個 而且在構造方法裏面初始化 };
看到頭文件大體也能夠知道要處理的內容了吧!
下面我就介紹一下其中的關鍵部分:
一、其中用到一個結構體包含了怪獸的一些基本屬性,方便使用。
二、在前面弓箭發射運動以前都要檢測弓箭和怪獸是否發生碰撞的檢測,就是調用這個類中的 collisionDetection 方法,在這個方法中傳入弓箭,檢測是否和怪物發生碰撞,若是發射碰撞,就是說怪獸被打中了,那麼就要對怪獸頭上的血條進行掉血的處理,調用的方法是怪獸類的 fallBlood 方法。
// 這個是接受攻擊 主要改變 該怪物的氣血值和血條的顯示 void MonsterSprite::fallBlood(float hurted) { // 按照 一點防護 能抵擋10%的傷害 來計算 float temp= this->bloodBwlid->getPercentage()*this->getBlood()/100;// 獲得真正屬於的氣血值 float cha=temp-(hurted-this->getDefense()*0.1); // 獲取 受傷後之後的氣血 if(cha<=0){ this->bloodBwlid->setPercentage(0); this->deathAnimation(); }else { this->bloodBwlid->setPercentage(cha/this->blood*100); } }
首先,從代碼中知道,一個怪獸的血值是:100,傷害值是10,防護值是1;弓箭的傷害值是10.
這段代碼說,一點防護能夠抵擋10%的傷害,那麼也就是說,弓箭射中怪獸,其傷害也就是 10 * (100% - 10%) = 9,那麼每一次都是血值減小 9 。而後再根據剩下血值的比例設置血條,若是血值爲0,則怪獸執行死亡動畫。
三、怪獸系統差很少搞定了,那麼如今就能夠向 DefenderGameLayer 遊戲場景中添加怪獸了。
this->monsterSystem = new MonsterSystem(); monsterSystem->setDefenderGameLayer(this); this->monsterSystem->addMonster(1, 2); this->schedule(schedule_selector(DefenderGameLayer::releaseMonster),1);
void DefenderGameLayer::releaseMonster(float tim){ this->monsterSystem->addMonster(1,2); }
每隔一秒向遊戲場景添加兩隻怪獸,不過如今武器系統的攻擊能力有限,估計打不死那麼多的怪獸,並且源源不斷產生這麼 多,機子估計也受不了!不怕,後面的程序還會改進的!敬請期待!