iOS粒子特效

最早接触这个概念是在去年的圣诞节的时候,那个时候手机淘宝首页出了一个页面下雪的动画,当时觉得蛮炫酷的,而且以为实现起来蛮难的,后来自己查了资料才知道“粒子特效”这个概念,今天就来简单聊聊它吧。

粒子特效

粒子系统表示三维计算机图形学中模拟一些特定的模糊现象的技术,而这些现象用其它传统的渲染技术难以实现的真实感的游戏图形。经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等。

以上定义来自维基百科,详细的解释点击这里.

在iOS系统当中的实现

粒子特效由两个部分组成,一个是发射器,主要负责粒子的生成和发射,另一个是粒子单元,用于描述粒子的状态。在iOS系统当中这两部分分别由CAEmitterLayer(发射器)和CAEmitterCell(组成)。

具体使用可以查看以下代码:

//雪花动画
- (void)snow {

    //粒子发射器
    CAEmitterLayer *snowEmitter = [CAEmitterLayer layer];
    //粒子发射的位置
    snowEmitter.emitterPosition = CGPointMake(100, 30);
    //发射源的大小
    snowEmitter.emitterSize = CGSizeMake(self.view.bounds.size.width, 0.0);
    //发射模式
    snowEmitter.emitterMode = kCAEmitterLayerOutline;
    //发射源的形状
    snowEmitter.emitterShape = kCAEmitterLayerLine;

    //创建雪花粒子
    CAEmitterCell *snowflake = [CAEmitterCell emitterCell];
    //粒子的名称
    snowflake.name = @"snow";
    //粒子参数的速度乘数因子。越大出现的越快
    snowflake.birthRate = 1.0;
    //存活时间
    snowflake.lifetime = 120.0;
    //粒子速度
    snowflake.velocity = -10;                // falling down slowly
    //粒子速度范围
    snowflake.velocityRange = 10;
    //粒子y方向的加速度分量
    snowflake.yAcceleration = 2;
      //周围发射角度
    snowflake.emissionRange = 0.5 * M_PI;        // some variation in angle
    //子旋转角度范围
    snowflake.spinRange = 0.25 * M_PI;        // slow spin
    //粒子图片
    snowflake.contents = (id)[[UIImage imageNamed:@"DazFlake"] CGImage];
    //粒子颜色
    snowflake.color = [[UIColor redColor] CGColor];

    //设置阴影
    snowEmitter.shadowOpacity = 1.0;
    snowEmitter.shadowRadius = 0.0;
    snowEmitter.shadowOffset = CGSizeMake(0.0, 1.0);
    snowEmitter.shadowColor = [[UIColor whiteColor] CGColor];

    // 将粒子添加到粒子发射器上
    snowEmitter.emitterCells = [NSArray arrayWithObject:snowflake];
    [self.view.layer insertSublayer:snowEmitter atIndex:0];
}

//烟花动画
- (void)fireworks {
    // Cells spawn in the bottom, moving up

    //分为3种粒子,子弹粒子,爆炸粒子,散开粒子
    CAEmitterLayer *fireworksEmitter = [CAEmitterLayer layer];
    CGRect viewBounds = self.view.layer.bounds;
    fireworksEmitter.emitterPosition = CGPointMake(viewBounds.size.width/2.0, viewBounds.size.height);
    fireworksEmitter.emitterSize = CGSizeMake(viewBounds.size.width/2.0, 0.0);
    fireworksEmitter.emitterMode = kCAEmitterLayerOutline;
    fireworksEmitter.emitterShape = kCAEmitterLayerLine;
    fireworksEmitter.renderMode = kCAEmitterLayerAdditive;
    fireworksEmitter.seed = (arc4random()%100)+1;

    // Create the rocket
    CAEmitterCell *rocket = [CAEmitterCell emitterCell];

    rocket.birthRate = 1.0;
    rocket.emissionRange = 0.25 * M_PI;  // some variation in angle
    rocket.velocity = 380;
    rocket.velocityRange = 100;
    rocket.yAcceleration = 75;
    rocket.lifetime = 1.02;    // we cannot set the birthrate < 1.0 for the burst

    //小圆球图片
    rocket.contents = (id)[[UIImage imageNamed:@"DazRing"] CGImage];
    rocket.scale = 0.2;
    rocket.color = [[UIColor redColor] CGColor];
    rocket.greenRange = 1.0;        // different colors
    rocket.redRange = 1.0;
    rocket.blueRange = 1.0;
    rocket.spinRange = M_PI;        // slow spin



    // the burst object cannot be seen, but will spawn the sparks
    // we change the color here, since the sparks inherit its value
    CAEmitterCell *burst = [CAEmitterCell emitterCell];

    burst.birthRate = 1.0;        // at the end of travel
    burst.velocity = 0;        //速度为0
    burst.scale = 2.5;      //大小
    burst.redSpeed =-1.5;        // shifting
    burst.blueSpeed =+1.5;        // shifting
    burst.greenSpeed =+1.0;        // shifting
    burst.lifetime = 0.35;     //存在时间

    // and finally, the sparks
    CAEmitterCell *spark = [CAEmitterCell emitterCell];

    spark.birthRate = 400;
    spark.velocity = 125;
    spark.emissionRange = 2* M_PI;    // 360 度
    spark.yAcceleration = 75;        // gravity
    spark.lifetime = 3;
    //星星图片
    spark.contents = (id)[[UIImage imageNamed:@"DazStarOutline"] CGImage];
    spark.scaleSpeed = -0.2;
    spark.greenSpeed = -0.1;
    spark.redSpeed = 0.4;
    spark.blueSpeed =-0.1;
    spark.alphaSpeed =-0.25;
    spark.spin = 2* M_PI;
    spark.spinRange = 2* M_PI;

    // 3种粒子组合,可以根据顺序,依次烟花弹-烟花弹粒子爆炸-爆炸散开粒子
    fireworksEmitter.emitterCells = [NSArray arrayWithObject:rocket];
    rocket.emitterCells = [NSArray arrayWithObject:burst];
    burst.emitterCells = [NSArray arrayWithObject:spark];
    [self.view.layer addSublayer:fireworksEmitter];
}

粒子特效的叠加

通过上面的代码和注释、以及苹果的注释,我相信大部分人都已掌握了粒子效果的基本使用,下面我们来谈谈粒子特效叠加的问题。

在CAEmitterCell.h当中我们可以看到这样一段描述

/* An array containing the sub-cells of this cell, or nil (the default
 * value). When non-nil each particle emitted by the cell will act as
 * an emitter for each of the cell's sub-cells. The emission point is
 * the current particle position and the emission angle is relative to
 * the current direction of the particle. Animatable. */

@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells;

这段描述说明了当设置粒子单元(CAEmitterCell *)的emitterCells时,每个粒子会充当一个发射器的角色,发射对应粒子,形成粒子特效的叠加。

rocket.emitterCells = [NSArray arrayWithObject:burst];修改为rocket.emitterCells = [NSArray arrayWithObject: spark];就可以看到如下图对应的叠加效果了。

粒子特效叠加

取消叠加

看了粒子特效的叠加,你可能还会有这样一个想法,我能不能取消某个时间段的叠加,只让发射的粒子在最后的时间作为发射器产生粒子效果呢,类似于发射一颗礼炮,在礼炮到达空中时爆炸,而不是像现在这样一路炸上天呢。

取消叠加

实例代码的fireworks部分就实现了这样的效果,下面我们来分析一下他的思路

fireworks部分的基本思路是这样的,给发射的粒子A附加一个新的粒子效果B,B的效果是在A生命周期的结尾发射一个生命周期极短的粒子,在B粒子极短的生命周期内,发射爆炸效果的粒子C,这样看起来的效果就是A被发射出去,在其生命周期的结尾发射了一个爆炸的粒子C,用户并没有意识到B粒子的存在,从而达到取消叠加的效果。

如何在代码中实现这种取消叠加的效果

birthRate粒子每秒钟产生粒子的个数,决定了粒子产生的间隔,而且并不是一开始就发射粒子,而是要从开始过相应的时间间隔才发送粒子。所以我们只需要将B粒子的生成间隔设定为A粒子的生存长度即可,这样就实现了在A粒子即将结束的时候发射B粒子。

fireworks部分的代码也可以这样表示

burst.birthRate = 1.0/rocket.lifetime;

取消叠加的坑

你做了取消叠加部分的操作,发现A粒子生命周期结束时并没有出现爆炸效果的粒子C,这个时候问题出现在哪里呢?

原因就是刚好在A生命周期的结尾要发射单个粒子B的时候,A died,然后B就没发射出来……

这个时候你需要将A的生命周期比原来加长一点,要比B的生成间隔时间长那么一小点,代码实现上为:

CGFloat rocketLifeTime = 1.0f;
rocket.lifetime = rocketLifeTime + 0.01f;
…
burst.birthRate = 1.0f/rocketLifeTime;

或者

burst.birthRate = 1.0f/(rocket.lifetime-0.01f);

补充:粒子属性的继承

如果发射器是粒子的话,它的相应属性,会继承给它发射出的子粒子。比如说粒子A设置了redSpeed属性,粒子B redSpeed的默认值会和A相同,除非B自己设置一个redSpeed的数值。


参考博文:

iOS粒子效果

CAEmitterLayer(粒子系统)学习笔记