Learn IPhoneand iPad Cocos2d Game Delevopment》第9章 。
由于相冊空間已滿,csdn不允許發布站外圖片,所以所有圖片以鏈接方式顯示。
為了產生粒子效果,程序員們不得不使用粒子系統。粒子系統噴射大量細小的粒子并對其進行高效的渲染,這要比sprite高效得多,因此你可以模擬出雨、煙、火、雪、爆炸、蒸汽尾跡等效果。
粒子系統由大量屬性驅動。所謂大量的意思,指超過30種,它們不僅影響著每個粒子的外觀和行為,而且影響著整體的效果。粒子效果是所有粒子共同創造出來的可視化效果。單個粒子無法產生火焰效果,哪怕10個粒子也遠遠不夠。如果不是數百,起碼也要成打的粒子才能產生火焰效果。
創建可觀的粒子效果是一個需要不斷嘗試的過程。在代碼中嘗試改變各種屬性,編譯,根據運行的結果調整粒子系統,不斷改變并重復這些過程——無論如何,這都是十分繁瑣的。粒子設計工具在這里派上了用場,我稱之為粒子設計器,本章我們將講述如何使用它。
## 一、粒子效果示例
Cocos2d提供了大量內置的粒子效果,這給你提供了一個很好的概念。在CCParticleExamples.m中有一些例子,你可以把它們作為模板,然后在你的游戲或者子類中修改它們,你僅僅需要做一些微小的調整。這些例子不需要任何特別的技術,你可以創建這些示例粒子效果,如同普通的CCNode對象。實際上,它們派生自CCNode。
我創建了一個ParticleEffects01項目,演示了cocos2d的所有示例粒子效果。你輕觸屏幕就可以快速地翻看這些效果,也可以通過拖曳動作移動它們。有許多粒子效果當它們開始移動后看起來就完全不一樣了,看起來效果不錯的大粒子束,在開始移動后很可能變得很吃力。
只有一種效果無法移動,這就是CCParticleExplosion效果,它是一次性效果,它一次性噴射所有粒子然后終止噴射,如圖所示:
[](http://img.ph.126.net/HfbEdaRE9qgx35T5BFYAwA==/567735028042252740.png)[http://img.ph.126.net/HfbEdaRE9qgx35T5BFYAwA==/567735028042252740.png](http://img.ph.126.net/HfbEdaRE9qgx35T5BFYAwA==/567735028042252740.png)
其他效果則可以持續運行,當有粒子生命結束并被刪除后又會創建出新的粒子。在這種情況下的挑戰是要維持屏幕中粒子數量的平衡。
?
面則顯示了示例程序ParticleEffects01中使用的一些有關粒子效果的方法。particleType作為switch語句的開關變量,表示了要創建的內置粒子系統類型。注意CCParticleSystem指針用于存儲粒子系統,因此我在runEffect方法末尾把它傳給addChild方法調用。每個示例的粒子效果都繼承自CCParticleSystem。
~~~
-(void) runEffect
{
// remove any previous particle FX
[self removeChildByTag:1 cleanup:YES];
CCParticleSystem* system;
switch (particleType)
{
case ParticleTypeExplosion:
system =[CCParticleExplosion node];
break;
case ParticleTypeFire:
system =[CCParticleFire node];
break;
case ParticleTypeFireworks:
system =[CCParticleFireworks node];
break;
case ParticleTypeFlower:
system =[CCParticleFlower node];
break;
case ParticleTypeGalaxy:
system =[CCParticleGalaxy node];
break;
case ParticleTypeMeteor:
system =[CCParticleMeteor node];
break;
case ParticleTypeRain:
system =[CCParticleRain node];
break;
case ParticleTypeSmoke:
system =[CCParticleSmoke node];
break;
case ParticleTypeSnow:
system =[CCParticleSnow node];
break;
case ParticleTypeSpiral:
system =[CCParticleSpiral node];
break;
case ParticleTypeSun:
system =[CCParticleSun node];
break;
default:
// do nothing
break;
}
[self addChild:system z:1 tag:1];
[label setString:NSStringFromClass([system class])];
}
-(void) setNextParticleType
{
particleType++;
if(particleType == ParticleTypes_MAX)
{
particleType = 0;
}
}
~~~
注意: NSStringFromClass方法在這個例子中很有用,我們用它來顯示類名,而不需要每個類都指定一個字符串。這是很酷的Object-c 運行時特性,你可以得到一個NSString表述的類名。如果用C++,你只能去咬自己的腳趾了。這對游戲的邏輯代碼沒有任何用處,但這是非常有用的調試和日志輸出技巧。你可以從蘋果的基礎框架參考中找到這些方法的完整列表和描述:
http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html.
如果你想在自己的工程中使用這些示例效果,你可能會看到一些丑陋的方塊,如下圖所示。
[http://img.ph.126.net/OVlPXIW-AGFQx-PR7vqXXg==/1042864788729840959.png](http://img.ph.126.net/OVlPXIW-AGFQx-PR7vqXXg==/1042864788729840959.png)
這是因為內建的例子試圖加載指定的貼圖fire.png,而它已經隨同cocos2d-iphone一起發布在Resources/Images目錄下。當然,不使用貼圖也可以創建出炫目的粒子效果——通過指定粒子的大小。但要想看到內置的示例原本的效果,就需要把fire.png加到你的Xcode工程中。
## 二、創建粒子效果
很容易派生出一個CCParticleSystem的子類。但是用它去創建出可觀且最終效果接近于最初設想的粒子效果就不那么容易了。以下列出了影響粒子系統外觀和行為的屬性列表:
~~~
¥? emitterMode = gravity
? gravity,centerOfGravity
?radialAccel, radialAccelVar
speed, speedVar
tangentialAccel, tangentialAccelVar
¥? emitterMode = radius
? startRadius,startRadiusVar, endRadius, endRadiusVar ?
?rotatePerSecond, rotatePerSecondVar
¥? duration
¥? posVar
¥? positionType ?
¥? startSize, startSizeVar, endSize, endSizeVar ?
¥? angle, angleVar
¥? life, lifeVar
¥? emissionRate?
¥? startColor, startColorVar, endColor, endColorVar
¥? blendFunc, blendAdditive
¥? texture
~~~
正如你所想的,這里有許多變量。當然,需要對每個屬性的用途有一個徹底的了解。先讓我們從創建一個CCParticleSystem派生類開始。
實際上,你可以從CCPointParticleSystem或CCQuadParticleSystem進行派生。PointParticle在1代或2代iOS設備上運行得更快,但在3代和4代設備上(iPhone3GS、iPad、iPhone4)表現不佳。這是由于CPU架構的不同。3代和4代蘋果使用ARMv7CPU架構的新技術和特性,比如矢量浮點處理器以及SIMD(單指令多數據技術,用于提高3D圖形運算效率)指令集(NEON),因此使用CCQuadParticleSystem能充分利用這些特性。
如果有疑問的話,不管什么設備總是用CCQuadParticelSystem去創建可視效果,或者讓cocos2d替你決定buildtarget。你可以查看我在ParticleEffects02項目中加入的ParticleEffectSelfMade文件:
~~~
#import <Foundation/Foundation.h>
#import "cocos2d.h"
// Depending on the targeted device the ParticleEffectSelfMadeclass will either derive
// from CCPointParticleSystem orCCQuadParticleSystem (preferred for iOS 3rd and 4th Generation)
@interface ParticleEffectSelfMade : ARCH_OPTIMAL_PARTICLE_SYSTEM
{
}
~~~
預處理定義ARCH_OPTIML_PARTICLE_SYSTEM取代了真實的基類名稱,它會在編譯動態決定從哪一個粒子系統繼承。Cocos2d基于處理器架構采用CCQuadParticleSystem或者CCPointParticleSystem給ARCH_OPTIML_PARTICLE_SYSTEM賦值:
~~~
// build each architecture with the optimalparticle system
#ifdef __ARM_NEON__
// armv7
#define ARCH_OPTIMAL_PARTICLE_SYSTEM CCQuadParticleSystem
#elif __arm__ || TARGET_IPHONE_SIMULATOR
// armv6 or simulator
#define ARCH_OPTIMAL_PARTICLE_SYSTEM CCPointParticleSystem
#else
#error(unknown architecture)
#endif
~~~
現在來看ParticleEffectSelMade類的實現,它使用了所有可用的屬性。我企圖示范每個屬性的使用,但你一一對每個屬性進行些修改學習效果會更佳,所以我建議你在這個項目中調整這些屬性值。同時,你也可以在代碼中看到關于這些參數的簡短描述:
~~~
@implementation ParticleEffectSelfMade
-(id) init
{
return [self initWithTotalParticles:250];
}
-(id) initWithTotalParticles:(int)numParticles
{
if((self = [super initWithTotalParticles:numParticles]))
{
//DURATION 時長
//大部分特效使用無限時長
self.duration = kCCParticleDurationInfinity;
//對于有時間限制的特效,使用粒子噴射時間的秒數,
//self.duration= 2.0f;
//如果粒子系統運行達到指定時間,當其所有粒子死亡后將被移出父節點 ??????????????adee??????????????
////
???? ?// 對于無限時長的粒子系統,該屬性無效
self.autoRemoveOnFinish = YES;
//MODE 模式
//噴射粒子會受重力的影響
self.emitterMode = kCCParticleModeGravity;
//粒子以環行運動
//self.emitterMode= kCCParticleModeRadius;
//某些屬性只能在特定的emitterMode下使用
if (self.emitterMode == kCCParticleModeGravity)
{
// 重心會讓你以為是粒子顯示時的偏移位置 ,實際上是指節點的位置
self.centerOfGravity = CGPointMake(-15, 0);
// gravity 會影響粒子x和y方向的速度
self.gravity = CGPointMake(-50, -90);
// 徑向加速決定了粒子離開發射器后的移動速度,正值表示粒子從發
// 射器出來后呈加速運動,負值表示粒子從發射器出來后作減速運動
self.radialAccel = -90;
self.radialAccelVar = 20;
// 切向加速使粒子圍繞加速器作旋轉運動,且旋轉速度會越來越快
// (彈弓效應)
self.tangentialAccel = 120;
self.tangentialAccelVar = 10;
// 粒子產生時的運動速度
self.speed = 15;
self.speedVar = 4;
}
else if (self.emitterMode == kCCParticleModeRadius)
{
// 開始半徑:從發射的位置到發射器的距離
self.startRadius = 100;
self.startRadiusVar = 0;
// 粒子移動的結束半徑。如果比開始半徑小,則粒子向內旋轉,如果 // 比開始半徑大,作外向旋轉。可以用
// kCCParticleStartRadiusEqualToEndRadius 常量做出完
//??整的環形運動效果
self.endRadius = 10;
self.endRadiusVar = 0;
// 旋轉速度
self.rotatePerSecond = 180;
self.rotatePerSecondVar = 0;
}
//EMITTER POSITION 噴射器位置
//噴射器位置默認在節點中心
//這是粒子產生后的顯示位置
self.position = CGPointZero;
self.posVar = CGPointZero;
//positionType 決定了當節點移動時已噴射出的粒子是否要重新定位
//(kCCPositionTypeGrouped) ,或者保持在原地
//?? (kCCPositionTypeFree).
self.positionType = kCCPositionTypeFree;
//PARTICLE SIZE 粒子大小
//每個粒子的大小(以像素為單位)
self.startSize = 40.0f;
self.startSizeVar = 0.0f;
self.endSize = kCCParticleStartSizeEqualToEndSize;
self.endSizeVar = 0;
//ANGLE (DIRECTION) 角度或方向
//粒子噴射角度,0表示正上方
self.angle = 0;
self.angleVar = 0;
//PARTICLE LIFETIME 粒子壽命
//粒子在屏幕上顯示的時間
self.life = 5.0f;
self.lifeVar = 0.0f;
//PARTICLE EMISSION RATE 粒子噴射速度
//每秒中噴射的粒子數目,當self.particleCount>=
// self.totalParticles時,會停止創建新粒子--如果你想制造間歇式爆
//?? 發的效果,在兩次爆發間會產生一個暫停
self.emissionRate = 30;
//通常設為該值,你可以改變它
self.totalParticles = 250;
//PARTICLE COLOR 粒子顏色
//開始顏色必須設置一個有效的顏色值,否則粒子會不可見。其他顏色
//是可選的。這些顏色決定了粒子在生命周期開始和結束時的顏色。
startColor.r = 1.0f;
startColor.g = 0.25f;
startColor.b = 0.12f;
startColor.a = 1.0f;
startColorVar.r = 0.0f;
startColorVar.g = 0.0f;
startColorVar.b = 0.0f;
startColorVar.a = 0.0f;
endColor.r = 0.0f;
endColor.g = 0.0f;
endColor.b = 0.0f;
endColor.a = 1.0f;
endColorVar.r = 0.0f;
endColorVar.g = 0.0f;
endColorVar.b = 1.0f;
endColorVar.a = 0.0f;
//BLEND FUNC 混合函數
//混合函數用于計算透明色,參數1是源,參數2是目
//有效的參數包括:GL_ZERO??GL_ONE??GL_SRC_COLOR?
//GL_ONE_MINUS_SRC_COLOR??GL_SRC_ALPHA
// GL_ONE_MINUS_SRC_ALPHA?? GL_DST_ALPHA?
// GL_ONE_MINUS_DST_ALPHA
self.blendFunc = (ccBlendFunc){GL_SRC_ALPHA, GL_DST_ALPHA};
//等同于把blendFunc設置為: {GL_SRC_ALPHA, GL_ONE}
//self.blendAdditive= YES;
//PARTICLE TEXTURE 粒子貼圖
self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
}
return self;
}
@end
~~~
1、可變屬性
有許多屬性以Var后綴命名,這些是可變屬性,它們為屬性的取值定義了一個模糊的范圍。例如life=5 和 lifeVar=1,意味著每個粒子的壽命為5秒,并在5±1的范圍內波動。
如果你不想使用可變屬性,將其設置為0。可變屬性使粒子呈現更有機和模糊的行為及外觀。但當你在設計一種新效果時,除非你相當的有經驗,可變屬性會造成一定的干擾。我建議初學者不使用可變屬性或者將可變屬性設置為比較小的值。
2、粒子數
totalParticles屬性控制了粒子效果的粒子數目,它通常用initWithTotalParticles方法初始化,但你可以在此后修改。粒子數直接影響粒子效果的外觀和性能。
~~~
-(id) init
{
return [selfinitWithTotalParticles:250];
}
~~~
粒子數太小無法得到很炫的效果,太多則導致除了一個白塊外你什么也看不到。而且,太多的粒子會浪費幀率。因此,粒子設計工具不會讓你創建超過2000個粒子的效果。
3、發射器周期
duration屬性定義粒子發射的時間。如果設置為2,則只發射2秒鐘的新粒子然后終止:
~~~
self.duration= 2.0f;
~~~
如果你想讓粒子系統在停止發射而且最后一個粒子已經消失時,將粒子效果節點從父節點刪除,可將autoRemoveOnFinish屬性設置為 YES:
~~~
self.autoRemoveOnFinish= YES;
~~~
autoRemoveOnFinish屬性只能和有限運行的粒子系統一起使用。Cocos2d定義了一個kCCParticleDurationInfinity常量(等于-1),用于無限運行的粒子效果。大部分粒子效果屬于此類。
~~~
self.duration= kCCParticleDurationInfinity;
~~~
4、發射器模式
有兩種發射器模式:重力模式和徑向模式,由emitterMode屬性指定。這兩種模式使用類似的參數實現了截然不同兩種效果,如后面兩張圖所示。它們有一兩個獨特
~~~
ParticleEffects[6332:207]*** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason: 'Particle Mode should be Radius'
~~~
技巧:一般,針對你的目標效果,你應當使用最少的粒子數。粒子尺寸也很重要——每個粒子的尺寸越小,其性能就越好。
(1)重力模式
重力模式使粒子趨向或背離某個圓心運動。使用下面的代碼設置重力模式:
~~~
self.emitterMode= kCCParticleModeGravity;
~~~
重力模式使用以下獨有屬性(只能在 self.emitterMode= kCCParticleModeGravity 時使用):
~~~
self.centerOfGravity= CGPointMake(-15, 0);
?self.gravity = CGPointMake(-50, -90);
self.radialAccel= -90;
self.radialAccelVar= 20;
self.tangentialAccel= 120;
self.tangentialAccelVar= 10;
self.speed =15;
self.speedVar= 4;
~~~
centerOfGravity定義了一個CGPoint,距離新粒子出現位置的偏移量。centerOfGravity這個名稱很容易讓人誤解。真正的重心其實是節點的位置,centerOfGravity實際上是重心的某個偏移位置。而gravity屬性定義了粒子在x和y軸上的加速度。由于重心的作用,粒子的重力加速度不能太大,而centerOfGravity也不應該偏離的太遠。上面這些數值能給你一個比較好的參考。
radialAccel 屬性定義了粒子從發射器出來的加速度。該參數為負時,粒子的移動速度會逐漸減慢。tangentialAccel屬性與此類似,但它指的是粒子圍繞發射器旋轉的加速度——負值呈正時針旋轉,正值呈反時針旋轉。
很顯然,speed屬性就是粒子的速度。下圖顯示了重力模式下的粒子效果。
[http://img.ph.126.net/ukhAO3gFmhjlork4tOcvfA==/2560014913200276340.png](http://img.ph.126.net/ukhAO3gFmhjlork4tOcvfA==/2560014913200276340.png)
2)徑向模式
徑向模式使粒子呈圓環運動。它還可以做出螺旋效果(內旋或者外旋)。使用下面代碼設置徑向模式:
self.emitterMode= kCCParticleModeRadius;
如同重力模式,徑向模式也有幾個獨有的屬性,只能用于 self.emitterMode = kCCParticleModeRadius的情況:
~~~
self.startRadius= 100;
self.startRadiusVar= 0;
self.endRadius= 10;
self.endRadiusVar= 0;
self.rotatePerSecond= -180;
self.rotatePerSecondVar= 0;
~~~
startRadius屬性定義粒子將被噴射的位置距離重心(粒子效果節點的位置)的距離。類似,endRadius定義粒子旋轉結束的位置到重心的距離。如果你想到達閉環運動的效果,可以將endRadius和startRadius設置為相同的常量值:
~~~
self.endRadius= kCCParticleStartRadiusEqualToEndRadius;
~~~
rotatePerSecond屬性影響粒子移動方向和速度,如果startReadius和endRadius不同,則是指旋轉的圈數。
[http://img.ph.126.net/DUj0vgcCdols7DaMfyB3uw==/2847400864421855304.png](http://img.ph.126.net/DUj0vgcCdols7DaMfyB3uw==/2847400864421855304.png)
重力模式和徑向模式的效果如圖所示,你會發現盡管除了各自獨有的屬性外其他屬性完全相同,但兩者的顯示結果卻完全不同。要測試這點,把ParticleEffectSelMade類中以下語句取消注釋:
~~~
//self.emitterMode= kCCParticleModeRadius;
~~~
## 粒子位置
通過移動節點,你可以移動效果。但效果又一個posVar屬性決定了新粒子產生時的位置的可變范圍。默認情況下,position和posVar都位于節點中心:
~~~
self.position= CGPointZero;
self.posVar =CGPointZero;
~~~
粒子位置的一個非常重要的作用是粒子是否會隨著節點的移動而動。例如,如果你想創建一個星點圍繞玩家角色運動的效果,你可能想讓星點隨著玩家一起運動,這就要設置如下屬性:
~~~
self.positionType= kCCPositionTypeGrouped;
~~~
另外,如果想創建玩家著火并且讓粒子呈現尾跡效果(粒子會留在原地,不跟隨角色移動),則需要這樣設置positionType屬性:
~~~
self.positionType= kCCPositionTypeFree;
~~~
自由移動常用于蒸汽、火焰、發動機尾氣等效果, 粒子在噴射后就不再和發射器聯系在一起,但會在跟隨在物體后面。
## 粒子大小
粒子大小用startSize和endSize屬性描述(以像素為單位),這兩個屬性分別指定了粒子被噴射出來和粒子被移除時的粒子大小。粒子大小會從startSize逐漸向endSize變化。
~~~
self.startSize= 40.0f;
?self.startSizeVar = 0.0f;
self.endSize= kCCParticleStartSizeEqualToEndSize;
self.endSizeVar= 0;
~~~
常量kCCParticleStartSizeEqualToEndSize用于確保粒子在整個生命周期中不會變化。
## 粒子方向
angle屬性設定粒子最初的發射方向,為0表示粒子向正上方發射(發射模式為重力模式時)。對于徑向模式,它決定了噴射點在開始半徑上的位置,隨著該值的增加,噴射點會沿著開始半徑(startRadius)作反時針移動。
~~~
self.angle =0;
self.angleVar= 0;
~~~
## 粒子壽命
粒子壽命即粒子從開始到結束所經過的秒數,當粒子生命終止時,粒子會簡單的淡出并消失。粒子壽命使用life屬性設置。注意,粒子壽命越長,同一時間內在屏幕上顯示的粒子越多。當達到粒子數上限時,粒子噴射停止直到有粒子死亡。
~~~
self.life =5.0f;
self.lifeVar= 1.0f;
~~~
噴射速度emissionRate指每秒鐘能生成的粒子數目。同粒子存活數totalParticles屬性一起,二者對粒子效果的外觀有重要影響。
~~~
self.emissionRate= 30;
self.totalParticles= 250;
~~~
一般,你需要平衡emissionRate和totalParticles兩個屬性。你可以用totalParticles除以life作為emissionRate:
~~~
self.emissionRate= self.totalParticles / self.life;
~~~
小技巧:通過調整粒子壽命,粒子存活數,以及噴射速度,你可以創建間歇噴發效果,由于屏幕中的粒子數已達到最大,新粒子不再產生,導致粒子束呈現周期性的中斷。如果你不希望見到粒子流中出現間斷,則需要增加粒子存活數,或者減少粒子壽命/或噴發速度。當然,你也可以使用emissionRate = totalParticles/ life。
## 粒子顏色
每個粒子都會從起始色向終止色進行顏色漸變,創建一種生動的彩色效果。你至少要設置一個startColor,否則粒子不可見-因為默認被設置為黑色。顏色值是典型的ccColor4F結構體,即用4個浮點數分別表示r,g,b顏色和alpha值。每個浮點數取值范圍在0-1之間,代表該顏色數從0到100%之間,或者alpha值在完全透明到完全不透明之間。例如,一種白色塑料的顏色可以用如下方法表示:
~~~
startColor.r= 1.0f;
startColor.g= 0.25f;
?startColor.b = 0.12f;
startColor.a= 1.0f;
startColorVar.r= 0.0f;
startColorVar.g= 0.0f;
startColorVar.b= 0.0f;
startColorVar.a= 0.0f;
endColor.r =0.0f;
endColor.g =0.0f;
endColor.b =0.0f;
endColor.a =1.0f;
endColorVar.r= 0.0f;
endColorVar.g= 0.0f;
endColorVar.b= 1.0f;
endColorVar.a= 0.0f;
~~~
## 粒子混合模式
混合(blend)是指粒子在顯示前需要計算其所有像素。blendFunc的取值是一個ccBlendFunc結構體,用以指定源混合模式和目標混合模式:
self.blendFunc= (ccBlendFunc){GL_SRC_ALPHA, GL_DST_ALPHA};
混合時采用源圖像(粒子)的r,g,b以及alpha值與屏幕圖片原有的顏色值進行混合(粒子被渲染時)。實際上,粒子是以某種方式和其背景混合在一起的,blendFunc屬性決定了源圖像的哪些及多少顏色需要和背景的哪些及多少顏色進行混合。
blendFunc屬性對于粒子的顯示有深刻影響。通過在源圖像和目標圖像上配合使用下列混合模式,你可以創建出乎意料的效果,或者僅僅是一個個黑色顆粒。這需要大量的經驗和練習。
~~~
GL_ZERO
GL_ONE
?GL_SRC_COLOR ?
GL_ONE_MINUS_SRC_COLOR
?GL_SRC_ALPHA
?GL_ONE_MINUS_SRC_ALPHA
?GL_DST_ALPHA ?
GL_ONE_MINUS_DST_ALPHA
~~~
在[www.khronos.org/opengles/documentation/opengles1_0/html/glBlendFunc.html](http://www.khronos.org/opengles/documentation/opengles1_0/html/glBlendFunc.html),你能找到許多關于OpenGL混合模式的信息及細節。
源圖和目標圖配合使用GL_SRC_ALPHA和GL_ONE模式常用于創建疊加式混合,在繪制大量粒子摞在一起時會導致一種非常亮甚至白色的效果。
~~~
self.blendFunc= (ccBlendFunc){GL_SRC_ALPHA, GL_ONE};
~~~
當然,你也可以簡單地將blendAdditive 屬性設為 YES, 等同于將 blendFunc 設為 GL_SRC_ALPHA 和 GL_ONE:
~~~
self.blendAdditive= YES;
~~~
通常,創建透明的粒子需要使用GL_SRC_ALPHA 和 GL_ONE_MINUS_SRC_ALPHA 模式:
~~~
self.blendFunc= (ccBlendFunc){GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};
~~~
## 粒子貼圖
如果不使用貼圖,所有的粒子都會是平淡的有色顆粒。要在粒子效果中使用貼圖,用CCTextTureCache的addImage方法增加一個圖片,該方法會將指定的圖片文件加載并返回一個CCTexture2D對象:
~~~
self.texture= [[CCTextureCache sharedTextureCache] addImage:@"fire.png"];
~~~
粒子圖片最好看起來像云塊,或者接近于球體。如果粒子圖片存在有強烈對比區域、呈不規則形狀,例如圖片redcorss.png(一個紅叉形狀), 通常不利于粒子效果的顯示。這會很容易導致顆粒感的出現,因為粒子間很難混合。某些效果可以這樣干,比如前面提到的在角色頭頂旋轉的小星點。
粒子貼圖的最重要的一點,是圖片大小不得超過64x64像素。貼圖尺寸越小,粒子效果的性能越好。
?
## 粒子設計器
粒子設計器是一種在cocos2d和iOSOpenGL下創建粒子效果的圖形工具。可以在[http://particledesigner.71squared.com](http://particledesigner.71squared.com/)下載其試用版。
這是一個難得的工具,為你在創建粒子效果時省去大量的時間。它強大的地方是當你一改變粒子效果的屬性,就可以從屏幕上看到其效果。粒子設計器的用戶界面默認顯示了一個可視化的粒子效果列表。通過選擇,可以編輯粒子效果,通過點擊或者點擊右上角的Emitter Config按鈕可以切換到Emitter Config視圖:
[http://img.ph.126.net/TZd-2tQGaIzQXW9Ka8JjBQ==/3102417193321709960.png](http://img.ph.126.net/TZd-2tQGaIzQXW9Ka8JjBQ==/3102417193321709960.png)
你應該通過前面的介紹認出這些參數了。粒子設計器中只有很少的幾個屬性是無效的,不能編輯。一個是positionType。另一個是endRadiusVar,這是在徑向模式中使用的。后者意味著你不能創建外向旋轉的粒子效果(徑向模式)。但你還是可以從粒子設計器加載效果,然后通過代碼修改某些屬性。這只是一個可以忽略不計的小缺陷。
僅有的一個特別地方是粒子貼圖Particle Texture。這里既沒有可以加載圖片的按鈕,雙擊這個地方也沒有任何反應。竅門是,Particle Texture欄只接受拖拽。吧圖片從Finder拖拽到ParticleTexture欄,如果它變成了綠色,就可以了。當你拖拽圖片后,圖片就會應用于該粒子效果。如果你使用了超過64*64的圖片,粒子設計器會警告你,但仍然會使用該圖片,不過會將圖片縮放為64*64,而不管源圖的比例。
粒子設計器的預覽窗口如下,就像一個模擬器窗口:
[http://img.ph.126.net/4OpprKS8nVOqKT8RaUjS0g==/1561060220854158265.png](http://img.ph.126.net/4OpprKS8nVOqKT8RaUjS0g==/1561060220854158265.png)
也可以將它設置為iPad的屏幕,可以通過點擊這個iPad/iPhone來改變屏幕朝向,也可以通過設計器菜單欄上的Orientation按鈕操作。
點擊并在預覽窗口中進行拖動,粒子效果也會隨之而動,這將使你更容易看出移動的粒子效果。
注意,BackgroundColor設置對真實的粒子效果沒有影響,它只能改變預覽窗口的背景色。這對于設計一個在明亮背景下顯示的昏暗的粒子效果是有幫助的,因為你可以在預覽窗口中看到效果。
如果你缺少創意,你可以使用Ramdomize按鈕。你可能會奇怪ramdomize 在粒子設計器中是什么意思。Urban詞典告訴我, ramdomize是random的俚語。我猜開發人員是不是因為使用ramdomize會顯得更酷一些。
這絕對是一個創意,但它并不能做到任何屬性都隨機變化。例如,Ramdomize的Emitter Type、Emitter Location和一些與特定Emitter Type相關的參數始終不會改變。
?一旦你尋找到自己的創意,你可以拖動滑動條,然后在預覽窗口查看效果。不停地調整其效果直到你滿意。盡管這非常有趣,但你很快就發現你制作出來的新效果只能讓你高興一下子。
提示:設計粒子效果的時候一定要小心!要牢記,游戲還需要計算和渲染大量其他對象。如果你在預覽窗口查看效果時有60fps的幀率,這并不意味著在游戲中這個粒子效果也能達到60fps。別忘記在你的游戲中測試你的粒子效果并始終關注它的幀率。還要保證在設備上也要進行同樣的測試。在模擬器上體現出來的游戲性能經常是不真實的,別把它當真。粒子設計器預覽窗口也同樣如此。
?
?
## 使用粒子設計器效果
假設幾個小時后,你做出了一個完美的粒子效果。現在,你想把它用在cocos2d中。首先需要保存粒子效果。當你點擊粒子設計器保存按鈕時,彈出如下對話框:
[http://img.ph.126.net/VWiYCf3Nqh7IcyKUTeqGqg==/612208074362537220.png](http://img.ph.126.net/VWiYCf3Nqh7IcyKUTeqGqg==/612208074362537220.png)
要保存為cocos2d能夠使用的粒子效果,你必須使用cocos2d格式(plist)進行保存。你也可以鉤上Embedtexture選項,這會將紋理貼圖保存在plist文件中。這個好處是你只需要把plist文件加到Xcode項目就可以了,壞處是如果不在粒子設計器中加載粒子效果,你將無法改變粒子貼圖。
保存之后,你可以把效果的plist以及png文件(如果未使用Embedtexture)加到Xcode項目的Resources組。在ParticleEffects03項目中,我加入了兩個效果,一個效果帶有一個單獨的png貼圖,而另一個效果的貼圖則嵌在了plist文件中。
下面顯示了對runEffect方法的修改,以加載用粒子設計器設計出來的效果。
~~~
-(void) runEffect{
// 刪除原先的粒子效果
[selfremoveChildByTag:1 cleanup:YES];
CCParticleSystem*system;
switch (particleType){
caseParticleTypeDesignedFX:
system = [CCQuadParticleSystemparticleWithFile:@"fx1.plist"];
break;
caseParticleTypeDesignedFX2:
system = [CCQuadParticleSystemparticleWithFile:@"fx2.plist"];
system.positionType = kCCPositionTypeFree;
break;
caseParticleTypeSelfMade:
system = [ParticleEffectSelfMade node];
break;
default:
break;
}
CGSizewinSize = [[CCDirector sharedDirector] winSize];
system.position= CGPointMake(winSize.width / 2, winSize.height / 2); [self addChild:system z:1tag:1];
[label setString:NSStringFromClass([systemclass])];
}
~~~
初始化CCParticleSystem時,用particleWithFile方法加載粒子設計器效果的plist文件。在case語句中,我用CCQuadParitcleSystem的原因是在所有的iOS文件中它都能運行。你也可以使用ARCh_OPTIMAL_PARTICLE_SYSTEM來代替實際的類名:
~~~
system =[ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"fx1.plist"];
~~~
提示:粒子設計器的效果必須用CCQuadParticleSystem或者CCPointParticleSystem。 盡管 CCParticleSystem是前兩者的父類, 也實現了 particleWithFile 方法, 但除非你使用前面的兩個子類,加載設計器的效果后它不會顯示任何東西。
補充一下,我把兩行代碼移到了switchcase之外以避免代碼重復,它們是用于把粒子系統節點的位置放到屏幕中心的。
?
## 共享粒子效果
對于粒子設計器這是一個很酷的功能,你可以把你的創意和其他用戶共享。在設計器菜單里,只需選擇Share->Share Emitter,就彈出一個對話框,讓你輸入要共享的效果的標題、描述:
[http://img.ph.126.net/maz-k4aWBqMLRRJ-Gb1mkg==/25332747920817771.png](http://img.ph.126.net/maz-k4aWBqMLRRJ-Gb1mkg==/25332747920817771.png)
共享出來的粒子效果不一定完全滿足你的要求,但也能提供一個好的開始,以此開始你自己的創意。它們能幫助你較快地實現需要的效果,至少能激發創意。
我建議你瀏覽一下這些效果列表,試著找一些感覺。
## 射擊游戲和粒子效果
我現在想看看這些效果在游戲中的樣子。讓我們繼續推進這個射擊游戲的開發進程。在這章的ShootEmUp04項目,你會看到如圖所示的效果:
[http://img.ph.126.net/InHErGyRu8mBOq3T6jU40w==/2485986994325373221.png](http://img.ph.126.net/InHErGyRu8mBOq3T6jU40w==/2485986994325373221.png)
在EnemyEntity類中,gotHit方法是加入粒子爆炸效果的最佳地方,如下所示。我決定為Boss使用一種專門的效果,因為它是如此的大。
~~~
-(void)gotHit {
hitPoints--;
if (hitPoints <= 0) {
self.visible= NO;
// 當敵人被摧毀時,播放粒子效果
CCParticleSystem* system;
if (type == EnemyTypeBoss) {
system = [ARCH_OPTIMAL_PARTICLE_SYSTEMparticleWithFile:@"fx-explosion2.plist"];
} else {
?system= [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"fx-explosion.plist"];
}
// 設置不能在 Particle Designer 中設置的參數
system.positionType = kCCPositionTypeFree;
system.autoRemoveOnFinish = YES;
system.position = self.position;
?[[GameScenesharedGameScene] addChild:system];
}
}
~~~
粒子效果文件 fx-explosion.plist和 fx-explosion2.plist 必須加到項目的Resources組中。粒子系統的初始化在前面已經提過。因為粒子效果應該也必須獨立于敵人,不需要為創建它做過多的工作。首先,aurtoRemoveOnFinish設為YES,以便效果能自動移除。效果也應該定位到敵人的當前位置,以便爆炸時顯示在合理位置。
我把粒子效果加到GameScene,因為敵人是無法主動顯示粒子效果的。當敵人剛剛出現時是不可見的,它的出現會被粒子效果打斷。最主要的是,所有的EnemyEntity對象都被加到了CCSpriteBatchNode中,后者不允許你加入任何非CCSprite對象。如果粒子效果被加到EnemyEntity對象,會導致一個運行時異常。
在玩這個帶有粒子效果的游戲時,你會注意到當第1次顯示粒子效果時會有短暫的停頓。這是因為cocos2d需要加載粒子貼圖——無論貼圖是嵌入在plist中的,還是單獨的貼圖文件,這都是個緩慢的過程。為了避免這種情況,我在GameScene中加入了一種預加載的機制:在init方法中針對每一個粒子效果都調用preloadParticleEffect方法:
~~~
// 預加載粒子貼圖,每個粒子效果調用一次
[self preloadParticleEffects:@"fx-explosion.plist"];
[self preloadParticleEffects:@"fx-explosion2.plist"];
?preloadParticleEffects 方法只是創建粒子效果。因為返回的是autorelease對象,它的內存將自動釋放。但它所加載的貼圖仍然存在于CCTextureCache中。
-(void)preloadParticleEffects:(NSString*)particleFile {
[ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:particleFile];
}
~~~
如果你沒有使用在plist中嵌入貼圖,你可以通過調用CCTextureCache的addImage方法預加載粒子貼圖:
~~~
?[[CCTextureCache sharedTextureCache]addImage:particleFile];
~~~
## 結論
本章真是一個視覺盛宴。Cocos2d提供了大量的粒子效果,極好地說明了所能達到的效果,它們是高效而且易于使用的。
但使用源代碼創建粒子效果是十分痛苦的事情。有那么多的屬性需要設置;有一些又是某種emitter mode專有的;有一些從名稱上容易混淆無法理解其真實含義。但是在對每個屬性進行過一些講解后,你應該對這些屬性放到一個效果中有什么作用有一個基本的了解,同時知道其中最為關鍵的參數是哪些。
然后我們介紹了粒子設計器。這個工具非常有用——也很有趣。當你移動滑塊,然后觀察屏幕上的結果時,你突然就對粒子效果有了一個完整的理解,甚至你還可以吧你的作品與其他人共享,并體驗他人的成果。
最終,我們的射擊游戲也得到了改進,現在敵人被摧毀時能夠播放粒子效果了。這為游戲增色不少。
下一章,我們將介紹tilemaps。