如何製作一個橫版格鬥過關遊戲 2 Cocos2d x 2 0 4
在第一篇《如何製作一個橫版格鬥過關遊戲》基礎上,增加角色運動、碰撞、敵人、AI和音樂音效,原文《How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 2》,在這裡繼續以Cocos2d-x進行實現。有關原始碼、資源等在文章下面給出了地址。步驟如下:1.使用上一篇的工程;2.移動英雄。在第一部分我們建立了虛擬方向鍵,但是還未實現按下方向鍵移動英雄,現在讓我們進行實現。開啟Hero.cpp檔案,在init函式attack animation後面,新增如下程式碼:
123456789 | //walk animationCCArray *walkFrames = CCArray::createWithCapacity(8);for (i = 0; i < 8; i++){ CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("hero_walk_%02d.png", i)->getCString()); walkFrames->addObject(frame);}CCAnimation *walkAnimation = CCAnimation::createWithSpriteFrames(walkFrames, float |
123456789101112131415161718192021 | void ActionSprite::walkWithDirection(CCPoint direction){ if (_actionState == kActionStateIdle) { this->stopAllActions(); this |
1234567891011121314151617 | void GameLayer::didChangeDirectionTo(SimpleDPad *simpleDPad, CCPoint direction){ _hero->walkWithDirection(direction);}void GameLayer::isHoldingDirection(SimpleDPad *simpleDPad, CCPoint direction){ _hero->walkWithDirection(direction);}void GameLayer::simpleDPadTouchEnded(SimpleDPad *simpleDPad){ if (_hero->getActionState() == kActionStateWalk) { _hero->idle(); }} |
此時,編譯執行程式的話,通過方向鍵移動英雄,發現英雄只是原地踏步。改變英雄的位置是ActionSprite和GameLayer共同的責任。一個ActionSprite永遠不會知道它在地圖上的位置。因此,它並不知道已經到達了地圖的邊緣,它只知道它想去哪裡。而GameLayer的責任就是將它的期望位置轉換成實際的位置。開啟ActionSprite.cpp檔案,實現以下方法:
1234567 | void ActionSprite::update(float dt){ if (_actionState == kActionStateWalk) { _desiredPosition = ccpAdd(this->getPosition(), ccpMult(_velocity, dt)); }} |
1 | this->scheduleUpdate(); |
1234 | GameLayer::~GameLayer(void){ this->unscheduleUpdate();} |
1234567891011121314 | void GameLayer::update(float dt){ _hero->update(dt); this->updatePositions();}void GameLayer::updatePositions(){ float posX = MIN(_tileMap->getMapSize().width * _tileMap->getTileSize().width - _hero->getCenterToSides(), MAX(_hero->getCenterToSides(), _hero->getDesiredPosition().x)); float posY = MIN(3 * _tileMap->getTileSize().height + _hero->getCenterToBottom(), MAX(_hero->getCenterToBottom(), _hero->getDesiredPosition().y)); _hero->setPosition(ccp(posX, posY));} |
設定GameLayer的更新方法,每次迴圈時,GameLayer讓英雄更新它的期望位置,然後通過以下這些值,將期望位置進行檢查是否在地圖地板的範圍內:
- mapSize:地圖tile數量。總共有10x100個tile,但只有3x100屬於地板。tileSize:每個tile的尺寸,在這裡是32x32畫素。
GameLayer還使用到了ActionSprite的兩個測量值,centerToSides和centerToBottom,因為ActionSprite要想保持在場景內,它的位置不能超過實際的精靈邊界。假如ActionSprite的位置在已經設定的邊界內,則GameLayer讓英雄達到期望位置,否則GameLayer會讓英雄留停在原地。3.編譯執行,此時點選方向鍵,移動英雄,如下圖所示:但是,很快你就會發現英雄可以走出地圖的右邊界,然後就這樣從螢幕上消失了。4.以上的問題,可以通過基於英雄的位置進行滾動地圖,這個方法在文章《如何製作一個基於Tile的遊戲》中有描述過。開啟GameLayer.cpp檔案,在update函式裡最後新增如下程式碼:
1 | this->setViewpointCenter(_hero->getPosition()); |
1234567891011121314 | void GameLayer::setViewpointCenter(CCPoint position){ CCSize winSize = CCDirector::sharedDirector()->getWinSize(); int x = MAX(position.x, winSize.width / 2); int y = MAX(position.y, winSize.height / 2); x = MIN(x, (_tileMap->getMapSize().width * _tileMap->getTileSize().width) - winSize.width / 2); y = MIN(y, (_tileMap->getMapSize().height * _tileMap->getTileSize().height) - winSize.height / 2); CCPoint actualPosition = ccp(x, y); CCPoint centerOfView = ccp(winSize.width / 2, winSize.height / 2); CCPoint viewPoint = ccpSub(centerOfView, actualPosition); this->setPosition(viewPoint);} |
以上程式碼讓英雄處於螢幕中心位置,當然,英雄在地圖邊界時的情況除外。編譯執行,效果如下圖所示:5.建立機器人。我們已經建立了精靈的基本模型:ActionSprite。我們可以重用它來建立遊戲中電腦控制的角色。新建Robot類,派生自ActionSprite類,增加如下方法:
12 | CREATE_FUNC(Robot);bool init(); |
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 | bool Robot::init(){ bool bRet = false; do { CC_BREAK_IF(!ActionSprite::initWithSpriteFrameName("robot_idle_00.png")); int i; //idle animation CCArray *idleFrames = CCArray::createWithCapacity(5); for (i = 0; i < 5; i++) { CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( CCString::createWithFormat("robot_idle_%02d.png", i)->getCString()); idleFrames->addObject(frame); } CCAnimation *idleAnimation = CCAnimation::createWithSpriteFrames(idleFrames, float(1.0 / 12.0)); this->setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation))); //attack animation CCArray *attackFrames = CCArray::createWithCapacity(5); for (i = 0; i < 5; i++) { CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( CCString::createWithFormat("robot_attack_%02d.png", i)->getCString()); attackFrames->addObject(frame); } CCAnimation *attackAnimation = CCAnimation::createWithSpriteFrames(attackFrames, float(1.0 / 24.0)); this->setAttackAction(CCSequence::create(CCAnimate::create(attackAnimation), CCCallFunc::create(this, callfunc_selector(Robot::idle)), NULL)); //walk animation CCArray *walkFrames = CCArray::createWithCapacity(6); for (i = 0; i < 6; i++) { CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( CCString::createWithFormat("robot_walk_%02d.png", i)->getCString()); walkFrames->addObject(frame); } CCAnimation *walkAnimation = CCAnimation::createWithSpriteFrames(walkFrames, float(1.0 / 12.0)); this->setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation))); this->setWalkSpeed(80.0); this->setCenterToBottom(39.0); this->setCenterToSides(29.0); this->setHitPoints(100.0); this->setDamage(10.0); bRet = true; } while (0); return bRet;} |
跟英雄一樣,以上程式碼建立一個帶有3個動作的機器人:空閒、出拳、行走。它也有兩個測量值:centerToBottom和centerToSides。注意到機器人的屬性比英雄低一點,這是合乎邏輯的,不然英雄永遠打不贏機器人。讓我們開始新增一些機器人到遊戲中去。開啟GameLayer.h檔案,新增如下程式碼:
1 | CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _robots, Robots); |
1 | #include "Robot.h" |
1 | _robots = NULL; |
1 | this->initRobots(); |
123456789101112131415161718192021 | void GameLayer::initRobots(){ int robotCount = 50; this->setRobots(CCArray::createWithCapacity(robotCount)); for (int i = 0; i < robotCount; i++) { Robot *robot = Robot::create(); _actors->addChild(robot); _robots->addObject(robot); int minX = SCREEN.width + robot->getCenterToSides(); int maxX = _tileMap->getMapSize().width * _tileMap->getTileSize().width - robot->getCenterToSides(); int minY = robot->getCenterToBottom(); int maxY = 3 * _tileMap->getTileSize().height + robot->getCenterToBottom(); robot->setScaleX(-1); robot->setPosition(ccp(random_range(minX, maxX), random_range(minY, maxY))); robot->setDesiredPosition(robot->getPosition()); robot->idle(); }} |
這些程式碼做了以下事情:
- 建立一個包含50個機器人的陣列,並把它們新增到精靈表單中。
- 使用Defines.h裡面的隨機函式隨機放置50個機器人到地圖地板上。同時,讓最小隨機值大於螢幕寬度,以確保不會有任何機器人出現在起點處。
- 讓每個機器人都處於空閒狀態。
編譯執行,讓英雄向前走,直到看到地圖上的機器人,如下圖所示:試著走到機器人區域中,你會發現機器人的繪製有些不對。如果英雄是在機器人的下面,那麼他應該被繪製在機器人的前面,而不是在後面。我們需要明確的告訴遊戲,哪個物件先繪製,這就是Z軸來進行控制的。新增英雄和機器人時,並沒有明確指定其Z軸,預設下,後面新增的物件會比前面的物件Z軸值高,這就是為什麼機器人擋住了英雄。為了解決這個問題,我們需要動態的處理Z軸順序。每當精靈在螢幕上垂直移動時,它的Z軸值應該有所改變。螢幕上越高的精靈,其Z軸值應越低。開啟GameLayer.cpp檔案,新增如下方法:
123456789 | void GameLayer::reorderActors(){ CCObject *pObject = NULL; CCARRAY_FOREACH(_actors->getChildren(), pObject) { ActionSprite *sprite = (ActionSprite*)pObject; _actors->reorderChild(sprite, (_tileMap->getMapSize().height * _tileMap->getTileSize().height) - sprite->getPosition().y); }} |
1 | this->reorderActors(); |
- Hit box:代表精靈的身體
- Attack box:代表精靈的手
假如某個ActionSprite的Attack box碰撞到另一個ActionSprite的Hit box,那麼這就是一次碰撞發生。這兩個矩形之間的區別,將幫助我們知道誰打了誰。Defines.h檔案中的BoundingBox定義,包含兩種矩形:實際的,和原始的:①原始矩形,每個精靈的基本矩形,一旦設定後就不會改變。②實際矩形,這是位於世界空間中的矩形,當精靈移動時,實際的矩形也跟著變動。開啟ActionSprite.h檔案,新增如下程式碼:
1234 | CC_SYNTHESIZE(BoundingBox, _hitBox, Hitbox);CC_SYNTHESIZE(BoundingBox, _attackBox, AttackBox);BoundingBox createBoundingBoxWithOrigin(cocos2d::CCPoint origin, cocos2d::CCSize size); |
以上建立了ActionSprite的兩個包圍盒:Hit box和Attack box。還定義了一個方法,用於根據給定的原點和大小來建立一個BoundingBox結構體。開啟ActionSprite.cpp檔案,新增如下方法:
1234567891011121314151617181920212223 | BoundingBox ActionSprite::createBoundingBoxWithOrigin(CCPoint origin, CCSize size){ BoundingBox boundingBox; boundingBox.original.origin = origin; boundingBox.original.size = size; boundingBox.actual.origin = ccpAdd(this->getPosition(), ccp(boundingBox.original.origin.x, boundingBox.original.origin.y)); boundingBox.actual.size = size; return boundingBox;}void ActionSprite::transformBoxes(){ _hitBox.actual.origin = ccpAdd(this->getPosition(), ccp(_hitBox.original.origin.x, _hitBox.original.origin.y)); _attackBox.actual.origin = ccpAdd(this->getPosition(), ccp(_attackBox.original.origin.x + (this->getScaleX() == -1 ? (- _attackBox.original.size.width - _hitBox.original.size.width) : 0), _attackBox.original.origin.y));}void ActionSprite::setPosition(CCPoint position){ CCSprite::setPosition(position); this->transformBoxes();} |
第一個方法建立一個新的包圍盒,這有助於ActionSprite的子類建立屬於它們自己的包圍盒。第二個方法,基於精靈的位置、比例因子,和包圍盒原本的原點和大小來更新每個包圍盒實際測量的原點和大小。之所以要用到比例因子,是因為它決定著精靈的方向。位於精靈右側的盒子,當比例因子設定為-1時,將會翻轉到左側。開啟Hero.cpp檔案,在init函式後面新增如下程式碼:
123 | this->setHitbox(this->createBoundingBoxWithOrigin(ccp(-this->getCenterToSides(), -this->getCenterToBottom()), CCSizeMake(this->getCenterToSides() * 2, this->getCenterToBottom() * 2)));this->setAttackBox(this->createBoundingBoxWithOrigin(ccp(this->getCenterToSides(), -10), CCSizeMake(20, 20))); |
123 | this->setHitbox(this->createBoundingBoxWithOrigin(ccp(-this->getCenterToSides(), -this->getCenterToBottom()), CCSizeMake(this->getCenterToSides() * 2, this->getCenterToBottom() * 2)));this->setAttackBox(this->createBoundingBoxWithOrigin(ccp(this->getCenterToSides(), -5), CCSizeMake(25, 20))); |
1234567891011121314151617181920212223 | void ActionSprite::hurtWithDamage(float damage){ if (_actionState != kActionStateKnockedOut) { this->stopAllActions(); this->runAction(_hurtAction); _actionState = kActionStateHurt; _hitPoints -= damage; if (_hitPoints <= 0) { this->knockout(); } }}void ActionSprite::knockout(){ this->stopAllActions(); this->runAction(_knockedOutAction); _hitPoints = 0; _actionState = kActionStateKnockedOut;} |
只要精靈還未死亡,被擊中時狀態將會切換到受傷狀態,執行受傷動畫,並且精靈的生命值將會減去相應的傷害值。如果生命值少於0,那麼死亡的動作將會觸發。為了完成這兩個動作,我們還需更改Hero類和Robot類。開啟Hero.cpp檔案,在init函式walk animation後面新增如下程式碼:
12345678910111213141516171819 | //hurt animationCCArray *hurtFrames = CCArray::createWithCapacity(3);for (i = 0; i < 3; i++){ CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("hero_hurt_%02d.png", i)->getCString()); hurtFrames->addObject(frame);}CCAnimation *hurtAnimation = CCAnimation::createWithSpriteFrames(hurtFrames, float(1.0 / 12.0));this->setHurtAction(CCSequence::create(CCAnimate::create(hurtAnimation), CCCallFunc::create(this, callfunc_selector(Hero::idle)), NULL));//knocked out animationCCArray *knockedOutFrames = CCArray::createWithCapacity(5);for (i = 0; i < 5; i++){ CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("hero_knockout_%02d.png", i)->getCString()); knockedOutFrames->addObject(frame);}CCAnimation *knockedOutAnimation = CCAnimation::createWithSpriteFrames(knockedOutFrames, float(1.0 / 12.0));this->setKnockedOutAction(CCSequence::create(CCAnimate::create(knockedOutAnimation), CCBlink::create(2.0, 10.0), NULL)); |
開啟Robot.cpp檔案,在init函式walk animation後面新增如下程式碼:
12345678910111213141516171819 | //hurt animationCCArray *hurtFrames = CCArray::createWithCapacity(3);for (i = 0; i < 3; i++){ CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("robot_hurt_%02d.png", i)->getCString()); hurtFrames->addObject(frame);}CCAnimation *hurtAnimation = CCAnimation::createWithSpriteFrames(hurtFrames, float(1.0 / 12.0));this->setHurtAction(CCSequence::create(CCAnimate::create(hurtAnimation), CCCallFunc::create(this, callfunc_selector(Robot::idle)), NULL));//knocked out animationCCArray *knockedOutFrames = CCArray::createWithCapacity(5);for (i = 0; i < 5; i++){ CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("robot_knockout_%02d.png", i)->getCString()); knockedOutFrames->addObject(frame);}CCAnimation *knockedOutAnimation = CCAnimation::createWithSpriteFrames(knockedOutFrames, float(1.0 / 12.0));this->setKnockedOutAction(CCSequence::create(CCAnimate::create(knockedOutAnimation), CCBlink::create(2.0, 10.0), NULL)); |
以上程式碼應該不陌生了。我們用建立其他動作同樣的方式建立了受傷和死亡動作。受傷動作結束時,會切換到空閒狀態。死亡動作結束時,精靈進行閃爍。開啟GameLayer.cpp檔案,新增碰撞處理,在ccTouchesBegan函式後面新增如下程式碼:
123456789101112131415161718 | if (_hero->getActionState() == kActionStateAttack){ CCObject *pObject = NULL; CCARRAY_FOREACH(_robots, pObject) { Robot *robot = (Robot*)pObject; if (robot->getActionState() != kActionStateKnockedOut) { if (fabsf(_hero->getPosition().y - robot->getPosition().y) < 10) { if (_hero->getAttackBox().actual.intersectsRect(robot->getHitbox().actual)) { robot->hurtWithDamage(_hero->getDamage()); } } } } } |
1 | CC_SYNTHESIZE(float, _nextDecisionTime, NextDecisionTime); |
1 | _nextDecisionTime = 0; |
1234567891011121314151617181920212223242526272829303132333435363738 | #pragma once#include "cocos2d.h"// 1 - convenience measurements#define SCREEN CCDirector::sharedDirector()->getWinSize()#define CENTER ccp(SCREEN.width / 2, SCREEN.height / 2)#define CURTIME GetCurTime()// 2 - convenience functions#ifndef UINT64_C#define UINT64_C(val) val##ui64#endif#define random_range(low, high) (rand() % (high - low + 1)) + low#define frandom (float)rand() / UINT64_C(0x100000000)#define frandom_range(low, high) ((high - low) * frandom) + low// 3 - enumerationstypedef enum _ActionState { kActionStateNone = 0, kActionStateIdle, kActionStateAttack, kActionStateWalk, kActionStateHurt, kActionStateKnockedOut} ActionState;// 4 - structurestypedef struct _BoundingBox { cocos2d::CCRect actual; cocos2d::CCRect original;} BoundingBox;inline float GetCurTime(){ timeval time; gettimeofday(&time, NULL); unsigned long millisecs = (time.tv_sec * 1000) + (time.tv_usec / 1000); return (float)millisecs;}; |
開啟GameLayer.cpp檔案,新增如下方法:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 | void GameLayer::updateRobots(float dt){ int alive = 0; float distanceSQ; int randomChoice = 0; CCObject *pObject = NULL; CCARRAY_FOREACH(_robots, pObject) { Robot *robot = (Robot*)pObject; robot->update(dt); if (robot->getActionState() != kActionStateKnockedOut) { //1 alive++; //2 if (CURTIME > robot->getNextDecisionTime()) { distanceSQ = ccpDistanceSQ(robot->getPosition(), _hero->getPosition()); //3 if (distanceSQ <= 50 * 50) { robot->setNextDecisionTime(CURTIME + frandom_range(0.1, 0.5) * 1000); randomChoice = random_range(0, 1); if (randomChoice == 0) { if (_hero->getPosition().x > robot->getPosition().x) { robot->setScaleX(1.0); } else { robot->setScaleX(-1.0); } //4 robot->setNextDecisionTime(robot->getNextDecisionTime() + frandom_range(0.1, 0.5) * 2000); robot->attack(); if (robot->getActionState() == kActionStateAttack) { if (fabsf(_hero->getPosition().y - robot->getPosition().y) < 10) { if (_hero->getHitbox().actual.intersectsRect(robot->getAttackBox().actual)) { _hero->hurtWithDamage(robot->getDamage()); //end game checker here } } } } else { robot->idle(); } } else if (distanceSQ <= SCREEN.width * SCREEN.width) { //5 robot->setNextDecisionTime(CURTIME + frandom_range(0.5, 1.0) * 1000); randomChoice = random_range(0, 2); if (randomChoice == 0) { CCPoint moveDirection = ccpNormalize(ccpSub(_hero->getPosition(), robot->getPosition())); robot->walkWithDirection(moveDirection); } else { robot->idle(); } } } } } //end game checker here} |
這是一個漫長的程式碼片段。將程式碼分解為一段段。對於遊戲中的每個機器人:①.使用一個計數來儲存仍然存活著的機器人數量。一個機器人只要不是死亡狀態,就被認為仍然存活著。這將用於判斷遊戲是否應該結束。②.檢查當前應用程式時間的推移是否超過了機器人的下一次決定時間。如果超過了,意味著機器人需要作出一個新的決定。③.檢查機器人是否足夠接近英雄,以便於有機會出拳攻擊落在英雄身上。如果接近英雄了,那麼就進行一個隨機選擇,看是要朝著英雄出拳,還是要繼續空閒著。④.假如機器人決定攻擊,我們就用之前檢測英雄攻擊時相同的方式來進行檢測碰撞。只是這一次,英雄和機器人的角色互換了。⑤.如果機器人和英雄之間的距離小於螢幕寬度,那麼機器人將作出決定,要麼朝著英雄移動,要麼繼續空閒。機器人的移動基於英雄位置和機器人位置產生的法向量。每當機器人作出決定,它的下一個決定的時間被設定為在未來的一個隨機時間。在此期間,它將繼續執行上次作出決定時所做出的動作。接著在update函式裡,this->updatePositions();前新增如下程式碼:
1 | this->updateRobots(dt); |
12345678910 | CCObject *pObject = NULL;CCARRAY_FOREACH(_robots, pObject){ Robot *robot = (Robot*)pObject; posX = MIN(_tileMap->getMapSize().width * _tileMap->getTileSize().width - robot->getCenterToSides(), MAX(robot->getCenterToSides(), robot->getDesiredPosition().x)); posY = MIN(3 * _tileMap->getTileSize().height + robot->getCenterToBottom(), MAX(robot->getCenterToBottom(), robot->getDesiredPosition().y)); robot->setPosition(ccp(posX, posY));} |
1 | #include "GameScene.h" |
1234567891011121314 | void GameLayer::endGame(){ CCLabelTTF *restartLabel = CCLabelTTF::create("RESTART", "Arial", 30); CCMenuItemLabel *restartItem = CCMenuItemLabel::create(restartLabel, this, menu_selector(GameLayer::restartGame)); CCMenu *menu = CCMenu::create(restartItem, NULL); menu->setPosition(CENTER); menu->setTag(5); _hud->addChild(menu, 5);}void GameLayer::restartGame(CCObject* pSender){ CCDirector::sharedDirector()->replaceScene(GameScene::create());} |
第一個方法建立顯示一個重新開始的按鈕,當按下它時,觸發第二個方法。後者只是命令導演用新的GameScene例項替換當前場景。接著在updateRobots函式裡面,在第一個end game checker here註釋後面,新增如下程式碼:
1234 | if (_hero->getActionState() == kActionStateKnockedOut && _hud->getChildByTag(5) == NULL){ this->endGame();} |
1234 | if (alive == 0 && _hud->getChildByTag(5) == NULL){ this->endGame();} |
12.音樂和音效。開啟GameLayer.cpp檔案,新增標頭檔案引用:
1 | #include "SimpleAudioEngine.h" |
1234567 | // Load audioCocosDenshion::SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("latin_industries.aifc");CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("latin_industries.aifc");CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit0.wav");CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit1.wav");CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_herodeath.wav");CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_botdeath.wav"); |
1 | #include "SimpleAudioEngine.h" |
12 | int randomSound = random_range(0, 1);CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(CCString::createWithFormat("pd_hit%d.wav", randomSound)->getCString()); |
1 | virtual void knockout(); |
1 | #include "SimpleAudioEngine.h" |
12345 | void Hero::knockout(){ ActionSprite::knockout(); CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pd_herodeath.wav");} |
1 | #include "SimpleAudioEngine.h" |
12345 | void Robot::knockout(){ ActionSprite::knockout(); CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pd_botdeath.wav");} |