Cocos2d-x 3.x基础学习: 拖尾渐隐效果MotionStreak

2015年03月23日 11:06 0 点赞 0 评论 更新于 2020-01-11 23:24

在许多游戏中,常常需要一些炫目的效果。例如,我们有时需要在某个游戏对象的运动轨迹上实现拖尾渐隐效果,这种效果类似飞机拉线,能让游戏在视觉上更具吸引力。常见的应用场景包括刀光、子弹的运动轨迹、流星划痕等。

Cocos2d-x提供了一种内置的拖尾渐隐效果实现方法:MotionStreak。当然,要做出酷炫的拖尾效果,通常需要与粒子特效Particle相结合。下面附上一张唯美的拖尾效果(MotionStreak + 粒子特效)图片,希望能激发大家的学习热情。学完MotionStreak后,相信大家也能实现类似的效果。

【MotionStreak】

1、原理

MotionStreak的拖尾效果原理是:在相应距离内动态生成一段段的条带,然后这些条带逐渐消隐。

可以指定以下参数:

  • fade:消隐速度(时间,单位为秒)。
  • minSeg:一段条带的最小距离,即拖尾条带相连顶点间的最小距离。
  • stroke:条带的宽度。
  • color:条带的颜色值。
  • 纹理图片对象:用于设置条带的纹理。

具体来说,MotionStreak在移动过程中(setPosition位置发生改变时),会动态生成一段段条带段。这些条带段会在生命周期fade秒内渐渐隐去(慢慢变透明),从而形成拖尾效果。

需要注意的是,由于拖尾是由一段段条带相连形成的,如果条带太粗(stroke太大),在视觉上可能会出现“脱节”的效果。因此,在实际使用中,stroke的大小设置应该合理。

2、创建方式

创建MotionStreak有两种方式:

  • 使用图片资源文件作为纹理创建
  • 通过纹理图片Texture2D创建

各个参数的说明在“原理”部分已经给出,以下是创建函数的定义:

// fade : 拖尾渐隐时间(秒)
// minSeg : 最小的片段长度(渐隐片段的大小),拖尾条带相连顶点间的最小距离
// stroke : 渐隐条带的宽度
// color : 片段颜色值
// path : 纹理图片的文件名
// texture : 纹理图片的对象指针
static MotionStreak* create(float fade, float minSeg, float stroke, const Color3B& color, const std::string& path);
static MotionStreak* create(float fade, float minSeg, float stroke, const Color3B& color, Texture2D* texture);

// 使用举例
auto motionstreak = MotionStreak::create(1.0f, 1.0f, 10.0f, Color3B(255, 0, 0), "streak.png");

3、相关函数

为了实现拖尾渐隐效果,MotionStreak对Node类的一些函数进行了重载,如update、draw、setPosition等。

每当MotionStreak改变位置(setPosition)后,就会形成一条拖尾。在update函数中,会根据位置信息产生一段段新的顶点(条带段),并让之前生成的条带段渐隐或消失。

以下是一些相关函数的介绍:

// 条带段使用的颜色值
void tintWithColor(const Color3B& colors);

// 重置,删除所有条带段
void reset();

// 设置是否是快速模式,默认为true
// 当为快速模式时,新的顶点被更快地加入,但是新的顶点具有更小的精确值
inline bool isFastMode() const { return _fastMode; }
inline void setFastMode(bool bFastMode) { _fastMode = bFastMode; }

// 在刚创建MotionStreak的时候会置为false(表示创建后,还未移动过)
// 一旦改变Position(即移动后),会置为true(表示拖尾效果开始了,然后移动就会有拖尾的效果了)
// 一般不会手动去设置它,所以不需要了解这个函数
inline bool isStartingPositionInitialized() const { return _startingPositionInitialized; }
inline void setStartingPositionInitialized(bool bStartingPositionInitialized) { _startingPositionInitialized = bStartingPositionInitialized; }

4、支持Action动作

MotionStreak继承自Node类。由于它一旦移动(位置Position发生改变)就会拉出一条拖尾,因此执行MoveTo/MoveBy或者JumpTo/JumpBy等Action动作时,自然也能形成拖尾效果。

5、使用步骤

(1) 创建MotionStreak:使用MotionStreak::create()函数。 (2) 将MotionStreak加入到场景中:使用this->addChild()函数。 (3) 设置位置或执行移动Action动作:可以使用MotionStreak->setPosition()函数,也可以执行移动Action动作。 (4) 形成拖尾效果:一旦改变了位置,就会形成一段拖尾效果。

【代码实践】

1、《水果忍者》划动刀光效果(MotionStreak + 粒子特效)

代码实现可参见:http://cn.cocos2d-x.org/tutorial/show?id=2225

手势划动产生的刀光效果,是利用触摸移动事件,改变MotionStreak的位置来形成拖尾的。

2、《流星划痕效果》(精灵移动 + MotionStreak + 粒子效果)

代码实现可参见:http://cn.cocos2d-x.org/tutorial/show?id=1483

流星的尾巴,是在schedule/update中不断改变MotionStreak的位置来形成拖尾的。

3、通过触摸事件,实现拖尾效果

图片资源借用《流星划痕效果》。

预期实现效果:

  • 触摸开始(touchBegan):流星的位置设置为触摸点位置。
  • 触摸移动(touchMoved):流星跟随触摸点移动,同时MotionStreak跟随流星移动,形成拖尾效果。

创建两种不同规格的MotionStreak,查看效果:

  • 设置minSeg = 50stroke = 30color = WHITE、纹理为steak.png
  • 设置minSeg = 1stroke = 10color = RED、纹理为steak.png

3.1、在HelloWorld.h中添加如下变量与函数

class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);

// 触摸事件 回调函数
bool onTouchBegan(Touch* pTouch, Event* pEvent);
void onTouchMoved(Touch* pTouch, Event* pEvent);

private:
Sprite* star; // 流星精灵
MotionStreak* streak; // 拖尾
};

3.2、在HelloWorld.cpp的init()中,创建流星精灵、MotionStreak拖尾,并添加触摸监听事件

bool HelloWorld::init()
{
if ( !Layer::init() ) return false;

// 流星精灵Sprtie
star = Sprite::create("PIC_XX.png");
star->setPosition(100, 100);
this->addChild(star);

// 流星拖尾MotionStreak
streak = MotionStreak::create(0.5f, 50, 30, Color3B::WHITE, "steak.png");
// streak = MotionStreak::create(0.5f, 1, 10, Color3B::RED, "steak.png");
streak->setPosition(star->getPosition()); // 设置拖尾streak的位置
this->addChild(streak);

// 注册单点触摸
auto dispatcher = this->getEventDispatcher();
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this); // 触摸开始
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this); // 触摸移动
dispatcher->addEventListenerWithSceneGraphPriority(listener, this);

return true;
}

3.3、实现触摸事件

  • 触摸开始(touchBegan):流星位置设置为触摸点。
  • 触摸移动(touchMoved):流星移动,streak跟随流星移动,形成拖尾效果。
// 触摸开始 :设置star和streak的位置
bool HelloWorld::onTouchBegan(Touch* pTouch, Event* pEvent)
{
// 获取触摸点位置
Vec2 pos = pTouch->getLocation();
// 设置位置
star->setPosition(pos);
streak->setPosition(star->getPosition());
// 删除所有活动条带段
streak->reset();
return true;
}

// 触摸移动 :移动star和streak的位置
void HelloWorld::onTouchMoved(Touch* pTouch, Event* pEvent)
{
// 触摸移动的偏移量
Vec2 delta = pTouch->getDelta();
// 设置位置
star->setPosition(star->getPosition() + delta);
streak->setPosition(star->getPosition());
}

3.4、运行结果

  • 设置minSeg = 50stroke = 30color = WHITE、纹理为steak.png
  • 设置minSeg = 1stroke = 10color = RED、纹理为steak.png

关于streak->reset()函数: 在onTouchBegan中调用streak->reset()函数的原因是,如果去掉这句话,每次触摸开始时,流星直接移动到触摸点,streak也跟随改变位置,就会出现没有触摸拖动,只是点击鼠标也会出现拖尾划痕的现象。streak->reset()的作用是清除所有活动条带段。

3.5、分析与总结

  • 如果minSegstroke设置较大,即每一段条带都比较长或宽,视觉上会看到明显“一节一节”的效果。
  • 如果拖尾效果不与粒子特效组合使用,会比较单调。
  • MotionStreak只要改变位置,就会形成拖尾效果,无论是使用setPosition,还是执行动作(如MoveTo、JumpTo)。
  • reset()函数可以清除当前所有存在的渐隐条带,实现重置功能。

MotionStreak的应用潜力巨大,希望大家能充分发挥创意,组合出各种“酷炫”的拖尾效果。

作者信息

boke

boke

共发布了 3994 篇文章