首先,先创建一个特效层,其主要负责特效,如帧动画或粒子特效。
#ifndef __EffectLayer_H__
#define __EffectLayer_H__
#include <vector>
#include <string>
#include "SDL_Engine/SDL_Engine.h"
using namespace std;
USING_NS_SDL;
class EffectLayer : public Layer
{
private:
static const int ANIMATION_TAG;
private:
vector<Sprite*> m_spritePool;
Sprite* m_pClickPoint;
LayerColor* m_pLayerColor;
public:
EffectLayer();
~EffectLayer();
CREATE_FUNC(EffectLayer);
bool init();
bool showClickAnimation(const Point& position, Layer* layer = nullptr);
//显示动画特效
bool showAnimationEffect(const string& animationName, const Point& position, Layer* layer = nullptr);
//淡入场景
void fadeInScreen(float duration);
//淡出场景
void fadeOutScreen(float duration);
private:
Sprite* popSpriteFromPool();
void pushSpriteToPool(Sprite* sprite);
bool showAnimationEffect(Sprite* sprite, const string& animationName, const Point& position, Layer* layer = nullptr);
};
#endif
EffectLayer中包含了一个精灵池,负责精灵的分配与回收;一个点击精灵,负责当点击地面时显示一个反馈动画;LayerColor则是为了淡入场景和淡出场景考虑(cocos2d-x给的场景切换特效并不适合此时的情况)。
bool EffectLayer::showAnimationEffect(Sprite* sprite, const string& animationName, const Point& position, Layer* layer)
{
auto animation = AnimationCache::getInstance()->getAnimation(animationName);
if (animation == nullptr)
return false;
//添加精灵
if (layer != nullptr)
layer->addChild(sprite);
else
this->addChild(sprite);
sprite->setSpriteFrame(animation->getFrames().front()->getSpriteFrame());
sprite->setPosition(position);
sprite->setName(animationName);
sprite->setVisible(true);
//创建动画
auto animate = Animate::create(animation);
CallFunc* end = CallFunc::create([this, sprite]()
{
this->pushSpriteToPool(sprite);
});
auto seq = Sequence::createWithTwoActions(animate, end);
seq->setTag(ANIMATION_TAG);
//运行动画
sprite->runAction(seq);
return true;
}
先把精灵添加到对应的层中,然后再创建对应动画并运行动画(此时的特效层函数后续可能会修改)。
bool EffectLayer::showClickAnimation(const Point& position, Layer* layer)
{
string animationName = "click_path";
//当前为空 或者正在播放且名称不为click_path 表示被别人占用,另选
if (m_pClickPoint == nullptr
|| (m_pClickPoint->getParent() != nullptr
&& m_pClickPoint->getName() != animationName))
{
m_pClickPoint = this->popSpriteFromPool();
}
else
{
m_pClickPoint->stopActionByTag(ANIMATION_TAG);
}
return this->showAnimationEffect(m_pClickPoint, animationName, position, layer);
}
这里增加了一个判断,主要是保证点击反馈动画在同一时间内只有一个,且为了保证利用率,当点击动画完成后,依然放入精灵池中。
bool EffectLayer::showAnimationEffect(const string& animationName, const Point& position, Layer* layer)
{
Sprite* sprite = this->popSpriteFromPool();
return this->showAnimationEffect(sprite, animationName, position, layer);
}
重载函数,只是选定了精灵的来源。
void EffectLayer::fadeInScreen(float duration)
{
auto sprite = m_pLayerColor->getInnerSprite();
m_pLayerColor->setVisible(true);
sprite->setOpacity(0);
FadeIn* fade = FadeIn::create(duration);
CallFunc* end = CallFunc::create([this]()
{
this->m_pLayerColor->setVisible(false);
});
auto seq = Sequence::createWithTwoActions(fade,end);
sprite->runAction(fade);
}
void EffectLayer::fadeOutScreen(float duration)
{
auto sprite = m_pLayerColor->getInnerSprite();
m_pLayerColor->setVisible(true);
sprite->setOpacity(255);
FadeOut* fade = FadeOut::create(duration);
CallFunc* end = CallFunc::create([this]()
{
this->m_pLayerColor->setVisible(false);
});
auto seq = Sequence::createWithTwoActions(fade,end);
sprite->runAction(fade);
}
淡入淡出函数,LayerColor中主要起到作用的是内部的精灵,故对内部精灵进行淡入淡出操作。(这里是SDL_Engien的实现问题。。)。
Sprite* EffectLayer::popSpriteFromPool()
{
Sprite* sprite = nullptr;
if (m_spritePool.empty())
{
sprite = Sprite::create();
}
else
{
sprite = m_spritePool.back();
sprite->autorelease();
m_spritePool.pop_back();
}
return sprite;
}
void EffectLayer::pushSpriteToPool(Sprite* sprite)
{
SDL_SAFE_RETAIN(sprite);
sprite->removeFromParent();
m_spritePool.push_back(sprite);
}
由于把精灵放入精灵池中的时候,会SDL_SAFE_RETAIN即引用一下这个精灵,故在弹出可用精灵时,为保证其通用性,精灵主动调用了一下autorelease函数。
然后在GameScene类中添加特效层即可。
class GameScene : public Scene
{
private:
static GameScene* s_pInstance;
public:
static GameScene* getInstance();
static void purge();
SDL_SYNTHESIZE_READONLY(EventLayer*, m_pEventLayer, EventLayer);
SDL_SYNTHESIZE_READONLY(MapLayer*, m_pMapLayer, MapLayer);
SDL_SYNTHESIZE_READONLY(PlayerLayer*, m_pPlayerLayer, PlayerLayer);
SDL_SYNTHESIZE_READONLY(ScriptLayer*, m_pScriptLayer, ScriptLayer);
SDL_SYNTHESIZE_READONLY(EffectLayer*, m_pEffectLayer, EffectLayer); SDL_SYNTHESIZE_READONLY(EventLayer*, m_pEventLayer, EventLayer);
SDL_SYNTHESIZE_READONLY(MapLayer*, m_pMapLayer, MapLayer);
SDL_SYNTHESIZE_READONLY(PlayerLayer*, m_pPlayerLayer, PlayerLayer);
SDL_SYNTHESIZE_READONLY(ScriptLayer*, m_pScriptLayer, ScriptLayer);
SDL_SYNTHESIZE_READONLY(EffectLayer*, m_pEffectLayer, EffectLayer);
这里使用了宏,其宏如下:
#define SDL_SYNTHESIZE_READONLY(varType,varName,funcName) \
protected:varType varName; \
public: varType get##funcName(void)const{return varName;}
即声明一个保护成员和一个getter方法,对应的getter方法会在以后注册c函数给lua时用到。
然后在onTouchBegan函数中添加即可。
else if (!this->isPassing(tilePos))//目标不可达
return false;
//主角运动
player->moveToward(tilePos);
//显示点击特效
auto collisionLayer = m_pMapLayer->getCollisionLayer();
auto tileSize = tiledMap->getTileSize();
Point pos((tilePos.x + 0.5f) * tileSize.width, (tilePos.y + 0.3f) * tileSize.height);
m_pEffectLayer->showClickAnimation(pos, collisionLayer); //显示点击特效
auto collisionLayer = m_pMapLayer->getCollisionLayer();
auto tileSize = tiledMap->getTileSize();
Point pos((tilePos.x + 0.5f) * tileSize.width, (tilePos.y + 0.3f) * tileSize.height);
m_pEffectLayer->showClickAnimation(pos, collisionLayer);
注意此时的显示仍有些绘制顺序问题,这个以后再解决。

本文详细介绍了一种游戏特效层的设计与实现方案,包括精灵池的管理、点击反馈动画的处理、淡入淡出效果的实现,以及如何将特效层整合到游戏场景中。
2544





