iOS中粒子发射(封装下雨,下雪,喷火等特效)

在iOS中实现粒子特效,有对应的的属性CAEmitterLayer,它是Layer的子类,用它来实现粒子特效不会怎么占用资源,从而去影响UI的流程性。
粒子CAEmitterLayer发射器的基本属性

//是否允许在规定的范围内
@property BOOL masksToBounds;

//发射器的形状
//kCAEmitterLayerPoint 点的形状,粒子从一个点发出
//kCAEmitterLayerLine 线的形状,粒子从一条线发出
//kCAEmitterLayerRectangle 矩形形状,粒子从一个矩形中发出
//kCAEmitterLayerCuboid 立方体形状,会影响z平面的效果
//kCAEmitterLayerCircle 圆形,粒子会在圆形范围发射
//kCAEmitterLayerSphere 球形
@property(copy) NSString *emitterShape;

//发射器的发射模式
//kCAEmitterLayerPoints 从发射器中发出
//kCAEmitterLayerOutline 从发射器边缘发出
//kCAEmitterLayerSurface 从发射器表面发出
//kCAEmitterLayerVolumen 从发射器中点发出
@property(copy) NSString *emitterMode;

// 发射器的尺寸大小
@property CGSize emitterSize;

//发射器在xy平面的中心位置
@property CGPoint emitterPosition;

//发射器在Z平面的位置
@property CGFloat emitterZPosition;

粒子的属性,也就是特效,需要用到CAEmitterCell这个类

//类方法创建发射单元
+ (instancetype)emitterCell;

//粒子的创建速率
@property float birthRate;

//粒子的生存时间
@property float lifetime;

//粒子的生存时间容差
@property float lifetimeRange;

//粒子在Z轴方向的发射角度
@property CGFloat emissionLatitude;

//粒子在xy平面的发射角度
@property CGFloat emissionLongitude;

//粒子发射角度的容差
@property CGFloat emissionRange;

//粒子的速度
@property CGFloat velocity;

//粒子速度的容差
@property CGFloat velocityRange;

//x,y,z三个方向的加速度
@property CGFloat xAcceleration;
@property CGFloat yAcceleration;
@property CGFloat zAcceleration;

//缩放大小,缩放容差和缩放速度
@property CGFloat scale;
@property CGFloat scaleRange;
@property CGFloat scaleSpeed;

//旋转度与旋转容差
@property CGFloat spin;
@property CGFloat spinRange;

//
@property CGColorRef color;

//粒子在rgb三个色相上的容差和透明度的容差
@property float redRange;
@property float greenRange;
@property float blueRange;
@property float alphaRange;

//粒子在RGB三个色相上的变化速度和透明度的变化速度
@property float redSpeed;
@property float greenSpeed;
@property float blueSpeed;
@property float alphaSpeed;

//渲染粒子,可以设置为一个CGImage的对象
@property(strong) id contents;

//渲染的范围
@property CGRect contentsRect;

好,接下来我们看一下他的基本用法。

    //创建出layer
    CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];//遍历构造器创建
    //给定尺寸
    emitterLayer.frame = CGRectMake(100, 100, 100, 100);
    //显示边框
    emitterLayer.borderWidth = 1.f;
    //发射点
    emitterLayer.emitterPosition = CGPointMake(0, 0);
    
//    emitterLayer.masksToBounds = YES;
    
    //发射模式
    emitterLayer.emitterMode = kCAEmitterLayerSurface;
    //发射形状
    emitterLayer.emitterShape = kCAEmitterLayerLine;
    
    [self.view.layer addSublayer:emitterLayer];
    
    CAEmitterCell *cell = [CAEmitterCell emitterCell];
    //粒子产生率
    cell.birthRate = 1.f;
    //粒子生命周期
    cell.lifetime = 120.f;
    //速度值
    cell.velocity = 10;
    //速度值的微调值
    cell.velocityRange = 3.f;
    //y轴加速度
    cell.yAcceleration = 2.f;
    //发射角度
    cell.emissionRange = 0.5 *M_1_PI;
    //设置粒子颜色
//    cell.color = [UIColor blueColor].CGColor;
    
    cell.contents = (__bridge id _Nullable)([UIImage imageNamed:@"234"].CGImage);
    //让CAEmitterCell与CAEmitterLayer产生关系
    emitterLayer.emitterCells = @[cell];

然后运行后得到的效果如下:

caotu.gif

CAEmitterLayer基本的使用也就这样,但为了避免繁琐的设置,我们将CAEmitterLayer封装成一个较为通用的父类供子类使用。

首先创建一个继承UIView的视图CAEmitterLayerView,由于此view默认创建的隐式layer不是CAEmitterLayer,所以我们需要在CAEmitterLayerView.m中重写layerClass方法,返回CAEmitterLayer。由于CAEmitterLayer含有非常多的属性要设置,如果都写在CAEmitterLayerView.h中的话,会显得非常的繁琐,所以在这里我们写set和get方法,通过这个方法,当我们需要使用哪个属性的时候,再在子类中进行调用即可。CAEmitterLayerView的代码如下:
CAEmitterLayerView.h

@interface CAEmitterLayerView : UIView
//模仿setter,getter方法
- (void)setEmitterLayer:(CAEmitterLayer *)layer;
- (CAEmitterLayer *)emitterLayer;

//显示出当前view
- (void)show;
//隐藏
- (void)hide;
@end

CAEmitterLayerView.m

@interface CAEmitterLayerView() {
    CAEmitterLayer *_emitterLayer;
}

@end

@implementation CAEmitterLayerView
+(Class)layerClass {
    return [CAEmitterLayer class];
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        _emitterLayer = (CAEmitterLayer *)self.layer;
    }
    return self;
}

- (void)setEmitterLayer:(CAEmitterLayer *)layer {
    _emitterLayer = layer;
}

- (CAEmitterLayer *)emitterLayer {
    return _emitterLayer;
}

- (void)show {

}

- (void)hide {

}

好了,通过这种方法,我们在项目中倘若要设计下雨或者下雪等效果时候,只需要继承该CAEmitterLayerView,然后在子类中实现对应的效果即可。

1.下雪效果
首先创建一个CAEmitterLayerView的子类视图SnowView,在.m中的代码如下

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup {
 //初始化一些参数
    self.emitterLayer.masksToBounds = YES;
    self.emitterLayer.emitterShape = kCAEmitterLayerLine;
    self.emitterLayer.emitterMode = kCAEmitterLayerSurface;
    self.emitterLayer.emitterSize = self.frame.size;
    self.emitterLayer.emitterPosition = CGPointMake(self.bounds.size.width / 2.f, -20);
}

- (void)show {
   //配置
    CAEmitterCell *snowFlake = [CAEmitterCell emitterCell];
    snowFlake.birthRate = 1.f;
    snowFlake.speed = 10.f;
    snowFlake.velocity = 2.f;
    snowFlake.velocityRange = 10.f;
    snowFlake.yAcceleration = 10.f;
    snowFlake.emissionRange = 0.5 * M_PI;
    snowFlake.spinRange = 0.25 * M_PI;
    snowFlake.contents = (__bridge id _Nullable)([UIImage imageNamed:@"234"].CGImage);
    snowFlake.color = [UIColor redColor].CGColor;
    snowFlake.lifetime = 180.f;
    snowFlake.scale = 0.5;
    snowFlake.scaleRange = 0.3;
    
    //添加动画
    self.emitterLayer.emitterCells = @[snowFlake];
}

下雪模型已搭建好,然后在我们的控制器中导入SnowView

    SnowView *sView = [[SnowView alloc] initWithFrame:CGRectMake(100, 400, 100, 100)];
    [self.view addSubview:sView];
    [sView show];

效果如下:

snow.gif

2.下雨效果
创建一个CAEmitterLayerView的子类视图RainView,在.m中的代码如下

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup {
    self.emitterLayer.masksToBounds = YES;
    self.emitterLayer.emitterShape = kCAEmitterLayerLine;
    self.emitterLayer.emitterMode = kCAEmitterLayerSurface;
    self.emitterLayer.emitterSize = self.frame.size;
    self.emitterLayer.emitterPosition
    = CGPointMake(self.bounds.size.width / 2.f, -20);
}

- (void)show {
    CAEmitterCell *rainFlake = [CAEmitterCell emitterCell];
    rainFlake.birthRate = 25.f;
    rainFlake.speed = 10.f;
    rainFlake.velocity = 10.f;
    rainFlake.velocityRange = 10.f;
    rainFlake.yAcceleration = 1000.f;
    rainFlake.contents = (__bridge id _Nullable)([UIImage imageNamed:@"234"].CGImage);
    rainFlake.color = [UIColor blackColor].CGColor;
    rainFlake.lifetime = 7.f;
    rainFlake.scaleRange = 0.f;
    rainFlake.scale = 0.2f;
    //添加动画
    self.emitterLayer.emitterCells = @[rainFlake];
}

效果如下(图片可以切长点,我在这里没改图片,看着效果并没那么好):

rain.gif

3.火焰
创建一个CAEmitterLayerView的子类视图fireView,在这里设置了两个CAEmitterCell(特效),在.m中的代码如下

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup {
    self.emitterLayer.masksToBounds = YES;
    self.emitterLayer.renderMode = kCAEmitterLayerAdditive;
    self.emitterLayer.emitterSize = CGSizeMake(self.bounds.size.width - 100, 20);
    self.emitterLayer.emitterPosition
    = CGPointMake(self.bounds.size.width / 2.f, self.bounds.size.height - 20);
}

- (void)show {
    //火焰
  CAEmitterCell * fire = [CAEmitterCell emitterCell];
    fire.birthRate=800;
    fire.lifetime=2.0;
    fire.lifetimeRange=1.5;
    fire.color=[[UIColor colorWithRed:0.8 green:0.4 blue:0.2 alpha:0.1]CGColor];
    fire.contents=(id)[[UIImage imageNamed:@"fire"]CGImage];
    
    fire.velocity=160;
    fire.velocityRange=80;
    fire.emissionLongitude=M_PI+M_PI_2;
    fire.emissionRange=M_PI_2;
    
    
    
    fire.scaleSpeed=0.3;
    fire.spin=0.2;
    
    //烟雾
    CAEmitterCell * smoke = [CAEmitterCell emitterCell];
    smoke.birthRate=400;
    smoke.lifetime=3.0;
    smoke.lifetimeRange=1.5;
    smoke.color=[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.05]CGColor];
    smoke.contents=(id)[[UIImage imageNamed:@"smoke"]CGImage];
    
    smoke.velocity=250;
    smoke.velocityRange=100;
    smoke.emissionLongitude=M_PI+M_PI_2;
    smoke.emissionRange=M_PI_2;
    
    self.emitterLayer.emitterCells = [NSArray arrayWithObjects:smoke,fire, nil];
}

效果如下:

fire.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • >复杂的组织都是专门化的 >Catharine R. Stimpson 到目前为止,我们已经探讨过`CALayer...
    夜空下最亮的亮点阅读 995评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,429评论 25 707
  • 最近 除了瑜伽的练习,其他基本处于放松的状态。我在观察着,思考着。是不是我的世界里仅留给正能量这个价值观-这个社会...
    东方沐雪_09ed阅读 226评论 0 0
  • 前言 通过安装ag和ack.vim,给vim提供全局搜索代码的功能 安装以及配置 安装Ag 装好之后,可以直接在s...
    坏坏的大猫猫阅读 3,007评论 0 1
  • 骄阳在头顶笑 麦穗在田里笑 我在风中笑 只有远处的乌鸦哀叫 风中的笑刺痛他的哀叫 他不知道 我只剩微笑
    马流云阅读 324评论 0 2