免責申明(必讀!):本部落格提供的所有教程的翻譯原稿均來自於互連網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本部落格所有人、發表該翻譯稿之人無任何關係。謝謝合作!
原文地址:http://www.raywenderlich.com/6063/uikit-particle-systems-in-ios-5-tutorial
本文由糖炒小蝦、Benna翻譯 ,校對:sai、u0u0、iven、子龍山人
iOS 5中的UIKit粒子系統教程
Ray的話:這是第15篇、也是最後一篇《iOS 5 盛宴》中的iOS 5教程!這篇教程來自我們的新書《iOS 5 教程》中的一篇免費預覽章節。這個禮拜三我們將迎來《iOS 5 盛宴》系列的最後一次發布——來自史詩般的《iOS 5 盛宴》奉送,最後一次#ios5feast的廣播!:]
這是篇教程由iOS教程小組成員Marin Todorov所撰寫,他是一位擁有超過12年經驗的軟體開發人員,一位iOS的獨立開發人員,同時他也是Touch Code Magazine的創立者。
你可能已經看過一些粒子系統,它們被應用於很多iOS應用程式和遊戲中,諸如爆炸、火特效、下雨或者下雪等。然而,你所看到的這些特效類型可能大部分出現在遊戲之中,因為UIKit不提供內建的功能來建立粒子系統----直到iOS 5的出現,這種情況將有所改變,本教程將採用UIKit來製作粒子系統!
現在,利用iOS 5你能直接在UIkit中使用粒子系統,給你的應用程式帶來很多令人興奮的視覺享受。這裡有一些非常適用於使用粒子系統的例子:
- UIKit 遊戲:是的,你可以通過普通的UIKit製作遊戲(有些遊戲類型運作得相當好,尤其是棋牌類遊戲)。現在,你可以用爆炸、煙霧等其他更引人的東西製作更好的遊戲!
- 美化UI效果:當你的使用者在介面上移動一個物體時,它能留下一條煙霧痕迹,為什麼不做呢?
- 令人目眩的螢幕轉場效果:何不在你的應用程式顯現下一個情境時,讓之前的情境消失在一個火球中?
希望能用UIKit粒子系統做些什麼,也許你已經有一些很酷的想法啦。那麼,讓我們開始吧!
在這個教程中,我們將開發一個叫“Draw with fire”的應用程式,讓你(你猜中了)在螢幕上繪製火焰。
你將和我一起完成粒子系統的建立與設定來實現螢幕上看到的效果,讓你能將你的想法一步步實現。當這個應用完成,你就能用它繪製一個用火焰標記的漂亮的問號,就像這個:
新的粒子 API
有兩個類在你建立粒子系統時將會需要使用,它們在QuartzCore架構中,名叫CAEmitterLayer和CAEmitterCell。
通常的想法是你建立一個CAEmitterLayer,並將一個或多個CAEmitterCell添加到裡面。接著每個單元(cell)會按它配置的樣式產生粒子。
而且CAEmitterLayer繼承自CALayer,你能輕易地在UIKit分層的任何地方加入它!
我想這個新的UIKit粒子系統最酷的是一個單獨的CAEmitterLayer可以支援多個CAEmitterCell。這支援你完成一些相當複雜而且很酷的效果。例如當你建立泉水時,你能擁有一個cell發射水滴,另一個cell在泉水上發射水蒸汽!
Getting Started
開啟Xcode,並從主菜單中選擇File\New\New Project,選擇iOS\Application\Single View Application模版,點擊Next,鍵入程式名“DrawWithFire”,再鍵入DWF為首碼,選擇iPhone for Device Family,確認勾選“Use automatic reference counting”(其他選擇框別選)。接著點擊Next,再點擊Create儲存項目。
選擇你的項目,再選擇DrawWithFire的target。接著開啟Build Phases選項卡,展開Link Binary With Libraies部分,再點擊“+”按鈕,雙擊QuartzCore.framework,將Quartz繪圖功能添加到項目裡面。
我們將建立一個自訂UIView類來開始項目,這個類將有CAEmitterLayer作為它的層。事實上,完成這些非常簡單,通過重寫UIView類的+(Class)layerClass方法並返回一個CAEmitter類。相當酷哦!
建立一個新檔案,採用iOS\Cocoa Touch\Objective-C類模板,類名為DWFParticleView,繼承於UIView。
開啟DWFParticleView.m並替換為如下代碼:
#import "DWFParticleView.h"
#import <QuartzCore/QuartzCore.h>
@implementation DWFParticleView
{
CAEmitterLayer* fireEmitter; //1
}
-(void)awakeFromNib
{
//set ref to the layer
fireEmitter = (CAEmitterLayer*)self.layer; //2
}
+ (Class) layerClass //3
{
//configure the UIView to have emitter layer
return [CAEmitterLayer class];
}
@end
讓我們重溫下初始代碼:
- 我們建立一個單一的私人執行個體變數來控制CAEmitterLayer。
- 在awakeFromNib中,我們設定fireEmitter為這個視圖的self.layer。我們將它儲存在我們建立的fireEmitter執行個體變數中,因為之後我們將在這上面設定許多參數。
- +(Class)layerClass是UIView的類方法,它告訴UIKit使用哪個類作為這個視圖的根CALayer。想要更多關於CALayer的資訊,請查看CALayer教程介紹。
接下來,讓我們將視圖控制器的根視圖轉到DWFParticleView。開啟DWFViewController.xib並實現如下步驟:
1、 確認Utilities工具條是可見的(在這張圖上突出的按鈕都改被按下)。
2、 選擇Interface Builder中的灰色地區——這是這個視圖控制器的根視圖。
3、 點擊Identity Inspector選項卡。
4、 在Custom Class面板,在文字框中輸入DWFParticleView。
現在,我們已經將UI全都設定好——幹得好!讓我們在圖中添加一些粒子。
A Particle Examined
為了發射火焰、煙霧、瀑布或者其他什麼,你將需要一份好的PNG檔案來啟動你的粒子。你可以在任何影像編輯程式中自己製作它。看看我為這個教程製作的圖片(它被放大了共置於黑色背景中,這樣你才能看清楚它的形狀):
我的粒子檔案大小是32*32像素,這是份透明的PNG檔案,我僅使用有點時髦的筆刷,隨意地用白色繪製而成。對粒子來說,最好的就是使用白色,因為粒子發射器可以用我們想要的顏色對提供的映像著色。讓粒子映像呈半透明也是個很好的想法,如此粒子系統就能將粒子通過它們自己混合在一起(你可以通過少量不同的影像檔理解它如何工作)。
這樣,你就能建立自己的粒子或者就使用我製作的這個,但是要確定它添加到你的Xcode項目中並命名為Particles_fire.png。
讓我們開始產生粒子吧!
是時候添加代碼讓我們的CAEmitterLayer做一些神奇的事情啦!
開啟DWFParticleView.m 將如下代碼添加到awakeFromNib:中
//configure the emitter layer
fireEmitter.emitterPosition = CGPointMake(50, 50);
fireEmitter.emitterSize = CGSizeMake(10, 10);
上面的代碼是用來設定emitter的座標(view的座標系下) 和產生粒子的大小的。
然後,在awakeFromNib後面添加一個CAEmitterCell到CAEmitterLayer上,讓我們最終能在螢幕上看到粒子效果
CAEmitterCell* fire = [CAEmitterCell emitterCell];
fire.birthRate = 200;
fire.lifetime = 3.0;
fire.lifetimeRange = 0.5;
fire.color = [[UIColor colorWithRed:0.8 green:0.4 blue:0.2 alpha:0.1]
CGColor];
fire.contents = (id)[[UIImage imageNamed:@"Particles_fire.png"] CGImage];
[fire setName:@"fire"];
//add the cell to the layer and we're done
fireEmitter.emitterCells = [NSArray arrayWithObject:fire];
我們產生了一個cell執行個體,並設定了一些屬性。然後設定CAEmitterLayer中emitterCells的屬性---一個包含cells的NSArray數組。現在emitterCell們已經設定好了,CAEmitterLayer準備好發射粒子了!
剛才設定了很多CAEmitterCell的屬性,讓我們一一過下
- Birthrate(出生率):每秒發射的粒子數量,一個好的火焰或者瀑布你最少需要幾百個粒子,所以我們設定為200
- lifetime(生命時間):一個粒子幾秒後消失,我們設定為3.0
- liftetimeRange(生命時間變化範圍):你可以用這個東西使粒子的lifetime產生少許變化。粒子系統會隨機在這個區間中取一個lifetime出來(lifetime – lifetimeRange, lifetime + lifetimeRange) 在我們的程式中,粒子會存活2.5~3.5秒
- Color(顏色):粒子內容的顏色,我們這裡選擇橙色
- Contents(內容):用於cell的內容,一般是一個CGImage. 我們把它賦值給粒子映像。
- Name(名稱):你可以給你的cell取一個名字,用來在之後的時間裡尋找和修改它的屬性。
運行程式,來測試一下我們的粒子效果!
好吧,它工作了,但是並不像我們想象的那麼酷。你甚至可以毫不掩飾的承認,它看起來像一個橘黃色的斑點。
讓我們做一些小改動使粒子變得更具有動感。把這些代碼添加到setName: on the cell前面
fire.velocity = 10;
fire.velocityRange = 20;
fire.emissionRange = M_PI_2;
- velocity(速度): 粒子每秒移動的像素數. 這裡我們讓cell發射的粒子向螢幕的右邊沿移動這裡我們設定如下的新屬性在CAEimtterCell中:
- velocityRange(速度範圍): 速度變化範圍,和lifetimeRange相似
- emissionRange(發射角度):這是一個cell發射的角度範圍(弧度制).M_PI_2(pi/2)是45度(也就是說產生範圍會+-45度)
(編譯並運行來)檢查一下我們的成果
這次看起來好點,距離我們的目標不遠了!如果你想把這些屬性是如何影響粒子發生器理解的更透徹,那就去自由發揮吧,修改屬性值看效果。
再添加兩行,來結束cell的設定
fire.scaleSpeed = 0.3;
fire.spin = 0.5;
- ScaleSpeed(變大速度):每秒修改粒子大小的百分比。我們設定0.3讓粒子隨著時間則推移變大 這裡我們設定如下的新屬性在CAEimtterCell中
- Spin(旋轉):每個粒子的旋轉速率。我們設定0.5來給粒子一個漂亮的旋轉
再次運行:
現在看起來有點像鐵鏽色的煙,這是為什麼呢?CAEmitterCell有很多屬性來調整,天空類型在這裡受限制。在設定fireEmitter.emitterSize後面加這麼一句話
fireEmitter.renderMode = kCAEmitterLayerAdditive;
這一行代碼用於讓我們鐵鏽色的煙變成沸騰的火球,運行來查看效果
發生了什嗎?遞增渲染模式(additive render mode)基本上是告訴系統不要用普通的方式——一個蓋住一個的繪製粒子,而是換了一種更酷的方法:如果粒子相互重疊的話他們的顏色強度會增加!所以,你會在粒子發生器的地區裡面看到大量的白色亮斑,但在地區外是火球,那裡因為粒子不斷消亡而數量減少,色彩漸層到原來的鐵鏽色。太棒了!
你現在可能會覺得火焰非常不真實。的確,你可以通過修改cell的屬性讓火焰的效果更好。但是我們需要這麼厚的效果,因為我們要去繪製它。當你在裝置螢幕上拖動手指的時候,螢幕上會收到相對少的觸摸點,所以我們用厚重的火球來補償他。
玩兒火吧!
現在,你終於可以玩火了(在現實生活中我們被告知永遠不要玩火):]
下面來實現用手指在螢幕上畫火焰,我們需要通過使用者觸摸點來修改粒子發生器的位置
首先在DWFParticleView.h中聲明一個方法:
-(void)setEmitterPositionFromTouch: (UITouch*)t;
然後在DWFParticleView.m中實現它
-(void)setEmitterPositionFromTouch: (UITouch*)t
{
//change the emitter's position
fireEmitter.emitterPosition = [t locationInView:self];
}
這個方法獲得一個觸摸點並作為實參, 將觸摸點資訊轉移到ParticleView的座標系中,並修改粒子發生器的座標
我們需要在view controller中控制它,所以我們下一步是在viewController中定義介面
#import <UIKit/UIKit.h>
#import "DWFParticleView.h"
@interface DWFViewController : UIViewController
{
IBOutlet DWFParticleView* fireView;
}
@end
之後開啟DWFViewController.xib 然後按住control從File’s Owner向root view拖動,在彈出的選項卡中選擇fireView正如你所見,我們import了我們的自訂view類,並且在DWFParticleView中定義了執行個體變數。
現在我們能通過view controller 訪問emitter 層。開啟DWFViewController.m 刪掉所有自動產生的程式碼,添加如下代碼
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setEmitterPositionFromTouch: [touches anyObject]];
}
試著用更快或者更慢的速度拖動它,來看對粒子產生所造成的影響運行,觸摸並向四周拖動,你會看到粒子發生器跟著移動並留下一串很酷的火焰
動態修改Cell
今天最後的話題是在emitter 層動態改動cell們。現在,粒子發生器一直在產生粒子。沒能給使用者一種是他們畫上去的感覺,讓我們來將粒子發生的條件改為僅當手指觸摸到螢幕的時候產生粒子.那麼一開始就不要產生了,於是將DWFParticleView.m的awakeFromNib method方法中 粒子的birthrate設為0
fire.birthRate = 0;
如果你現在啟動並執行話,會發現螢幕空空如也,很好!下面添加一個方法來作為粒子發生(發射)器的開關。首先在DWFParticeView的標頭檔中定義如下方法
-(void)setIsEmitting:(BOOL)isEmitting;
然後在DWFParticleView.m實現它
-(void)setIsEmitting:(BOOL)isEmitting
{
//turn on/off the emitting of particles
[fireEmitter setValue:[NSNumber numberWithInt:isEmitting?200:0]
forKeyPath:@"emitterCells.fire.birthRate"];
}
這裡使用setValue:forKeyPath:方法來改動一個cell, 是因為我們早先將cell的名字添加到了emitter中。我們使用”emitterCells.fire.birthRate”做keypath是因為birthRate是emitterCells數組中一個叫做叫fire的cells的屬性。
最後我們需要在觸摸開始的時候開啟粒子發生器的開關,在抬起手指的時候關掉它。在DWFViewController
中添加如下代碼
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setEmitterPositionFromTouch: [touches anyObject]];
[fireView setIsEmitting:YES];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setIsEmitting:NO];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[fireView setIsEmitting:NO];
}
完成並運行~觀察效果咯~記住,你在玩兒火:-)
Where To Go From Here?
這裡是範例工程的全部代碼。
如果你喜歡這個教程,這裡有更多你可以研究的東西。你可以
- 實驗不同的粒子圖片
- 去CAEmitterCell的官方文檔中看看它全部的屬性們
- 添加一個函數,將螢幕上的圖片渲染儲存到圖片中
- 將繪製過程儲存到視頻檔案中
- 在你的所有應用的文字框的後面添加燃燒的火焰作為背景。
需要論壇交流,請點擊:傳送門!