|
粒子系統(tǒng)通常用于三維場景中進(jìn)行真實環(huán)境模擬,比如第四節(jié)的天氣系統(tǒng)。天氣的實現(xiàn)是粒子系統(tǒng)最重要的應(yīng)用領(lǐng)域之一,所有的一切無論是云、雨、霧,還是落葉、隕石及閃電,都可視作基礎(chǔ)粒子;除此之外,游戲中常常還會用到粒子系統(tǒng)來渲染像發(fā)光、軌跡等抽象效果。當(dāng)然,有時簡單并不意味著就不能締造奇跡,比如本節(jié)大家將要看到的就是基于簡單粒子系統(tǒng)所創(chuàng)建的當(dāng)下主流2.5D RPG中極其拉風(fēng)之裝備粒子發(fā)散動畫特效。
一提到Silverlight中的粒子,首先想到的肯定是WriteableBitmap。沒錯,今天的主角就是它,讓人又愛又恨的WriteableBitmap。愛,因為它可以對Silverlight中任意UI對象進(jìn)行基于像素級別的處理,這意味著只要想得出的效果肯定能實現(xiàn);正因如此,對其恨之入骨則因為WriteableBitmap在目前的版本中還暫時無法得到GPU硬件加速的支持,其功能偏少,性能也有待進(jìn)一步提高。
僅當(dāng)為了Silverlight 5 的登場打基礎(chǔ),借鑒《降龍之劍》中的裝備粒子系統(tǒng),我們同樣可以通過非常簡單的代碼編寫出極度拉風(fēng)的粒子發(fā)射動畫特效。
于是我們得首先準(zhǔn)備基礎(chǔ)粒子素材,比如希望武器會冒著火光,亦或者你希望鎧甲能寒冰四散等,那么我們每樣準(zhǔn)備3張或更多的圖片,通過隨即的方式呈現(xiàn)便可達(dá)到相當(dāng)逼真的效果:
接下來的問題是這些粒子該如何以精確的裝備部件實體區(qū)域為起點進(jìn)行發(fā)射?
用WriteableBitmap對每幀圖片進(jìn)行寬*高數(shù)量像素的逐個掃描?試想如果角色的尺寸是500*500=250000,那么每間隔幾百毫秒都要遍歷幾十萬的像素,UI線程不卡死才怪(順便提一下,UI卡死的罪魁禍?zhǔn)拙褪茄h(huán),在UI線程中應(yīng)盡量減少循環(huán)次數(shù)或巧妙的進(jìn)行分解與規(guī)避)。其實我們完全可以采用隨機(jī)抽樣的方案,比如每次對其中的1000個像素進(jìn)行抽樣檢查,當(dāng)其A值(透明度)不為0時則表示該像素正處于裝備部件的實體區(qū)域中。接下來便可仿造流光追影的類似效果,通過WriteableBitmap對目標(biāo)裝備部件首先進(jìn)行復(fù)制,然后按照如上方式隨機(jī)抽取部分裝備實體上的粒子進(jìn)行發(fā)射飄散:
/// <summary>
/// 顯示風(fēng)中粒子
/// </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);
}
}
}
});
}
NET技術(shù):Silverlight 2.5D RPG游戲技巧與特效處理:(九)粒子系統(tǒng),轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。