首先当敌人被击毁时根据随机数判断是否出现一个奖励。以下代码简单,就不做多解释
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();
}
}
}
}