上一节我们做了点准备工作, 本节我们将定义自己的sprite类,以便于后续的开发。Coco2dx提供的默认sprite类只提供了最基础的功能,我们要做动作游戏,仅仅只用cocos2dx的sprite类的功能是不够的,所以我们要继承sprite类,来实现自己的精灵类。
1.实现思路
动作游戏角色就是各种各样的动作,所以我们自定义的类会围绕这个功能来写,落羽这里只实现有代表性的几个动作,主要就是待机,走,攻击,受伤,死亡。动作游戏还要有数据,但是本节落羽不讲数据,只讲动画,数据以后再讲。效果如下
2.程序
定义枚举
enum class SpriteType //精灵类型 { PLAYER = 1, ENEMY = 2 }; enum class SpriteState //精灵状态 { NONE = 0, IDLE = 1, WALK = 2, ATTACK = 3, HURT = 4, KNOCKEDOUT = 5 };
头文件
#ifndef GAME_SPRITE_H #define GAME_SPRITE_H #include "cocos2d.h" #include "Define.h" USING_NS_CC; class GameSprite : public Sprite { public: ~GameSprite(); void init(Point point, SpriteType type, float walkSpeed);//位置,精灵类型,移动速度 void update(float dt); virtual void idle(); //待机 virtual void walkWithDirection(Point direction);//移动 virtual void attack(); //攻击 virtual void hurtWithDamage(int damage); //受伤 virtual void knockedout(); //死亡 void loadAnimation(SpriteType type); protected: Action *idleAction; //动画 Action *walkAction; Animate *attackAction; Animate *hurtAction; Animate *knockedoutAction; Point moveDirection; //移动方向 float walkSpeed; //移动速度 Point velocity; //移动向量 SpriteType type; //精灵类型 SpriteState state; //精灵状态 }; #endif
析构
GameSprite::~GameSprite() //释放动画 { if(idleAction != NULL) { idleAction->release(); idleAction = NULL; } if(walkAction != NULL) { walkAction->release(); walkAction = NULL; } if(attackAction != NULL) { attackAction->release(); attackAction = NULL; } if(hurtAction != NULL) { hurtAction->release(); hurtAction = NULL; } if(knockedoutAction != NULL) { knockedoutAction->release(); knockedoutAction = NULL; } }
初始化
void GameSprite::init(Point point, SpriteType type, float walkSpeed) { Sprite::init(); this->setPosition(point); this->type = type; this->walkSpeed = walkSpeed; moveDirection = Point::ZERO; velocity = Point::ZERO; state = SpriteState::NONE; loadAnimation(type); this->scheduleUpdate(); idle(); }
更新
void GameSprite::update(float dt) { Point distance = velocity * dt; //根据向量移动 setPosition(getPosition() + distance); }
待机
void GameSprite::idle() { if(state != SpriteState::IDLE) { state = SpriteState::IDLE; this->stopAllActions(); this->runAction(idleAction); } }
走路
void GameSprite::walkWithDirection(Point direction) { if(state != SpriteState::WALK) { state = SpriteState::WALK; this->stopAllActions(); this->runAction(walkAction); } }
攻击
void GameSprite::attack() { if(state != SpriteState::ATTACK) { state = SpriteState::ATTACK; this->stopAllActions(); auto *atta = Sequence::create(attackAction, CallFunc::create(CC_CALLBACK_0(GameSprite::idle, this)), NULL); this->runAction(atta); } }
受伤
void GameSprite::hurtWithDamage(int damage) { if(state != SpriteState::HURT) { state = SpriteState::HURT; this->stopAllActions(); auto *hurt = Sequence::create(hurtAction, CallFunc::create(CC_CALLBACK_0(GameSprite::idle, this)), NULL); this->runAction(hurt); } }
死亡
void GameSprite::knockedout() { if(state != SpriteState::KNOCKEDOUT) { state = SpriteState::KNOCKEDOUT; this->stopAllActions(); this->runAction(knockedoutAction); } }
加载动画
Animation* findAnimation(const char* animName, const char* fileName, int fileStartNum, int fileEndNum, float dur, Rect rect) { Animation *animation; animation = AnimationCache::getInstance()->getAnimation(animName); if(animation != NULL) { return animation; } else { Vector<SpriteFrame*> arr; char str[50]; for(int i = fileStartNum; i <= fileEndNum; ++i) { sprintf(str, "%s_%02d.png", fileName, i); arr.pushBack(SpriteFrame::create(str, rect)); } animation = Animation::createWithSpriteFrames(arr, dur); animation->setRestoreOriginalFrame(false); AnimationCache::getInstance()->addAnimation(animation, animName); return animation; } }
void GameSprite::loadAnimation(SpriteType type) { if(type == SpriteType::PLAYER || type == SpriteType::ENEMY) { Animation *animation; Animate *animate; //idle animation = findAnimation("playerNormalIdle", "player_brother_idle", 1, 1, 0.2, Rect(0, 0, 200, 300)); animate = Animate::create(animation); idleAction = RepeatForever::create(animate); idleAction->retain(); //walk animation = findAnimation("playerNormalWalk", "player_brother_move", 1, 4, 0.2, Rect(0, 0, 200, 300)); animate = Animate::create(animation); walkAction = RepeatForever::create(animate); walkAction->retain(); //attack animation = findAnimation("playerNormalAttack", "player_brother_attack", 1, 2, 0.4, Rect(0, 0, 200, 300)); animate = Animate::create(animation); attackAction = animate; attackAction->retain(); //hurt animation = findAnimation("playerNormalHurt", "player_brother_hurt", 1, 2, 0.2, Rect(0, 0, 200, 300)); animate = Animate::create(animation); hurtAction = animate; hurtAction->retain(); //knockedout animation = findAnimation("playerNormalKnockedout", "player_brother_die", 1, 2, 0.2, Rect(0, 0, 200, 300)); animate = Animate::create(animation); knockedoutAction = animate; knockedoutAction->retain(); } }
使用精灵类
auto *sprite = new GameSprite(); sprite->init(Point(400, 400), SpriteType::PLAYER, 100); sprite->walkWithDirection(Point::ZERO); this->addChild(sprite); sprite->release(); sprite = new GameSprite(); sprite->init(Point(600, 400), SpriteType::ENEMY, 100); sprite->attack(); this->addChild(sprite); sprite->release(); sprite = new GameSprite(); sprite->init(Point(800, 400), SpriteType::PLAYER, 100); sprite->hurtWithDamage(0); this->addChild(sprite); sprite->release(); sprite = new GameSprite(); sprite->init(Point(1000, 400), SpriteType::PLAYER, 100); sprite->knockedout(); this->addChild(sprite); sprite->release();
3.总结
本节落羽一步步实现了一个自定义精灵类,实现了各种动作,下一节我们讲类之间的交互。
本节源代码和图片资源