美文网首页iOS之绘图动画动画iOS Developer
CoreAnimation - CAEmitterLayer粒子

CoreAnimation - CAEmitterLayer粒子

作者: _誌念 | 来源:发表于2017-09-16 16:38 被阅读116次

    简介

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

    一、CAEmitterLayer属性介绍

    CAEmitterLayer并不是杂乱无章地发射粒子的,它发射粒子时的位置,发射的面积和面积对应的几何图形都是可以配置的,可以是一个点,或者一个方形、圆形等等。

    //emitterPosition; 发射形状的中心点,默认是(0,0,0)
    @property CGPoint emitterPosition;
    @property CGFloat emitterZPosition;
    
    //发射形状的大小,默认(0,0,0)依赖于`emitterShape'属性
    @property CGSize emitterSize;
    @property CGFloat emitterDepth;
    
    //发射形状, 默认 kCAEmitterLayerPoint
    @property(copy) NSString *emitterShape;
    
    //发射模式, 默认kCAEmitterLayerVolume
    @property(copy) NSString *emitterMode;
    
    //渲染模式(粒子图片是如何混合的), 默认kCAEmitterLayerUnordered
    @property(copy) NSString *renderMode;
    
    1. emitterShape

    为什么叫做“发射形状”呢? 看看emitterShape的枚举,你就会大概能明白

     kCAEmitterLayerPoint      //点
     kCAEmitterLayerLine       //线段
     kCAEmitterLayerRectangle  //矩形
     kCAEmitterLayerCuboid     //3D矩形
     kCAEmitterLayerCircle     //圆形,半径是emitterSize.width
     kCAEmitterLayerSphere     //球形,半径是emitterSize.width
    

    我们可以这样理解,emitterPosition是所选emitterShape的中心点,例如对于矩形是对角线交点,对于圆形是圆心,对于直线是中点。而emitterSize则决定了矩形的大小,圆形的大小,直线的长度(直线默认是没有高度的)。

    2. emitterMode

    emitterMode的作用是进一步决定发射的区域是在发射形状的哪一部份。

    NSString * const kCAEmitterLayerPoints;      // 顶点
    NSString * const kCAEmitterLayerOutline;     // 轮廓,即边上
    NSString * const kCAEmitterLayerSurface;     // 表面,即图形的面积内
    NSString * const kCAEmitterLayerVolume;      // 容积,即3D图形的体积内
    

    当我们选择Points的时候,粒子会从发射形状的“顶点”发射出来,这里顶点只是一个简单的描述,有些图形不能用顶点来描述的,例如对于圆形来说,“顶点”就是圆心。Outline是指从形状的边界上发射,surface则是从形状的表面上发射,Voloume是相对于3D形状的“球体内”或“立方体内”发射

    3.renderMode

    renderMode控制着在视觉上粒子图片是如何混合的

      NSString * const kCAEmitterLayerUnordered      //无序的
      NSString * const kCAEmitterLayerOldestFirst    
      NSString * const kCAEmitterLayerOldestLast
      NSString * const kCAEmitterLayerBackToFront     //覆盖
      NSString * const kCAEmitterLayerAdditive        //叠加
    

    二、CAEmitterCell属性介绍

    因为CAEmitterCell的属性比较多,我用表格的形式列举出来:

    属性 作用
    name 粒子名称默认是nil
    birthRate 粒子的产生速率/秒,默认是0
    lifetime , lifetimeRange 粒子的生命周期平均值和生命周期的范围,以秒为单位。两者默认0
    velocity ,velocityRange 每一个粒子的初始速度平均值和变化范围,默认值都是0
    xAcceleration ,yAcceleration ,zAcceleration x,y,z方向上的加速度分量,三者默认都是0
    scale, scaleRange ,scaleSpeed 缩放比例 :默认是1,缩放比例范围 :默认是0,缩放速度:默认是0
    spin,spinRange 粒子的自转是以弧度制来计算的,表示每秒钟粒子自转的弧度数。
    emissionRange emissionRange则决定了粒子的发散范围,同样是一个弧度值,默认是0
    emissionLongitude emissionLongtitude决定了粒子飞行方向跟水平坐标轴(x轴)之间的夹角,默认是0,即沿着x轴向右飞行
    emissionLatitude 纬度角代表了x-z轴平面上与x轴之间的夹角
    color 粒子的颜色,默认白色
    redRange,greenRange,blueRange,alphaRange 粒子颜色blue,red,green,alpha能改变的范围,默认0,它的取值范围也是0~1,颜色值不能为负数。
    redSpeed,greenSpeed,blueSpeed,alphaSpeed 表示对应的颜色component的变化速度,它的取值范围也是0~1
    contents 粒子的内容,大多都是个CGImageRef的对象,既粒子要展现的图片,默认nil
    emitterCells

    CAEmitterCell的emitterCells跟CAEmitterLayer的一样,也是一个CAEmitterCell的队列。我们基本可以按照操作CAEmitterLayer的emitterCells一样来设置我们粒子的emitterCells。
    为了方便描述,我们将从粒子上发射出来的粒子称作称作subCell

    1. CAEmitterCell也是服从CAMediatiming协议的,我们通过控制subCell的beginTime来控制subCell的出现时机。subCell的beginTime是不能大于你的粒子的lifetime的

    2. 如果你想控制subCell的发射方向,那么需要考虑到父粒子的emissionLongtitude的情况。无论粒子是从什么样的形状上发射出来的,当它要发射subCell的时候,subCell总是从kCAEmitterLayerPoint形状上由父粒子的中心发射出来的。父粒子的发射方向是subCell的emissionLongtitude为0时的飞行方向

    三、CAEmitterLayer动画实例

    1. 粒子发射动画

    直接看下面的代码:

    - (void)addStarEmitter{
        self.view.backgroundColor = [UIColor blackColor];
        //创建粒子发射器
        _emitterLayer = [CAEmitterLayer layer];
        _emitterLayer.masksToBounds = YES;
        _emitterLayer.frame = self.view.bounds;
        [self.view.layer addSublayer:_emitterLayer];
        
        //设置CAEmitterLayer
        _emitterLayer.renderMode = kCAEmitterLayerAdditive;
        _emitterLayer.emitterPosition = CGPointMake(CGRectGetMidX(self.view.bounds)+50, 150);
        
        //创建粒子模板
        CAEmitterCell *emitterCell = [[CAEmitterCell alloc]init];
        emitterCell.contents = (__bridge id)[UIImage imageNamed:@"star"].CGImage;
        emitterCell.contentsScale = [UIScreen mainScreen].scale;
        emitterCell.color = [UIColor colorWithRed:0 green:0 blue:0 alpha:1].CGColor;
        emitterCell.redRange = 1.0;
        emitterCell.greenRange = 1.0;
        emitterCell.blueRange = 1.0;
        emitterCell.alphaRange = 0;
        emitterCell.redSpeed = 0;
        emitterCell.greenSpeed = 0;
        emitterCell.blueSpeed = 0;
        emitterCell.alphaSpeed = -0.5;
        
        emitterCell.scale = 1;
        emitterCell.scaleRange = 0.2;
        emitterCell.scaleSpeed = 0.1;
        
        emitterCell.spin = 130*M_PI/180.0;;
        emitterCell.spinRange = 0;
        
        emitterCell.emissionLatitude = 0;
        emitterCell.emissionLongitude = 0;
        emitterCell.emissionRange = M_PI *2;
        
        emitterCell.lifetime = 1;
        emitterCell.lifetimeRange = 0;
        emitterCell.birthRate = 200;
        emitterCell.velocity = 50;
        emitterCell.velocityRange = 500;
        emitterCell.xAcceleration = -750;
        emitterCell.yAcceleration = 0;
        
        //将粒子添加到发射器上
        _emitterLayer.emitterCells = @[emitterCell];
    }
    
    2. 点赞动画

    首先我们来分析一下这个动画,共有两部分:
    1.点赞按钮的Scale变化
    2.可用控制开始和结束的CAEmitterLayer动画

    - (void)setUp{
        _emitterLayer = [CAEmitterLayer layer];
        _emitterLayer.name = @"emitterLayer";
        //粒子发射器的形状
        _emitterLayer.emitterShape = kCAEmitterLayerCircle;
        //从轮廓进行发射
        _emitterLayer.emitterMode = kCAEmitterLayerOutline;
        _emitterLayer.renderMode = kCAEmitterLayerOldestFirst;
        _emitterLayer.emitterPosition = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
        _emitterLayer.emitterSize = CGSizeMake(25, 0);
        _emitterLayer.masksToBounds = NO;
        
        CAEmitterCell *cell = [[CAEmitterCell alloc]init];
        cell.contents = (__bridge id)[UIImage imageNamed:@"Sparkle"].CGImage;
        cell.name = @"explosion";
        cell.alphaRange = 0.2;
        cell.alphaSpeed = -1.0;
        cell.lifetime = 0.7;
        cell.lifetimeRange = 0.3;
        cell.birthRate = 0;
        cell.velocity = 40;
        cell.velocityRange = 10;
        //如果你的contents的图片太大了,可以通过设置sacle属性缩小
        cell.scale = 0.05;
        cell.scaleRange = 0.02;
    
        _emitterLayer.emitterCells = @[cell];
        [self.layer addSublayer:_emitterLayer];
    }
    

    接下来我们需要手动控制动画的开始和结束。还记得前面提到的 @property(copy) NSString *name; 吗?想要手动控制动画的开始和结 束,我们必须通过 KVC 的方式设置 cell 的值才行。
    [self.emitterLayer setValue:@500 forKeyPath:@"emitterCells.explosion.birthRate"];这句话的含义:CAEmitterLayer根据自己的"emitterCells"属性找到名叫"explosion"的cell,并设置他的birthRate = 500,从而间接控制了动画的开始。

    - (void)startExplosion{
        self.emitterLayer.beginTime = CACurrentMediaTime();
        [self.emitterLayer setValue:@500 forKeyPath:@"emitterCells.explosion.birthRate"];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self stopExplosion];
        });
    }
    
    - (void)stopExplosion{
        //通过设置birthRate间接控制了动画的结束
        [self.emitterLayer setValue:@0 forKeyPath:@"emitterCells.explosion.birthRate"];
    }
    

    你可以查看MCFireworksButton开源库。

    3. 烟花爆炸效果

    我们分析可以看到动画分为两个阶段:
    1.粒子发射阶段。
    2.粒子爆炸阶段。(这里我们需要用到上面提到的CAEmitterCellemitterCells

    - (void)fireworkEmitter{
        CGFloat rocketLifeTime = 1.0;
        //创建粒子发射器
        CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
        emitterLayer.emitterPosition = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetHeight(self.view.bounds));
        emitterLayer.emitterSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 0);
        emitterLayer.emitterShape = kCAEmitterLayerLine;
        emitterLayer.emitterMode = kCAEmitterLayerOutline;
        emitterLayer.renderMode = kCAEmitterLayerAdditive;
        
        //创建粒子模板(发射阶段)
        CAEmitterCell *rocket = [CAEmitterCell emitterCell];
        rocket.contents = (__bridge id)[UIImage imageNamed:@"小圆球"].CGImage;
        rocket.color = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1].CGColor;
        rocket.scale = 0.5;
        rocket.redRange = 0.7;
        rocket.greenRange = 0.7;
        rocket.birthRate = 1;
        rocket.lifetime = rocketLifeTime + 0.02;
        rocket.velocity = 500;
        rocket.velocityRange = 100;
        rocket.yAcceleration = 75;
        rocket.emissionRange = M_PI_4;
        
        //创建粒子模板(爆炸阶段)
        CAEmitterCell *spark = [CAEmitterCell emitterCell];
        spark.birthRate = 10000;
        spark.scale = 0.6;
        spark.velocity = 125;
        spark.emissionRange = 2* M_PI;
        spark.yAcceleration = 75;
        spark.lifetime = 2;
        spark.contents = (id)[[UIImage imageNamed:@"星"] CGImage];
        spark.scaleSpeed = -0.2;
        spark.greenRange = 0.4;
        spark.redRange = 0.7;
        spark.blueRange = 0.9;
        spark.alphaSpeed =-0.25;
        spark.spin = 2* M_PI;
        spark.spinRange = M_PI;
    
        //beginTime要小于 rocket.lifetime
        spark.beginTime = rocketLifeTime;
        
        emitterLayer.emitterCells = @[rocket];
        rocket.emitterCells = @[spark];
        [self.view.layer addSublayer:emitterLayer];
    }
    

    完整的代码可以在这查看Demo地址

    相关文章

      网友评论

        本文标题:CoreAnimation - CAEmitterLayer粒子

        本文链接:https://www.haomeiwen.com/subject/rbuysxtx.html