Silverlight 2.5D RPG游戲技巧與特效處理:(九)粒子系統

作者: 深藍色右手  來源: 博客園  發布時間: 2011-04-02 10:17  閱讀: 1249 次  推薦: 0   原文鏈接   [收藏]  

  粒子系統通常用于三維場景中進行真實環境模擬,比如第四節的天氣系統。天氣的實現是粒子系統最重要的應用領域之一,所有的一切無論是云、雨、霧,還是落葉、隕石及閃電,都可視作基礎粒子;除此之外,游戲中常常還會用到粒子系統來渲染像發光、軌跡等抽象效果。當然,有時簡單并不意味著就不能締造奇跡,比如本節大家將要看到的就是基于簡單粒子系統所創建的當下主流2.5D RPG中極其拉風之裝備粒子發散動畫特效。

   一提到Silverlight中的粒子,首先想到的肯定是WriteableBitmap。沒錯,今天的主角就是它,讓人又愛又恨的WriteableBitmap。愛,因為它可以對Silverlight中任意UI對象進行基于像素級別的處理,這意味著只要想得出的效果肯定能實現;正因如此,對其恨之入骨則因為WriteableBitmap在目前的版本中還暫時無法得到GPU硬件加速的支持,其功能偏少,性能也有待進一步提高。

  僅當為了Silverlight 5 的登場打基礎,借鑒《降龍之劍》中的裝備粒子系統,我們同樣可以通過非常簡單的代碼編寫出極度拉風的粒子發射動畫特效。

  于是我們得首先準備基礎粒子素材,比如希望武器會冒著火光,亦或者你希望鎧甲能寒冰四散等,那么我們每樣準備3張或更多的圖片,通過隨即的方式呈現便可達到相當逼真的效果:

  接下來的問題是這些粒子該如何以精確的裝備部件實體區域為起點進行發射?

  用WriteableBitmap對每幀圖片進行寬*高數量像素的逐個掃描?試想如果角色的尺寸是500*500=250000,那么每間隔幾百毫秒都要遍歷幾十萬的像素,UI線程不卡死才怪(順便提一下,UI卡死的罪魁禍首就是循環,在UI線程中應盡量減少循環次數或巧妙的進行分解與規避)。其實我們完全可以采用隨機抽樣的方案,比如每次對其中的1000個像素進行抽樣檢查,當其A值(透明度)不為0時則表示該像素正處于裝備部件的實體區域中。接下來便可仿造流光追影的類似效果,通過WriteableBitmap對目標裝備部件首先進行復制,然后按照如上方式隨機抽取部分裝備實體上的粒子進行發射飄散:

 
/// <summary>
/// 顯示風中粒子
/// </summary>
/// <param name="role">所修飾角色</param>
/// <param name="equipType">所參照的裝備</param>
/// <param name="particleType">粒子類型</param>
/// <param name="color">顏色</param>
/// <param name="amount"></param>
public void ShowWindParticles(Hero role, EquipTypes equipType, ParticleTypes particleType, Color color, double amount) {
int distance = 0;
double speed = 0;
if (role.Action == Actions.Stop) {
distance
= 40;
speed
= RandomSeed.Next(2000, 3000) * 0.01;
}
else {
distance
= 40;
speed
= RandomSeed.Next(1000, 1500) * 0.01;
}

int halfDistance = distance / 2;
int obliqueDistance = distance * 2 / 3;
string particleName = string.Empty;
switch (particleType) {
case ParticleTypes.Normal:
particleName
= "Particle";
break;
case ParticleTypes.Smoke:
particleName
= "Smoke";
break;
case ParticleTypes.Ice:
particleName
= "Ice";
break;
case ParticleTypes.Spark:
particleName
= "Spark";
break;
}
Dispatcher.BeginInvoke(
delegate {
ObjectBase equip
= role.EquipEntity(equipType);
WriteableBitmap writeableBitmap
= new WriteableBitmap(equip, null);
lock (writeableBitmap) {
writeableBitmap.Invalidate();

int z = 0;
if (equipType == EquipTypes.Weapon) {
z
= role.Z + equip.Z;
}
else {
z
= (role.Direction == Directions.North || role.Direction == Directions.NorthEast || role.Direction == Directions.NorthWest) ? role.Z : role.Z - 20;
}
Point position
= equipType == EquipTypes.Overall ? role.Center : equip.Position;
Point2D destination
= new Point2D();
MonoChrome monoChrome
= new MonoChrome() { FilterColor = color };
switch (role.Direction) {
case Directions.North:
destination.X
= 0; destination.Y = RandomSeed.Next(halfDistance, distance);
break;
case Directions.NorthEast:
destination.X
= -RandomSeed.Next(halfDistance, obliqueDistance); destination.Y = RandomSeed.Next(halfDistance, obliqueDistance);
break;
case Directions.East:
destination.X
= -RandomSeed.Next(halfDistance, distance); destination.Y = 0;
break;
case Directions.SouthEast:
destination.X
= -RandomSeed.Next(halfDistance, obliqueDistance); destination.Y = -RandomSeed.Next(halfDistance, obliqueDistance);
break;
case Directions.South:
destination.X
= 0; destination.Y = -RandomSeed.Next(halfDistance, distance);
break;
case Directions.SouthWest:
destination.X
= RandomSeed.Next(halfDistance, obliqueDistance); destination.Y = -RandomSeed.Next(halfDistance, obliqueDistance);
break;
case Directions.West:
destination.X
= RandomSeed.Next(halfDistance, distance); destination.Y = 0;
break;
case Directions.NorthWest:
destination.X
= RandomSeed.Next(halfDistance, obliqueDistance); destination.Y = RandomSeed.Next(halfDistance, obliqueDistance);
break;
}

for (int i = 0; i < amount; i++) {
int x = RandomSeed.Next(0, writeableBitmap.PixelWidth);
int y = RandomSeed.Next(0, writeableBitmap.PixelHeight);
byte[] bytes = BitConverter.GetBytes(writeableBitmap.Pixels[writeableBitmap.PixelWidth * y + x]);
if (bytes[3] != 0) {
Particle particle
= new Particle() { Effect = monoChrome, Z = z, Source = GlobalMethod.GetProjectImage(string.Format("UI/{0}{1}.png", particleName, RandomSeed.Next(0, 3))) };
space.Children.Add(particle);
EventHandler handler
= null;
particle.Disposed
+= handler = (s, e) => {
Particle p
= s as Particle;
p.Disposed
-= handler;
space.Children.Remove(p);
};
particle.Move(
new Point(role.Position.X - position.X + x, role.Position.Y - position.Y + y), new Point(role.Position.X - position.X + x + destination.X, role.Position.Y - position.Y + y + destination.Y), speed, MoveModes.Opacity);
}
}
}
});
}

  其中,根據角色的動作狀態以及當前的朝向,粒子在場景中的層次關系、移動速度,發散面積以及路徑等均應體現出差異;比如站立時粒子流動稍微慢些、貼身些,而當角色移動時模擬迎風的效果,粒子飄散則呈加速趨勢;另外,騎馬奔馳時因地面的凹凸跌宕也應塑造出粒子群散呈現起伏波動之勢等等:

  WriteableBitmapHLSLSilverlight中的兩把神器,特別是在制作動畫及特效方面尤顯突出,只要你的算法夠強,無論是采用C#還是HLSL,均可實現一切想要的特效動畫。雖然兩者目前的共同問題都出在性能上,如此這些華麗而炫酷的動畫特效使我更饑渴的期待Silverlight 5降臨的那天,當新的傳奇上演之刻起,歷史將被徹底的改寫!

  本節源碼下載地址:Demo8.rar

  在線演示地址:http://silverfuture.cn

0
0
 
標簽:Silverlight
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()