在上一篇中我們大致了解了粒子系統(tǒng),在這篇中我們再深入解析一下拭抬。在粒子系統(tǒng)中,CAEmitterLayer負(fù)責(zé)發(fā)射粒子(當(dāng)然粒子也可以發(fā)射粒子)侵蒙,而這些所謂的粒子造虎,就是CAEmitterCell,我們可以將CAEmitterLayer比作是CAEmitterCell的工廠纷闺,它會按照你的設(shè)置來以不同的樣式不斷產(chǎn)生粒子算凿,也就是CAEmitterCell。
(1)CAEmitterLayer決定了粒子從什么樣的幾何特性上發(fā)射出來犁功,這個幾何特性包括了位置氓轰,形狀,大小浸卦。另外還有一些渲染相關(guān)的特性署鸡。另外的一些屬性是CAEmitterLayer和CAEmiiterCell都有的,CAEmitterLayer的這些屬性會作為CAEmitterCell相同屬性取值的系數(shù),例如當(dāng)CAEmitterCell的lifetime(生命周期)為1靴庆,其所屬CAEmitterLayer的lifetime為2時时捌,在其它參數(shù)選擇默認(rèn)值的情況下,這個CAEmitterCell的生命周期就是1*2=2秒炉抒,2秒后奢讨,CAEmitterCell就會從粒子系統(tǒng)中被移除。
(2)CAEmitterCell則決定了粒子自身的一些特征焰薄,例如速度拿诸,加速度,發(fā)射的范圍塞茅,顏色等等亩码。這些屬性大多是以“中間值”搭配一個“范圍”(A mean and a “cone”)的方式來表示的,例如velocity和velocityRange野瘦。表示CAEmitterCell的初始速度為velocity ± velocityRange蟀伸。
下面我們再深入了解下CAEmitterLayer和CAEmitterCell中的幾個常用的屬性。
一缅刽、CAEmitterLayer常用屬性
1啊掏、控制粒子發(fā)射位置和形狀的屬性
CAEmitterLayer并不是雜亂無章地發(fā)射粒子的,它發(fā)射粒子時的位置衰猛,發(fā)射的面積和面積對應(yīng)的幾何圖形都是可以配置的迟蜜,可以是一個點(diǎn),或者一個方形啡省、圓形等等
emitterPosition決定了粒子發(fā)射形狀的中心點(diǎn)娜睛,
emitterSize則決定了粒子發(fā)射形狀的大小,
emitterShape是粒子從什么形狀發(fā)射出來卦睹,它并不是表示粒子自己的形狀,
emitterMode 決定了粒子的發(fā)射模式畦戒。
(1)emitterShape
為什么叫做“發(fā)射形狀”呢? 看看emitterShape的枚舉结序,你就會大概能明白了
NSString * const kCAEmitterLayerPoint; // 點(diǎn)
NSString * const kCAEmitterLayerLine; // 直線
NSString * const kCAEmitterLayerRectangle; // 矩形
NSString * const kCAEmitterLayerCircle; // 圓形
NSString * const kCAEmitterLayerCuboid; // 3D rectangle
NSString * const kCAEmitterLayerSphere; // 3D circle
我們用kCAEmitterLayerLine來說明一下障斋。當(dāng)你的CAEmitterLayer的emitterSize為CGSize(10, 10)時徐鹤,你的所選擇的emitterPosition為CGPoint(10,10)垃环。那么形狀為“Line”的CAEmitterLayer就會在如下圖紫色的直線上產(chǎn)生粒子,對于“Line”來說返敬,emitterSize的高度是被忽略的遂庄。
我們可以這樣理解,emitterPosition是所選emitterShape的中心點(diǎn)劲赠,例如對于矩形是對角線交點(diǎn)涛目,對于圓形是圓心秸谢,對于直線是中點(diǎn)。而emitterSize則決定了矩形的大小霹肝,圓形的大小钮追,直線的長度。這樣說應(yīng)該就夠通俗易懂了阿迈。
另外,我們可以將emitterCell的速度相關(guān)的屬性全部設(shè)置為0轧叽,你也可以直接注釋掉他們苗沧,采用默認(rèn)值,這樣我們將會得到一些不會移動的粒子炭晒,因?yàn)樗鼈儧]有速度待逞,這樣我們能看清楚CAEmitterLayer的形狀是什么
kCAEmitterLayerRectangle:
kCAEmitterLayerLine:
kCAEmitterLayerPoint:
kCAEmitterLayerCircle:
(2)emitterMode
emitterMode的作用是進(jìn)一步?jīng)Q定發(fā)射的區(qū)域是在發(fā)射形狀的哪一部份,當(dāng)我們看到它的枚舉時网严,也就能大概了解了识樱。
NSString * const kCAEmitterLayerPoints; // 頂點(diǎn)
NSString * const kCAEmitterLayerOutline; // 輪廓,即邊上
NSString * const kCAEmitterLayerSurface; // 表面震束,即圖形的面積內(nèi)
NSString * const kCAEmitterLayerVolume; // 容積怜庸,即3D圖形的體積內(nèi)
當(dāng)我們選擇Points的時候,粒子會從發(fā)射形狀的“頂點(diǎn)”發(fā)射出來垢村,這里頂點(diǎn)只是一個簡單的描述割疾,有些圖形不能用頂點(diǎn)來描述的,例如對于圓形來說,“頂點(diǎn)”就是圓心嘉栓。Outline是指從形狀的邊界上發(fā)射宏榕,surface則是從形狀的表面上發(fā)射,Voloume是相對于3D形狀的“球體內(nèi)”或“立方體內(nèi)”發(fā)射侵佃,關(guān)于3D形狀的問題麻昼,如果稍后有時間我再補(bǔ)吧,因?yàn)楣P者現(xiàn)在還不知道怎么實(shí)現(xiàn)3D的粒子系統(tǒng)馋辈。
現(xiàn)在我們利用同樣的方法看下emitterMode的樣式是什么樣的(我門保持發(fā)射形狀為kCAEmitterLayerRectangle):
kCAEmitterLayerPoints:
kCAEmitterLayerOutline:
kCAEmitterLayerSurface:
CAEmitterLayer的上述屬性抚芦,共同決定了你的粒子會在什么樣的位置上被均勻地發(fā)射出來。 還有一些屬性大多是跟CAEmitterCell同名的屬性迈螟,它們會作為CAEmitterCell同名屬性的“倍數(shù)”燕垃。
二、CAEmitterCell常用屬性
CAEmitterLayer決定了粒子系統(tǒng)的基調(diào)井联,致于怎么讓粒子系統(tǒng)酷炫起來卜壕,就需要CAEmitterCell的幫助。
(1)lifetime
粒子在系統(tǒng)上存在的時間烙常,單位是秒轴捎。它可以配合lifetimeRage來讓粒子生命周期均勻變化鹤盒,以便可以讓粒子的出現(xiàn)和消失顯得更加離散。
(2)birthRate
粒子產(chǎn)生數(shù)量的決定參數(shù),它表示CAEmitterLayer上每秒產(chǎn)生的粒子數(shù)量侦副,birthRate是一個浮點(diǎn)數(shù)侦锯,大家可以靈活一點(diǎn)使用它,例如出于某些需要(測試需要)秦驯,你想讓你的粒子在屏幕上停留久一點(diǎn)以便可以觀察它尺碰,一方面你可以讓你的粒子速度相關(guān)的屬性都為0,讓它不會移動译隘。另外設(shè)置lifetime讓你的粒子存在時間變長亲桥,例如10秒,那么你只要將birthRate設(shè)置成0.1固耘,就可以在10秒內(nèi)觀察同一個粒子题篷,并且不會有其它的粒子來影響你的觀察,10秒后厅目,新的粒子出現(xiàn)番枚,舊粒子被移除,你可以從頭在觀察它的變化损敷。這樣就方便很多葫笼。
下面介紹一下CAEmitterCell關(guān)于顏色控制的屬性,這部分屬性讓你獲得了控制粒子顏色拗馒,顏色變化范圍和速度的能力渔欢,你可以憑借它來完成一些漸變的效果或其它構(gòu)建在它之上的酷炫效果。不過在認(rèn)識CAEmitterCell的顏色屬性之前瘟忱,我們有必要先了解它的contents屬性:
(3)contents
contents其實(shí)我們在使用CALayer時就已經(jīng)十分熟悉了奥额,當(dāng)我們想要一個CALayer展示一張靜態(tài)圖片時,就會使用到這個屬性访诱。在CAEmitterCell上它的意義也是一樣的垫挨。不過由于下面4)提到的color屬性的特點(diǎn),我們一般都會將contents設(shè)置成一張純色的圖片触菜,尤其是白色九榔,看完4)的描述,你就明白了為什么這樣會給我們帶來方便了涡相。
(4)color
color 會結(jié)合contents內(nèi)容的顏色來改變我們的CAEmitterCell哲泊。它的結(jié)合算法其實(shí)是相當(dāng)簡單的,我們通過使用顏色來創(chuàng)建圖像的方法來觀察它跟contents的結(jié)合方式催蝗。下面的代碼簡單展示了如何用UIColor來創(chuàng)建UIImage切威。
-(UIImage*)imageWithColor:(UIColor*)color andSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGRect rect=CGRectMake(0, 0, size.width, size.height);
UIBezierPath*bezierPath=[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:size.width/2.0];
CGContextAddPath(context, bezierPath.CGPath);
CGContextFillPath(context);
CGContextSetFillColorWithColor(context, color.CGColor);
UIImage*theImage=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return theImage;
}
假設(shè)我們的contents通過上面的接口傳入 [UIColor colorWithRed:1 green:1 blue:1 alpha:1]來創(chuàng)建,并且我們cell的color傳入[UIColor colorWithRed:0.5 green:1 blue:1 alpha:1]丙号。那么在其它屬性都為默認(rèn)值的條件下先朦,我們得到的CAEmitterCell的顏色是[UIColor colorWithRed:0.5 green:1 blue:1 alpha:1]缰冤,如果用數(shù)碼測色計(jì)(在luanchPad的小工具集合里)來測色,會發(fā)現(xiàn)顏色值為rgb(128,255,255)喳魏。這是因?yàn)檫@兩個值的結(jié)合算法就是兩個顏色對應(yīng)component的乘積棉浸。例子中的紅色component的計(jì)算方式就是 0.51 = 0.5,換成rgb值的話刺彩,就是2550.51 = 127.5迷郑,再向上取整得到128。如果你將contents的紅色component從1改為0.75创倔,那么就可以知道紅色component是2550.5*0.75 = 95.62录豺,那么最終的顏色就會是rgb(96,255,255)瀑志。以此類推帆啃。不知道這樣說得是否清楚艘蹋。出于上面的原因牧挣,我們想完全通過color來控制CAEmitterCell的顏色往堡,那么最好就選用一張顏色值為(255,255,255)瓷产,即白色的圖片作為CAEmitterCell的contents涌矢。因?yàn)榇蠹叶贾涝赨IColor中摆出,rgb值為255的component的值其實(shí)為1朗徊,這就是為什么我們是總是用這樣的語句來表達(dá)值為rgb(x,y,z)的UIColor的原因:
[UIColor colorWithRed:x/255.f green:y/255.f blue:z/255.f alpha:1];
那么通過上面的算式我們知道,最后計(jì)算出來的結(jié)果一定跟color中的顏色是一致的偎漫,因?yàn)榘咨腢IColor每個component都為1爷恳,CAEmitterCell的color中任意component乘以contents中顏色對應(yīng)的component都會得到color上原來的值,乘以1不改變值嘛象踊。
5温亲、redRange,greenRange杯矩,blueRange
是粒子對應(yīng)顏色的component的初始取值范圍栈虚。例如redRange為0.1,那么當(dāng)你的粒子的color對應(yīng)的rgb為(10史隆,255魂务,255)。那么在其它值取默認(rèn)值的前提下泌射,這個粒子在CAEmitterLayer上發(fā)射出來的時候粘姜,它的rgb中的red component會均勻分布在 10正負(fù)0.1*255之間,即[0熔酷,35]孤紧,顏色值不能為負(fù)。
6拒秘、redSpeed坛芽,greenSpeed留储,blueSpeed
表示對應(yīng)的顏色component的變化速度,它的取值范圍也是0~1咙轩。表示每秒鐘的顏色變化率获讳,這樣說可能不太容易理解。我們拿一些數(shù)字來舉例就很容易明白了活喊。例如你的粒子的顏色是rgb(0丐膝,255,255)钾菊,并且你的粒子的redRange也為0 帅矗,這表示你的粒子被發(fā)射出來的時候,它的顏色值中的紅色值總是為0 煞烫。你的粒子的生命周期lifetime為10浑此,即粒子可以存在10秒,10秒后會從layer上移除滞详。那么當(dāng)你的redSpeed取值為0.1時凛俱,你將會看到你的粒子的紅色值每秒增加0.1*255,這個過程是連續(xù)不斷變化料饥,外觀上看就是你的粒子越來越接近白色蒲犬。當(dāng)粒子到達(dá)生命結(jié)束的時候也就是10秒的時候,你的粒子的顏色也恰好變成了rgb(255,255,255)岸啡。
上述5和6這些屬性共同決定了粒子的顏色變化情況原叮。它們都是一個值在[0,1] 的浮點(diǎn)數(shù)。另外還有一對類似的屬性巡蘸,alphaRange和alphaSpeed奋隶,他們決定了顏色中的alpha通道的值,你可以通過它來讓你的粒子漸隱或漸現(xiàn)悦荒。
下面我們一起來了解一下決定粒子發(fā)射角度和發(fā)散范圍的若干屬性达布。從CAEmitterLayer的介紹中,我們知道了粒子會從CAEmitterLayer設(shè)置好的位置大小和形狀上發(fā)射出來逾冬,然而發(fā)射出來后黍聂,粒子往什么方向繼續(xù)飛行,則是由下面的幾個屬性共同決定的:
7身腻、emissionLongitude
emissionLongtitude決定了粒子飛行方向跟水平坐標(biāo)軸(x軸)之間的夾角产还,默認(rèn)是0,即沿著x軸向右飛行嘀趟。順時針方向是正向脐区,例如emissionLongtitude為0 ,則粒子順著x軸飛行她按,如果你想讓你的粒子發(fā)射出來后牛隅,沿著y軸向下飛行炕柔,那么emissionLongtitude就應(yīng)該設(shè)置為PI/2。即90度媒佣,正如我所說的那樣匕累,順時針方向是正方向。如果你想讓你的粒子沿著y軸向上飛行默伍,你可以將emissionLongtitude設(shè)置為 3*PI/2也可設(shè)置為 -PI/2欢嘿。下圖就簡單展示了emissionLongtitude的幾個典型取值,圖中綠色箭頭所指的方向就是當(dāng)emissionLongtitude為箭頭對應(yīng)角度時的粒子飛行方向:
8也糊、emissionRange
emissionRange則決定了粒子的發(fā)散范圍炼蹦,同樣是一個弧度值(radians),表示粒子在沿著emissionLongtitude方向所形成的頂角為2倍emissionRange的圓錐范圍內(nèi)發(fā)散狸剃。我們看例圖:我們把emisstionLongtitude設(shè)置為-PI/2掐隐,讓粒子向上飛行,并且讓emissionRange為PI/4钞馁。那么按照上面的說法虑省,我們應(yīng)該能得到一個向上的,并且頂角為2 * PI/4的圓錐指攒,結(jié)果應(yīng)該如下圖:
我門可以修改代碼檢驗(yàn)一下:
emitterLayer.emitterShape=kCAEmitterLayerPoint;
emitterLayer.emitterPosition=self.view.center;
cell0.birthRate=10000;
cell0.velocity=10;
cell0.lifetime=20;
cell0.emissionLongitude=-M_PI_2;
cell0.emissionRange=M_PI_4;
結(jié)果如圖:
9慷妙、 xAcceleration yAcceleration zAcceleration
這3個屬性分別定義了3個坐標(biāo)軸上的加速度僻焚,它們代表了不同坐標(biāo)軸方向上的每秒的速度增量允悦,當(dāng)xAcceleration為正數(shù)時,粒子每秒向x軸正方向加速虑啤,為負(fù)數(shù)時則向負(fù)方向即水平向左加速隙弛。當(dāng)yAcceleration為正數(shù)時,粒子向y軸的負(fù)方向加速狞山,也是就是向下加速全闷,否則向上加速。
10萍启、spin总珠,spinRange
粒子的自轉(zhuǎn)是以弧度制來計(jì)算的,表示每秒鐘粒子自轉(zhuǎn)的弧度數(shù)勘纯。例如你的粒子的生命周期就是10秒局服,那么你想讓你的粒子在10秒內(nèi)剛好自轉(zhuǎn)1周,我們假定spinRange為0驳遵,那么要達(dá)到你的要求淫奔,你的粒子的spin值就應(yīng)該為((PI/180)*360)/10,前面是將360度轉(zhuǎn)成弧度堤结,后面是將360度對應(yīng)的弧度平均分到10秒去唆迁,這樣就得到了每秒需要轉(zhuǎn)動的弧度數(shù)鸭丛。另外,當(dāng)spin為正數(shù)的時候唐责,粒子是順時針旋轉(zhuǎn)的鳞溉,為負(fù)數(shù)的話就是逆時針選轉(zhuǎn)了。 spinRange就不多說了妒蔚,跟其它range的意義和作用是一樣的穿挨。
每一個粒子都可以作為一個發(fā)射器來發(fā)射粒子,我們可以通過這個特點(diǎn)來實(shí)現(xiàn)一些遞進(jìn)的粒子效果肴盏,例如瀑布下面的水花科盛,太陽的周圍的散射的光線等
11、emitterCells
CAEmitterCell的emitterCells跟CAEmitterLayer的一樣菜皂,也是一個CAEmitterCell的隊(duì)列贞绵。我們基本可以按照操作CAEmitterLayer的emitterCells一樣來設(shè)置我們粒子的emitterCells。只是有幾點(diǎn)內(nèi)容是需要我們留意一下的恍飘。為了方便描述榨崩,我們將從粒子上發(fā)射出來的粒子稱作稱作subCell。
(1)章母、CAEmitterCell也是服從CAMediatiming協(xié)議的母蛛,我們通過控制subCell的beginTime來控制subCell的出現(xiàn)時機(jī)。當(dāng)你的subCell的beginTime為0時乳怎,表示你的粒子從CAEmitterLayer上發(fā)射出來后就會立即開始發(fā)射subCell彩郊,你需要適當(dāng)選取你的subCell的birthRate,因?yàn)槟阋莉阶海愕拿總€粒子都會每秒發(fā)射birthRate個subCell秫逝。subCell的beginTime是不能大于你的粒子的lifetime的,它其實(shí)代表了你的粒子從什么時候開始發(fā)射subCell嘛询枚,所以大于你的粒子的生命周期违帆,它就無法發(fā)射你的subCell了,因?yàn)槟愕牧W右呀?jīng)“死了”金蜀。
(2)另外刷后,值得一提的是,如果你想控制subCell的發(fā)射方向渊抄,那么需要考慮到父粒子的emissionLongtitude的情況尝胆。無論粒子是從什么樣的形狀上發(fā)射出來的,當(dāng)它要發(fā)射subCell的時候抒线,subCell總是從kCAEmitterLayerPoint形狀上由父粒子的中心發(fā)射出來的班巩。根據(jù)上面關(guān)于emissionLongtitude的描述, 當(dāng)它為0時,粒子水平向右飛行抱慌。然而subCell的為0時逊桦,subCell是沿著父粒子的發(fā)射方向飛行的,也就是說抑进,父粒子的發(fā)射方向是subCell的emissionLongtitude為0時的飛行方向强经。
CAEmitterCell*cell0=[CAEmitterCell emitterCell];
cell0.lifetime=5.0;
cell0.birthRate=100.0;
cell0.velocity=50;
cell0.emissionLongitude=M_PI_4*1.5;
cell0.emissionRange=M_PI_4/2;
cell0.xAcceleration=-5;
cell0.contents=(id)[UIImage imageNamed:@"xiaolvkuai.png"].CGImage;
CAEmitterCell*subCell=[CAEmitterCell emitterCell];
subCell.lifetime=8;
subCell.birthRate=10;
subCell.contents=(id)[UIImage imageNamed:@"xiaolvkuai.png"].CGImage;
subCell.velocity=60;
subCell.emissionLongitude=0;
subCell.emissionRange=M_PI_2;
subCell.beginTime=4.5;
subCell.scale=0.5;
cell0.emitterCells=@[subCell];
emitterLayer.emitterCells=@[cell0];
在下圖中紅色的箭頭表示了上述例子中,subCell的emissionLongtitude的取值寺渗,大家可以看到垂直向下的紅色箭頭的取值為0 匿情,因?yàn)榇怪毕蛳戮褪莝ubCell的父粒子的發(fā)射方向