返回列表 回复 发帖

Coco3dx第三讲:闪电特效

大家好,上一节我们学习了如何给鱼身上加上波光的效果。这一节,我们将学习如何制作攻击时的闪电特效。

《捕鱼达人》中的闪电,是通过以下几个步骤来实现的:
一、构建三角形条带。
二、采用随机函数来扰动条带顶点。
三、快速反复循环一,二步骤。

这个过程主要使用到以下三个类:
(1)VertexVector:用于存储顶点和索引的类。
(2)Noise:噪音处理类,用于进行顶点扰动,使顶点不断的小幅变化。
(3)LightLineRender:实现闪电效果的类。

具体的代码实现,可以参看注释。我们要关心的是LightLineRender类,它封装了闪电效果,并提供了相应的参数调节选项,我们学会了使用它就可以做出闪电链的效果。

我们打开cpp-empty-test,在HelloWorld::init函数中加入以下代码:

1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


//线条容器
std::vector<LightLineRender:ine> lines;
//设置线条位置
//第一段闪电的起点和终点
Vec3 segStart = Vec3(-50,-50,-8);
Vec3 segEnd   = Vec3(50,50,-8);
lines.push_back( LightLineRender:ine( segStart, segEnd, 0 ) );
//第二段闪电的起点和终点
segStart = Vec3(50,50,-8);
segEnd   = Vec3(-50,50,-8);
lines.push_back( LightLineRender:ine( segStart, segEnd, 0 ) );
//第三段闪电的起点和终点
segStart = Vec3(-50,50,-8);
segEnd   = Vec3(50,-50,-8);
lines.push_back( LightLineRender:ine( segStart, segEnd, 0 ) );
//第四段闪电的起点和终点
segStart = Vec3(50,-50,-8);
segEnd   = Vec3(0,100,-8);
lines.push_back( LightLineRender:ine( segStart, segEnd, 0 ) );
//第五段闪电的起点和终点
segStart = Vec3(0,100,-8);
segEnd   = Vec3(-50,-50,-8);
lines.push_back( LightLineRender:ine( segStart, segEnd, 0 ) );
//创建出闪光链
LightLineRender*    _lighting = LightLineRender::create();
//设置不需要强制纹理循环
_lighting->setForceTexLoop( false );
//设置宽
_lighting->setWidth( 80 );
//设置 单张纹理长度,调整这个数值可以避免纹理过度拉伸或挤压
_lighting->setTextueLength( 100 );
//设置单个面片网格长,越小曲线越平滑,数值过于小可能带来效率问题
_lighting->setStep( 10 );
//设置振幅1
_lighting->setAmplitude0( 4 );
//设置频率1
_lighting->setFrequency0( 500 );
//设置振幅2
_lighting->setAmplitude1( 1 );
//设置频率2
_lighting->setFrequency1( 400 );
//设置产生噪音的时间系数
_lighting->setTimeFactor( 0.5 );
//使用线段容器创建闪电链
_lighting->setLines( lines );
//使用柏林噪音算法
_lighting->setLineType( LineType:T_PerlinNosie );
//设置每帧强制更新重建模型
_lighting->setForceUpdate(true);
//设置位置
_lighting->setPosition(Vec2(visibleSize.width / 4 + origin.x,visibleSize.height / 2 + origin.y));
//将闪电链加入到当前层中。
this->addChild(_lighting,0,10);




这样我们完成了使用了五条闪电组成一个五星闪电链不断的闪动。运行后的效果如图所示:


然后我们希望在触屏时能够有一条闪电链击中屏幕中央的乌龟,乌龟被击中后翻个身,闪电链渐渐消失,我们可以这样做:

首先我们在FishLayer这个层里将乌龟循环播放的游泳与被击中的两个动作改为只播放游泳,然后我们增加两个函数:

1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


//击中乌龟
void    FishLayer::AttackWuGui()
{
    if (m_Animation3D)
    {
        //从1.933秒到2.8秒截取为受伤的动作
        m_Hurt = Animate3D::create(m_Animation3D, 1.933f, 2.8f);
        m_Hurt->retain();

        m_Sprite->stopAllActions();
        //让精灵循环播放游泳和的受伤动作
        Sequence*   pSequence = Sequence::create(m_Hurt,CallFunc::create( std::bind(&FishLayer::ContinueSwim, this) ),NULL);
        m_Sprite->runAction(pSequence);

    }

}
//继续游动
void    FishLayer::ContinueSwim()
{
    if (m_Animation3D)
    {
        //从起始到1.933秒截取为游泳动作
        m_Swim = Animate3D::create(m_Animation3D, 0.f, 1.933f);
        m_Swim->retain();

        m_Sprite->stopAllActions();
        //让精灵循环播放游泳和的受伤动作
        Sequence*   pSequence = Sequence::create(m_Swim,NULL);
        m_Sprite->runAction(RepeatForever::create(pSequence));
    }
}




然后在HelloWorld的init函数尾部增加代码:

1


2


//设置可以点击
setTouchEnabled( true );





最后,我们重载一下onToucesBegan函数:

1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


//触屏事件处理
void HelloWorld:nTouchesBegan(const std::vector<Touch*>& touches, Event *unused_event)
{
//屏幕转换到射线
    kmVec3    tPt;
    kmVec3    tDir;
    // 获取点在视图中的坐标
    CCPoint touchLocation = touches[0]->getLocation();
    auto    visibleSize = Director::getInstance()->getVisibleSize();
    auto    origin = Director::getInstance()->getVisibleOrigin();
    //线条容器
    std::vector<LightLineRender:ine> lines;
    //闪电的起点和终点
    Vec2    tFishPos(Vec2(visibleSize / 2) + origin);
    tFishPos = m_FishLayer->GetSpritePosition() + origin;
    Vec3 segStart = Vec3(0,0,-8);
    Vec3 segEnd   = Vec3(touchLocation.x - tFishPos.x ,touchLocation.y - tFishPos.y ,-8);
    //取得方向
    Vec3  dir = segEnd - segStart ;
    float fLength = dir.length();
    dir.normalize();
    //顺时针转动45度形成一个偏移点做为第一个闪电链线段。
    Vec3  rotate_left;
    Mat4  rotate_left_Mat;
    kmMat4RotationZ(&rotate_left_Mat,MATH_DEG_TO_RAD(-45));
    kmVec3TransformCoord(&rotate_left,&dir,&rotate_left_Mat);
    rotate_left.normalize();
    //逆时针转动45度形成一个偏移点做为第一个闪电链线段。
    Vec3  rotate_right;
    Mat4  rotate_right_Mat;
    kmMat4RotationZ(&rotate_right_Mat,MATH_DEG_TO_RAD(45));
    kmVec3TransformCoord(&rotate_right,&dir,&rotate_right_Mat);
    rotate_right.normalize();

    //分成三段闪电链
    Vec3  v1_s = segStart ;
    Vec3  v1_e = segStart + dir * fLength / 4.0 + rotate_left * (fLength / 6.0);

    Vec3  v2_s = v1_e ;
    Vec3  v2_e = segStart + dir * fLength / 2.0 + rotate_right * (fLength / 6.0);

    Vec3  v3_s = v2_e ;
    Vec3  v3_e = segEnd;

    lines.push_back( LightLineRender:ine( v1_s, v1_e, 0 ) );
    lines.push_back( LightLineRender:ine( v2_s, v2_e, 0 ) );
    lines.push_back( LightLineRender::Line( v3_s, v3_e, 0 ) );
    //创建出闪光链
    LightLineRender*    _lighting = dynamic_cast<LightLineRender*>(getChildByTag(10));
    //使用线段容器创建闪电链
    _lighting->setLines( lines );
    _lighting->setPosition(tFishPos);
    //这一句可以让闪电链在1秒内渐渐消隐。它通过调节Shader中的u_color值从1变为0来实现。
    _lighting->OpenAlphaToZero(1.0);
    //击中乌龟,让乌龟翻身。
    m_FishLayer->AttackWuGui();
}




再次运行后,我们点击屏幕,就可以看到从点击屏幕位置到乌龟位置间会出现一条闪电链击中小乌龟啦!小乌龟摇了摇身体,勇敢的继续前行!
返回列表