首先當敵人被擊毀時根據隨機數判斷是否出現一個獎勵。以下代碼簡單,就不做多解釋
void Enemy::doHurt() {
float percentage = this->_hp / this->_sourceHp;
if(Config::sharedConfig()->getIsOpenSlot()) {
this->_topSlot->setScaleY(percentage);
}
if(this->_hp <= 0) {
...
<span style="white-space:pre"> </span>
float appearRate = CCRANDOM_0_1();
Award* award = NULL;
if(appearRate < 0.2f) {
if(GameLayer::shareGameLayer()->getIsAppearShader() == false && this->_id != 3 &&
Config::sharedConfig()->getIsTsuihikidanMode() == false) {
GameLayer::shareGameLayer()->setIsAppearShader(true);
award = Award::getOrCreate(SHADER);
}
}else {
appearRate = CCRANDOM_0_1();
if(0.2f > appearRate && appearRate < 0.45f) {
if(GameLayer::shareGameLayer()->getIsAppearShield() == false && this->_id != 3) {
GameLayer::shareGameLayer()->setIsAppearShield(true);
award = Award::getOrCreate(PROTECTED_BODY);
}
}
}
if(award && award != NULL) {
award->setVisible(true);
award->setPosition(this->getPosition());
award->startAction();
award->scheduleUpdate();
}
}
}
獎勵現在只做了三個,分別是:護盾、增加恢復值、敵人血量減半。用三個枚舉值記錄:
typedef enum {
PROTECTED_BODY,
ADD_RESUME_VALUE,
SHADER
}AWARD_TYPE;
最終出現的獎勵效果是由三個精靈組成:
其中,小光圈繞着圓形的環做圓周運動,中間的主體精靈做翻轉動作,當碰到屏幕的任何一邊後會根據其碰撞的角度做90度的反彈,當碰到主角後觸發獎勵功能。定義完了顯示效果後便是實現它。
class Award : public CCNode
{
public:
Award();//構造
bool init();//初始化
virtual void execute() = 0;//獎勵的實現功能
void startAction();//開始動作
void doAction();//執行動作
void endAction();//結束動作
CCRect collideRect();//碰撞判斷矩形
static void preSet();//重置
static Award* getOrCreate(AWARD_TYPE awardType);//根據不同的獎勵類別獲取或創建不同的獎勵
CC_SYNTHESIZE_READONLY(AWARD_TYPE, _awardType, AwardType);//獎勵類別
CC_SYNTHESIZE_READONLY(float, _speed, Speed);//速度
CC_SYNTHESIZE(int, _direction, Direction);//方向
CC_SYNTHESIZE(bool, _active, Active);//激活
void update(float dt);//更新
protected:
CCSprite* _circle;//圓環
CCSprite* _body;//獎勵主體
CCSprite* _halo;//小光圈
private:
bool _leftCollision;//是否碰到左邊的屏幕
bool _rightCollision;//是否碰到右邊的屏幕
bool _topCollision;//是否碰到上邊的屏幕
bool _bottomCollision;//是否碰到下邊的屏幕
};
其中獎勵的基類中有個虛函數execute,這個函數是真正的實現獎勵的功能的真正觸發點,由各自的子類實現各自的獎勵功能(這裏採用狀態機的實現方式,而不用變量_awardType去做switch或if的判斷,這樣可以讓程序的可維護性和代碼的可讀性更高)。除了觸發功能點外其餘的動作都交由這個基類來實現(畢竟對於子類來說他們就是實現的功能不一樣而已,其餘的像碰壁檢測等動作都是一樣的)。
實現類:
Award::Award() {//初始化成員變量
_leftCollision = false;
_rightCollision = false;
_topCollision = false;
_bottomCollision = false;
_circle = NULL;
_body = NULL;
_halo = NULL;
}
bool Award::init() {//初始化圓環和小光圈(主體由子類去完成)
_circle = CCSprite::createWithSpriteFrameName("circle.png");
_halo = CCSprite::createWithSpriteFrameName("halo.png");
this->addChild(_circle);
this->addChild(_halo);
_halo->setPosition(ccp(_circle->getContentSize().width / 2, 0));
return true;
}
void Award::preSet() {
for(unsigned int i = 0; i < 3; i++) {//分別預創建三個獎勵
ProtectedBodyAward::create();
AddResumeAward::create();
ShaderAward::create();
}
}
void Award::startAction() {//開始動作
if(_body) {
//主體精靈做翻轉動作,可以由引擎自帶的CCOrbitCamera類來實現
CCActionInterval* orbit = CCOrbitCamera::create(1, 1, 0, 0, 180, 180, 0);
_body->runAction(CCRepeatForever::create(CCSequence::create(orbit, orbit->reverse(), NULL)));
}
//小光圈做圓周運動,有自定義的CircleAction類來實現
_halo->runAction(CCRepeatForever::create(CircleAction::create(10, _circle->getPosition(), _circle->getContentSize().width / 2)));
}
void Award::endAction() {//結束動作
this->stopAllActions();
}
void Award::doAction() {//當主角碰到獎勵時觸發該方法
this->setActive(false);//設置不激活
this->unscheduleUpdate();//停止調度更新
this->endAction();//停止所有動作
this->execute(); //觸發獎勵功能
this->setVisible(false);//設置不可見
}
void Award::update(float dt) {
CCPoint p = this->getPosition();
CCSize size = _circle->getContentSize();
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
if(this->_direction < 1) {
this->_direction = (int)(CCRANDOM_0_1() * 360.0f);
}
//碰壁檢測
if(p.x - size.width / 2 <= 10 || p.y - size.height / 2 <= 10 || p.x + size.width / 2 >= ScreenWidth - 10 || p.y + size.height / 2 >= ScreenHeight - 10) {
if(p.x - size.width / 2 <= 10 && !_leftCollision) {//碰到左壁
if(this->_direction > 180) {
this->_direction = this->_direction + 90;
}else {
this->_direction = this->_direction - 90;
}
_leftCollision = true;
_rightCollision = false;
_topCollision = false;
_bottomCollision = false;
}else if(p.y - size.height / 2 <= 10 && !_bottomCollision) {//碰到下壁
if(this->_direction > 270) {
this->_direction = this->_direction + 90;
}else {
this->_direction = this->_direction - 90;
}
_leftCollision = false;
_rightCollision = false;
_topCollision = false;
_bottomCollision = true;
}else if(p.x + size.width / 2 >= winSize.width - 10 && !_rightCollision) {//碰到右壁
if(this->_direction < 180) {
this->_direction = this->_direction + 90;
}else {
this->_direction = this->_direction - 90;
}
_leftCollision = false;
_rightCollision = true;
_topCollision = false;
_bottomCollision = false;
}else if(p.y + size.height / 2 >= ScreenHeight - 10 && !_topCollision){//碰到上壁
if(this->_direction < 90) {
this->_direction = this->_direction - 90;
}else {
this->_direction = this->_direction + 90;
}
_leftCollision = false;
_rightCollision = false;
_topCollision = true;
_bottomCollision = false;
}
//計算最終的反彈角度
if(this->_direction > 360) {
this->_direction = this->_direction % 360;
}else if(this->_direction < 0) {
this->_direction = 360 + this->_direction;
}
float r = CC_DEGREES_TO_RADIANS(this->_direction);
float c = cosf(r);
float s = sinf(r);
p = this->getPosition();
this->setPosition(ccp(p.x + this->_speed * dt * c, p.y + this->_speed * dt * s));
}else {
float r = CC_DEGREES_TO_RADIANS(this->_direction);
float c = cosf(r);
float s = sinf(r);
this->setPosition(ccp(p.x + this->_speed * dt * c, p.y + this->_speed * dt * s));
}
}
Award* Award::getOrCreate(AWARD_TYPE awardType) {
CCArray* array = Container::sharedContainer()->getAwards();
CCObject* iterator;
Award* award = NULL;
CCARRAY_FOREACH(array, iterator) {
award = (Award*)(iterator);
if(award && award->getAwardType() == awardType && award->getActive() == false) {
award->setActive(true);
//設置初始化角度
int direction = (int)(CCRANDOM_0_1() * 360);
if(direction < 90) {
direction = direction + 90;
}else if(direction > 270) {
direction = direction - 90;
}
award->setDirection(direction);
return award;
}
}
if(awardType == PROTECTED_BODY) {
award = ProtectedBodyAward::create();
}else if(awardType == ADD_RESUME_VALUE) {
award = AddResumeAward::create();
}else if(awardType == SHADER) {
award = ShaderAward::create();
}
return award;
}
CCRect Award::collideRect() {//構造碰撞體
CCPoint p = this->getPosition();
CCSize size = _circle->getContentSize();
return CCRectMake(p.x - size.width / 2, p.y - size.height / 2, size.width, size.height);
}
實現了基類後便要實現三個不同獎勵功能的子類:
1)護盾:
class ProtectedBodyAward : public Award {
public:
static ProtectedBodyAward* create();
bool init();
void execute();
};
ProtectedBodyAward* ProtectedBodyAward::create() {
ProtectedBodyAward* protectedBodyAward = new ProtectedBodyAward();
protectedBodyAward->init();
Container::sharedContainer()->getAwards()->addObject(protectedBodyAward);
protectedBodyAward->release();
protectedBodyAward->setVisible(false);
GameLayer::shareGameLayer()->addChild(protectedBodyAward);
return protectedBodyAward;
}
bool ProtectedBodyAward::init() {
bool result = false;
if(Award::init()) {
this->_active = true;
this->_awardType = PROTECTED_BODY;//獎勵類別
this->_speed = 150;
int direction = (int)(CCRANDOM_0_1() * 360);
if(direction < 90) {
direction = direction + 90;
}else if(direction > 270) {
direction = direction - 90;
}
this->_direction = direction;
_body = CCSprite::createWithSpriteFrameName(shield_png);//主體精靈
this->addChild(_body);
}
return result;
}
void ProtectedBodyAward::execute() {
GameLayer::shareGameLayer()->getShip()->getProtectBody()->protectStart();//觸發護盾
}
其餘的兩個都是一樣的,都是在execute中實現不同的功能,隨後在跟主角碰撞時觸發下doAction方法即可。
//獎勵判斷
CCArray* awards = Container::sharedContainer()->getAwards();
Award* award = NULL;
for (unsigned int j = 0; j < awards->count(); j++) {
award = (Award*)awards->objectAtIndex(j);
if(award->getActive()) {
if(this->_ship->boundingBox().intersectsRect(award->collideRect())) {
if(this->_isAppearShield && award->getAwardType() == PROTECTED_BODY) {
if(this->_ship->getIsProtected() == false && this->_ship->getProtectBody()->getActive() == false) {
award->doAction();
}
}else if(this->_isAppearShader && award->getAwardType() == SHADER) {
award->doAction();
this->scheduleOnce(schedule_selector(GameLayer::endShader), STATIC_DATA_FLOAT("shader_time"));
}else if(award->getAwardType() == ADD_RESUME_VALUE){
award->doAction();
}
}
}
}