[推薦] 粒子系統在2D特效中的應用(全) |
|
axsoft
版主 發表:681 回覆:1056 積分:969 註冊:2002-03-13 發送簡訊給我 |
粒子系統在2D特效中的應用(全)-------------------------------------------------------------------------------- by: 劉小軍 Last Updated: Mar-22-2001 | First Version:Jan-29-1999 資料來源:http://www.cpp3d.com/downloads/ Particles01源碼 (25K) -------------------------------------------------------------------------------- 粒子系統是啥西?小軍曰:所謂的粒子系統,就是將我們看到的物體運動和自然現象,用一系列運動的粒子來描述,再將這些粒子運動的軌跡映射到顯示屏上,然后呢?就是我們在顯屏上看到的物體運動和自然現象的模擬效果了。 利用粒子系統我們可以在屏幕中表現諸多的特殊效果,如:焰火、火苗、落葉、雪花飛舞等。不怕做不到,就怕想不到。只要你的想象力足夠豐富,你可以創造出意想不到的奇跡來。 好,開始切入我們這章的主題,粒子系統應用的關鍵在于如何描述粒子的運動軌跡,也就是構造粒子的運動函數。函數選擇的恰當與否,決定效果的逼真程度。其次坐標系的選定(就是視角啦)也有一定的關系,視角不同,看到的效果自然不一樣了。在這章里,我會用很多的實例來解釋如何應用粒子系統來實現各種特效。接下來我們要實現的第一種特效就是------ 運動圖標(一) 圖標在這里我們泛指所有的位圖,如團體標識、文字等圖象。這里要實現的是讓圖標從遠處迎面快速飛來這麼一種效果。下面的同學別起哄,我要給你們演示的不是Windows的飛行窗口,它太缺乏速度感了,你們要感受的將是迎面碰撞的效果。下面我們一步一步來實現它: 第一步:構造粒子 一幅位圖可看作是現實世界中的一張相片,我們看到的是它(位圖)在與我們視線垂直的平面(顯屏)上的投影。因此可用{長*寬}個單元的數組來描述,每個單元是一個點的顏色,畫到屏幕上之前先計算好每個點在顯屏上投影的位置就可以了。但我們凡事都要首先考慮一下效率。因為我們的圖標一般都會設一個ColorKey,也就是透明色,位圖中與顏色值與ColorKey相同的點都不需要畫到顯屏上,該顏色在屏幕上看起來就成了透明的。而我們計算各點運動軌跡的計算量可能都不會小,且透明色在整個位圖中占的比例也一般較大,大量透明點參與運算勢必影響程序的整體效率。所以我們要採取內存換效率的辦法,犧牲少量的內存,爭取更高的執行效率。具體辦法是將數組中的透明點拿走,再給非透明點增加一些位置信息。 粒子系的生成過程是:先計算非透明點總數,再為這些點申請內存,最后讀入每個的位置、顏色信息。 第二步:計算粒子在屏幕上的投影位置 我們這里圖標是沿視線方向運動的,所以可讓坐標系的Z軸沿視線方向,X、Y軸與屏幕的X、Y軸相同即可。目標從遠處移近時,眼睛看到是從小到大,不斷加速變大的物體形象。所以可用一個比例因子來描述這種變化。將圖標的運動分成幾個時段,依次在每個時段加大比例因子的增量,粒子的投影坐標就取其實際坐標(也就是其對應于原始位圖的點坐標)與比例因子之積就行了。 第三步:畫粒子 第四步:重複第二、三步過程 以下代碼應該很容易讀懂:// Code in Particles.h class Cparticles { private: struct PARTICLE{ int X0, Y0; // 點在位圖中的坐標 DWORD dwColor; int X, Y; // 點投影到屏幕上的坐標 }; public: Cparticles(); virtual ~Cparticles(); BOOL Create(CDDSurface *pDds); VOID ServeParticles(); VOID Draw(LPDDSURFACEDESC2 pDdsd, int nX=0, int nY=0); protected: DWORD m_dwCnt; PARTICLE *m_pPtc; int m_nScale; }; // Code in Particles.cpp // 生成粒子系。方便起見,取位圖左上角第一個點的顏色值為ColorKey BOOL Cparticles::Create(CDDSurface *pDds) { DWORD dwColorKey; DWORD dwBPP; DWORD dwMask; DDSURFACEDESC2 ddsd; ddsd.dwSize = sizeof(DDSURFACEDESC2); if (pDds->Lock(&ddsd) != DD_OK) return FALSE; dwBPP = ddsd.ddpfPixelFormat.dwRGBBitCount>>3; // Bytes per pixel switch (dwBPP) { case 1: dwMask = 0xFF; break; case 2: dwMask = 0xFFFF; break; case 3: dwMask = 0xFFFFFF; break; case 4: dwMask = 0xFFFFFFFF; } dwColorKey = (*((DWORD *)ddsd.lpSurface)) & dwMask; BYTE *pS = (BYTE *)ddsd.lpSurface; for (DWORD I=0; I小節:這一節我們討論了一個簡單運動圖標的實例,大家對粒子系統應該有一個初步的了解了。如果讓圖標的運動更複雜一些會是什麼效果呢?………… Particles02源碼 (18K) -------------------------------------------------------------------------------- 前面我們的運動圖標應該是粒子系統運用中比較簡單的一種了,這也是我在游戲月亮之子里見到過的一種特效,這種特效是通過一系列的數學運算而產生的動態圖像效果。在特效的實現過程當中還有一種是通過對現實事物的模仿演變而成的。在月亮之子這個游戲中還有一種特效,就是漂浮的煙霧,慢慢成型,演變成精靈圖像。這種特效我與方泓曾討論過多次,盡管我們不知道月亮之子的作者具體是如何實現這一特效的,但我們通過不斷研究學習,覺得其實現原理應該屬繩索模擬的變種,這里我們將學習的一些心得寫出來供大家參考,希望對大家有所幫助。源碼編譯需DX6或以上SDK,VC,DXGuide。 運動圖標(二) 在開始之前,我們先來簡單介紹一下粒子系統中的另一種應用--繩索模擬,比如說我們拽住一根繩索的一端,不停用力抖動,力沿繩索往后傳,于是整根繩索也隨著擺動起來了。繩索可看成是由彈簧連接的若幹粒子組成,這樣就可以通粒子系統來模擬繩索的運動了。繩索模擬在游戲當中有很多的應用,如地蟲阿占手中揮動的啪啪作響的長鞭和劃過夜空的流星等,都可用繩索模擬來實現。但是我們這里並不是要模擬繩索類的東西,而是要描述圖標(或精靈)的變幻運動。它們之間有什麼聯系呢?其實我們可將圖標(或精靈)象素點的行或列看作是一根根獨立的繩索,再讓它們運動起來…… 是不是找到感覺了,好了,我們開始吧!首先我們來構造繩索運動算法,假定繩索是由N個點組成,外力作用在第一個點上,繩索的運動可以這樣描述: 在外力作用下,第一點開始移動,偏移量為(dx0, dy0),其它點不動 在張力的作用下,力由第一點傳到第二點,第二點也開始移動,偏移為(dx0, dy0),另外因外力繼續作用,第一點又偏移(dx1, dy1),其它點不動 力繼續往后傳,第二點的力傳到第三點,第三點偏移(dx0, dy0),第二點偏移(dx1, dy1),同時第一點再次偏移(dx2, dy2),其它點不動 接下來是第四點、第五點等各點依次活動起來,只要外力不撤消,整根繩索就不停運動…… 外力撤消,第一個點首先停止運動,接著第二點、第三點直至所有點都依次停止運動。 這就是一個最簡單的繩索運動過程,這里有很多內在和外在的因素都沒加以考慮,如質量、重力、力衰減等。因為我們要做的僅僅是運動圖標而已,這樣就已經足夠了,下次有時間的話我會作一個更象樣一點的繩索模擬應用,這里暫時就只引入繩索模擬的這個簡單實現方法。以上繩索運動的實現代碼如下: class Cparticle { public: Cparticle(){Next = NULL; xv = 0; yv = 0;}; ~Cparticle(){}; Cparticle *Next; double x, y; double xv, yv; DWORD dwColor; }; class Cstrings { public: Cstrings(); ~Cstrings(); Cparticle *m_pPtcs; //DWORD m_dwCount; virtual void AddPtc(double x, double y, DWORD color); virtual void Move(double dx, double dy); // First particle move to (x dx, y dy) virtual void Draw(LPDDSURFACEDESC2 pdesc); }; Cstrings::Cstrings(): m_pPtcs(NULL) { } Cstrings::~Cstrings() { Cparticle *pPtc; while (m_pPtcs != NULL) { pPtc = m_pPtcs; m_pPtcs = m_pPtcs->Next; delete pPtc; } } void Cstrings::AddPtc(double x, double y, DWORD color) { Cparticle *pPtc = new Cparticle; pPtc->x = x; pPtc->y = y; pPtc->dwColor = color; pPtc->Next = NULL; if (m_pPtcs == NULL) m_pPtcs = pPtc; else { Cparticle *ptc = m_pPtcs; while(ptc->Next!=NULL) { ptc = ptc->Next; } ptc->Next = pPtc; } } void Cstrings::Move(double dx, double dy) { Cparticle *pPtc = m_pPtcs; double x0, y0, x1, y1; x1 = dx; y1 = dy; while (pPtc) { x0 = pPtc->xv; y0 = pPtc->yv; pPtc->xv = x1; pPtc->yv = y1; x1 = x0; y1 = y0; pPtc = pPtc->Next; } pPtc = m_pPtcs; while (pPtc) { pPtc->x = pPtc->xv; pPtc->y = pPtc->yv; pPtc = pPtc->Next; } } void Cstrings::Draw(LPDDSURFACEDESC2 pdesc) { BYTE *pWord; Cparticle *pPtc = m_pPtcs; while(pPtc) { if (pPtc->xdwWidth && pPtc->ydwHeight && pPtc->x>=0 && pPtc->y>=0) { pWord = (BYTE*)pdesc->lpSurface; pWord = (int)pPtc->y*pdesc->lPitch (int)pPtc->x*sizeof(WORD); *((WORD*)pWord) = (WORD)pPtc->dwColor; } pPtc = pPtc->Next; } }繩索運動實現之后,運動圖標也就好實現了,具體過程如下: 將圖標位圖讀入 以每行或每列的象素點作為一條條繩索上的點構造繩索 讓繩索按各自軌跡運動起來(施加外力),顯示出來就形成千變萬化的煙霧了。 讓繩索停下來(撤消外力),則顯現圖標(或精靈)的原形 實現代碼如下: class Clogo { public: Clogo(); ~Clogo(); Cstrings *m_pStr; DWORD m_dwCount; virtual void Create(CDDDIBSurface *pDib); virtual void Init(); virtual void Service(); virtual void Draw(LPDDSURFACEDESC2 pddsd); }; Clogo::Clogo(): m_pStr(NULL), m_dwCount(0) { } Clogo::~Clogo() { if (m_dwCount) { delete [] m_pStr; m_dwCount = 0; } } void Clogo::Create(CDDDIBSurface *pDib) { DDSURFACEDESC2 ddsd; ddsd.dwSize = sizeof(DDSURFACEDESC2); m_dwCount = pDib->GetWidth(); m_pStr = new Cstrings[m_dwCount]; pDib->Lock(&ddsd); for (DWORD I=0; I< pDib->GetHeight(); I ) for (DWORD j=0; j |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |