您的位置:首页 > 其它

粒子系统实现

2013-06-08 17:05 357 查看
使用自定义的2D图形库实现粒子系统

给每个粒子定义属性

typedef struct PARTICLE_TYP
{
int state; // 状态
int type; // 类型
float x,y; // 位置
float xv,yv; // 速度
int curr_color; // 当前颜色
int start_color; //最初颜色
int end_color; //结束颜色
int counter; //生命期
int max_count; //生命上限

int ph3; //占 总大小为64字节
double ph1,ph2; //占位

} PARTICLE, *PARTICLE_PTR;分别实现  粒子初始化  粒子激活  粒子移动

 
然后用ddraw画出来

分别实现了三种粒子形态的模拟  

  1 环形

环形用随机方向但速度相同的粒子实现



 2 随机(模拟爆炸) 

随机就速度和方向都随机



3 心型

心型用数组存储半个心型的速度再对称一次



代码中可以加入重力风力的影响 从而改变速度  更加真实  

再细致一点可以为每个粒子加入碰撞检测 ,粒子碰撞后改变运动状态

其他实现细节看代码就好   有详细注释

#define INITGUID

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <iostream>
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>

#include <ddraw.h>
#include <dsound.h>
#include <dmksctrl.h>
#include <dmusici.h>
#include <dmusicc.h>
#include <dmusicf.h>
#include <dinput.h>

//自定义2D接口
#include "T3DLIB1.h"
#include "T3DLIB2.h"
#include "T3DLIB3.h"

using namespace std;

// 窗口名
#define WINDOW_CLASS_NAME "WINXCLASS" // class name

//窗口大小 1280x800
#define WINDOW_WIDTH 1280 // size of window
#define WINDOW_HEIGHT 800

//发射口位置
#define CANNON_X0 300
#define CANNON_Y0 450

//炮弹数量
#define NUM_PROJECTILES 64

// 粒子状态 当前是否存在
#define PARTICLE_STATE_DEAD 0
#define PARTICLE_STATE_ALIVE 1

// 粒子种类 0 闪烁型 1消失型
#define PARTICLE_TYPE_FLICKER 0
#define PARTICLE_TYPE_FADE 1

// 粒子颜色 8bit 256
#define PARTICLE_COLOR_RED 0
#define PARTICLE_COLOR_GREEN 1
#define PARTICLE_COLOR_BLUE 2
#define PARTICLE_COLOR_WHITE 3

//粒子最大数量
#define MAX_PARTICLES 256

// 颜色范围 某个颜色的区间
#define COLOR_RED_START 32
#define COLOR_RED_END 47

#define COLOR_GREEN_START 96
#define COLOR_GREEN_END 111

#define COLOR_BLUE_START 144
#define COLOR_BLUE_END 159

#define COLOR_WHITE_START 16
#define COLOR_WHITE_END 31

//在(x,y)这个范围里随机的一个
#define RAND_RANGE(x,y) ( (x) + (rand()%((y)-(x)+1)))

float Heart_XV[23] = {0 , 0.7 , 1.5 , 2.3 , 3.5 , 4.1, 5 , 5.5 , 5.7 , 6 , 5.7, 5.5, 5.2, 5 , 4.5, 4.3 , 4 , 3.5, 3 , 2 , 1 , 0.8 , 0 };
float Heart_YV[23] = {-4 , -4.5 , -5.2, -5.5 , -5.7 ,-5.2, -5 , -4 , -3.2 , -2.5 , -1.7, -1 , -0 , 0.8 , 1 , 1.8 , 2 , 3 , 3.5 , 4.5 , 5.5 , 5.8 , 6 };

//炸弹
typedef struct PROJ_TYP
{
int state; //状态 0 为未发射 1为发射中
float x,y; //位置
float xv, yv; //炸弹速度
int detonate; //爆炸倒计时
double placeholder; //占位 总大小32字节
} PROJECTILE, *PROJECTILE_PTR;

//粒子
typedef struct PARTICLE_TYP
{
int state; // 状态
int type; // 类型
float x,y; // 位置
float xv,yv; // 速度
int curr_color; // 当前颜色
int start_color; //最初颜色
int end_color; //结束颜色
int counter; //生命期
int max_count; //生命上限

int ph3; //占 总大小为64字节
double ph1,ph2; //占位

} PARTICLE, *PARTICLE_PTR;

//游戏流水线
int Game_Init(void *parms=NULL);
int Game_Shutdown(void *parms=NULL);
int Game_Main(void *parms=NULL);

//炸弹
void Init_Projectiles(void);
void Move_Projectiles(void);
void Draw_Projectiles(void);
void Fire_Projectile(int angle, float vel);

//粒子
void Init_Reset_Particles(void);
void Draw_Particles(void);
void Move_Particles(void);
void Start_Particle(int type, int color, int count, float x, float y, float xv, float yv);

//随机爆炸
void Start_Particle_Explosion(int type, int color, int count,
int x, int y, int xv, int yv, int num_particles);

//环形
void Start_Particle_Ring(int type, int color, int count,
int x, int y, int xv, int yv, int num_particles);

//心型
void Start_Particle_Heart(int type, int color, int count,int x, int y );

HWND main_window_handle = NULL;
HINSTANCE main_instance = NULL;

BITMAP_IMAGE background_bmp; // 存储背景

int cannon_ids[NUM_PROJECTILES]; // 发射声音
int explosion_ids[NUM_PROJECTILES]; // 爆炸声音

////绘制炮 可以去掉
POLYGON2D cannon; // the ship

//炸弹数量
PROJECTILE missiles[NUM_PROJECTILES]; // array of missiles

//初始的重力 风力 控制炸弹的
float gravity_force = 0.2; // gravity
float wind_force = -0.01; // wind resistance

float particle_gravity = .02; // assume it operates in the Y direction
float particle_wind = 0; // assume it operates in the X direction

//粒子
PARTICLE particles[MAX_PARTICLES];

LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch(msg)
{
case WM_CREATE:
{
return(0);
} break;

case WM_PAINT:
{
hdc = BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
return(0);
} break;

case WM_DESTROY:
{
PostQuitMessage(0);
return(0);
} break;

default:break;
} // end switch
return (DefWindowProc(hwnd, msg, wparam, lparam));
} // end WinProc

//WinMain
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASS winclass; // this will hold the class we create
HWND hwnd; // generic window handle
MSG msg; // generic message
HDC hdc; // generic dc
PAINTSTRUCT ps; // generic paintstruct

winclass.style = CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;

// register the window class
if (!RegisterClass(&winclass))
return(0);

// create the window, note the use of WS_POPUP
if (!(hwnd = CreateWindow( WINDOW_CLASS_NAME,
"粒子系统",
WS_POPUP | WS_VISIBLE,
0,0,
WINDOW_WIDTH,
WINDOW_HEIGHT,
NULL,
NULL,
hinstance,
NULL))
)
return(0);

main_window_handle = hwnd;
main_instance = hinstance;

//整体初始化 , 包括游戏中所有部分的初始化
Game_Init();

//游戏事件循环
while(1)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// test if this is a quit
if (msg.message == WM_QUIT)
break;

// translate any accelerator keys
TranslateMessage(&msg);

// send the message to the window proc
DispatchMessage(&msg);
} // end if

// 游戏逻辑
Game_Main();

} // end while

//退出游戏主循环后
Game_Shutdown();
return(msg.wParam);
} // end WinMain

int Game_Init(void *parms)
{
int index;

char filename[80]; // used to build up filenames

//时间种子 以当前时间为种子
srand(Start_Clock());

// start up DirectDraw (replace the parms as you desire)
DDraw_Init(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP);

//载入背景
Load_Bitmap_File(&bitmap8bit, "night.BMP");
Create_Bitmap(&background_bmp,0,0,640,480);
Load_Image_Bitmap(&background_bmp, &bitmap8bit,0,0,BITMAP_EXTRACT_MODE_ABS);
Set_Palette(bitmap8bit.palette);
Unload_Bitmap_File(&bitmap8bit);

//隐藏鼠标
ShowCursor(FALSE);

//输入初始化,只用键盘
DInput_Init();
DInput_Init_Keyboard();

//声音载入初始化
DSound_Init();

//粒子初始化
Init_Reset_Particles();

//发射声音
cannon_ids[0] = DSound_Load_WAV("CANNON.WAV");

for (index=1; index < NUM_PROJECTILES; index++)
cannon_ids[index] = DSound_Replicate_Sound(cannon_ids[0]);

//载入爆炸声音
explosion_ids[0] = DSound_Load_WAV("EXP1.WAV");

for (index=1; index < NUM_PROJECTILES; index++)
explosion_ids[index] = DSound_Replicate_Sound(explosion_ids[0]);

//建立sin 和 cos 的查找表 在计算角度的时候需要使用
//速度很快
Build_Sin_Cos_Tables();

//初始化炸弹
Init_Projectiles();

//设置裁剪,以屏幕为大小裁剪 超出的部分去掉
RECT screen_rect = {0,0,screen_width,screen_height};
lpddclipper = DDraw_Attach_Clipper(lpddsback,1,&screen_rect);

//设置裁剪边界
min_clip_x = 0;
max_clip_x = screen_width - 1;
min_clip_y = 0;
max_clip_y = screen_height - 1;

return(1);
} //Game_Init()

int Game_Shutdown(void *parms)
{
//释放所有资源

//图形
DDraw_Shutdown();

//声音
DSound_Stop_All_Sounds();
DSound_Shutdown();

//输入
DInput_Shutdown();

return(1);
} //Game_Shutdown

void Init_Reset_Particles(void)
{
//初始化粒子,设定初始状态和值
for (int index=0; index<MAX_PARTICLES; index++)
{
particles[index].state = PARTICLE_STATE_DEAD;
particles[index].type = PARTICLE_TYPE_FADE;
particles[index].x = 0;
particles[index].y = 0;
particles[index].xv = 0;
particles[index].yv = 0;
particles[index].start_color = 0;
particles[index].end_color = 0;
particles[index].curr_color = 0;
particles[index].counter = 0;
particles[index].max_count = 0;
}
} // end Init_Reset_Particles

void Start_Particle(int type, int color, int count, float x, float y, float xv, float yv)
{
//启动单个粒子
//需要设定 类型 颜色 持续时间 位置 速度

int pindex = -1; // index of particle

int index;
//找到一个当前不存在的粒子 将它激活
for (index=0; index < MAX_PARTICLES; index++)
{
if (particles[index].state == PARTICLE_STATE_DEAD)
{
pindex = index;
break;
} // end if
}

//没有可用的就返回
if (pindex==-1)
return;

//设置属性
particles[pindex].state = PARTICLE_STATE_ALIVE;
particles[pindex].type = type;
particles[pindex].x = x;
particles[pindex].y = y;
particles[pindex].xv = xv;
particles[pindex].yv = yv;
particles[pindex].counter = 0;
particles[pindex].max_count = count;

switch(color)
{
case PARTICLE_COLOR_RED:
{
particles[pindex].start_color = COLOR_RED_START;
particles[pindex].end_color = COLOR_RED_END;
} break;

case PARTICLE_COLOR_GREEN:
{
particles[pindex].start_color = COLOR_GREEN_START;
particles[pindex].end_color = COLOR_GREEN_END;
} break;

case PARTICLE_COLOR_BLUE:
{
particles[pindex].start_color = COLOR_BLUE_START;
particles[pindex].end_color = COLOR_BLUE_END;
} break;

case PARTICLE_COLOR_WHITE:
{
particles[pindex].start_color = COLOR_WHITE_START;
particles[pindex].end_color = COLOR_WHITE_END;
} break;

default:break;
} // end switch

//闪烁型
if (type == PARTICLE_TYPE_FLICKER)
{
//设置当前颜色
particles[index].curr_color = RAND_RANGE(particles[index].start_color, particles[index].end_color);
} // end if
else
{
//消失型 一直用原本的颜色直到消失
particles[index].curr_color = particles[index].start_color;
} // end if
} // end Start_Particle

void Start_Particle_Explosion(int type, int color, int count, int x, int y, int xv, int yv, int num_particles)
{
//产生随机的爆炸
while(--num_particles >=0)
{
int ang = rand()%360;

//速度,角度都不同 形成随机爆炸
float vel = 2+rand()%4;
Start_Particle( type,color,count,
x+RAND_RANGE(-4,4),y+RAND_RANGE(-4,4),
xv+cos_look[ang]*vel, yv+sin_look[ang]*vel
);
} // end while
} // end Start_Particle_Explosion

void Start_Particle_Ring(int type, int color, int count, int x, int y, int xv, int yv, int num_particles)
{
//爆炸产生一个环形

//由爆炸的力产生的瞬间速度变化
//所有粒子速度相同 角度不同 形成环形
float vel = 2+rand()%4;
while(--num_particles >=0)
{
//360度上随机的一个方向
int ang = rand()%360;
Start_Particle(type,color,count,
x,y,
xv+cos_look[ang]*vel,
yv+sin_look[ang]*vel);
} // end while

} // end Start_Particle_Ring

void Start_Particle_Heart(int type, int color, int count,int x, int y)
{
//爆炸产生一个心型
for(int index = 0;index < 23 ; index++)
{
Start_Particle(type,color,count,x,y,Heart_XV[index] , Heart_YV[index]);
Start_Particle(type,color,count,x,y,-Heart_XV[index] , Heart_YV[index]);
//
//if(index != 0 && index != 21)
//{
// Start_Particle(type,color,count,x,y,(Heart_XV[index]+Heart_XV[index+1])/2 , (Heart_YV[index] + Heart_YV[index+1])/2);
// Start_Particle(type,color,count,x,y,-(Heart_XV[index]+Heart_XV[index+1])/2 , (Heart_YV[index] + Heart_YV[index+1])/2);
//}
}
}

void Draw_Particles(void)
{
//锁
DDraw_Lock_Back_Surface();

//画活跃的粒子
for (int index=0; index<MAX_PARTICLES; index++)
{
if (particles[index].state==PARTICLE_STATE_ALIVE)
{
//传参数方便
int x = particles[index].x;
int y = particles[index].y;

//如果飞出边界了就不画了
if (x >= SCREEN_WIDTH || x < 0 || y >= SCREEN_HEIGHT || y < 0)
continue;

Draw_Pixel(x,y,particles[index].curr_color, back_buffer, back_lpitch);
} // end if
}
//解锁
DDraw_Unlock_Back_Surface();
} // end Draw_Particles

void Process_Particles(void)
{
//粒子的移动
//扫描当前所有存活的粒子 计算下一帧它的位置
for (int index=0; index<MAX_PARTICLES; index++)
{
if (particles[index].state == PARTICLE_STATE_ALIVE)
{
//位置变动
particles[index].x+=particles[index].xv;
particles[index].y+=particles[index].yv;

//速度变动
particles[index].xv+=particle_wind;
particles[index].yv+=particle_gravity;

//颜色变化
//闪烁型
if (particles[index].type==PARTICLE_TYPE_FLICKER)
{
//从颜色范围里随机挑一个颜色
particles[index].curr_color = RAND_RANGE(particles[index].start_color, particles[index].end_color);

//更新计数 时间到了就消失 设置状态
if (++particles[index].counter >= particles[index].max_count)
particles[index].state = PARTICLE_STATE_DEAD;
} // end if
else
{
//消失型 颜色逐渐变淡 从红色-绿色-蓝色-白色
if (++particles[index].counter >= particles[index].max_count)
{
particles[index].counter = 0;
if (++particles[index].curr_color>particles[index].end_color)
{
//转换完成 粒子消失
particles[index].state = PARTICLE_STATE_DEAD;
} // end if

} // end if
} // end else
} // end if
}
} // end Process_Particles

void Cannon_Sound(void)
{
for (int sound_index=0; sound_index < 8; sound_index++)
{
if (DSound_Status_Sound(cannon_ids[sound_index])==0)
{
DSound_Play(cannon_ids[sound_index]);
break;
} // end if
}
} // end Cannon_Sound

void Explosion_Sound(void)
{
//爆炸声
for (int sound_index=0; sound_index < NUM_PROJECTILES; sound_index++)
{
if (DSound_Status_Sound(explosion_ids[sound_index])==0)
{
DSound_Play(explosion_ids[sound_index]);
break;
} // end if
}
} // end Explosion_Sound

///////////////////////////////////////////////////////////

void Init_Projectiles(void)
{
//给所有炸弹信息赋0
memset(missiles, 0, sizeof(PROJECTILE)*NUM_PROJECTILES);
}

void Move_Projectiles(void)
{
//模拟炸弹的运动轨迹
for (int index=0; index<NUM_PROJECTILES; index++)
{
if (missiles[index].state==1)
{
//速度变化
missiles[index].x+=missiles[index].xv;
missiles[index].y+=missiles[index].yv;

//受力作用 速度变化
missiles[index].xv+=wind_force;
missiles[index].yv+=gravity_force;

//时间到 哄!
if (--missiles[index].detonate <= 0)
{
//1/3 概率产生一个圆
int random = RAND_RANGE(0,3);
//int random=2;
if (random == 0)
{
Start_Particle_Ring( PARTICLE_TYPE_FADE,PARTICLE_COLOR_RED+rand()%4, RAND_RANGE(2,5),
missiles[index].x, missiles[index].y,
0, 0,RAND_RANGE(75,100));
} // end if

else if(random == 1)
{
Start_Particle_Heart( PARTICLE_TYPE_FADE,PARTICLE_COLOR_RED+rand()%4, RAND_RANGE(2,5),
missiles[index].x, missiles[index].y
);
}
//产生一个随机的爆炸
else
{
Start_Particle_Explosion( PARTICLE_TYPE_FADE, PARTICLE_COLOR_RED+rand()%4, RAND_RANGE(2,5),
missiles[index].x, missiles[index].y,
0, 0,RAND_RANGE(20,50));
} // end if

//播放声音
Explosion_Sound();

//爆炸的再次修改状态
missiles[index].state = 0;
} // end if
//飞行过程中出了边界 直接杀死
else
if (missiles[index].x >= screen_width || missiles[index].y >=screen_height || missiles[index].y < 0)
missiles[index].state = 0;
} // end if
}
} // end Move_Projectiles

void Draw_Projectiles(void)
{
for (int index=0; index < NUM_PROJECTILES; index++)
{
if (missiles[index].state==1)
{
//把炸弹的小矩形画到后备表面
Draw_Rectangle(missiles[index].x-2,missiles[index].y-2,
missiles[index].x+2,missiles[index].y+2,
95, lpddsback);
} // end if
}
} // end Draw_Projectiles

void Fire_Projectile(int angle, float vel)
{
//以角度angle 速度vel发射

//在所有炸弹中找到一个没有启动中的然后发射
for (int index=0; index < NUM_PROJECTILES; index++)
{
if (missiles[index].state==0)
{
//初始的位置
missiles[index].x = CANNON_X0;
missiles[index].y = CANNON_Y0;

//以角度修正速度
missiles[index].xv = vel*cos_look[angle];
missiles[index].yv = -vel*sin_look[angle];

//设置飞行时间 结束后爆炸 循环时间和帧速度的乘积 30fps 33ms*(30-45)
missiles[index].detonate = RAND_RANGE(30,45);

//设置为启动的状态 等炸
missiles[index].state = 1;

//嘣!
Cannon_Sound();
break;
} // end if
}
} // end Fire_Projectile

int Game_Main(void *parms)
{
int index; // looping var
//发射角度
const int curr_angle = 90;
//速度
static float curr_vel = 12;

//时钟
Start_Clock();

//给后被缓冲加锁
DDraw_Lock_Back_Surface();

//画背景 8bit 256调色板 纯黑色
Draw_Bitmap(&background_bmp, back_buffer, back_lpitch,0);

//画完背景后解锁
DDraw_Unlock_Back_Surface();

//读取键盘
DInput_Read_Keyboard();

//检测发射 如果按下CTRL则发射
if (keyboard_state[DIK_LCONTROL])
Fire_Projectile(curr_angle, curr_vel);

//炸弹移动 爆炸产生粒子
Move_Projectiles();

//粒子移动 颜色变化
Process_Particles();

//画炸弹
Draw_Projectiles();

//画粒子
Draw_Particles();

//把后备表明推到主表面
DDraw_Flip();

//控制帧速率 30fps
Wait_Clock(33);

//检测推出
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
{
PostMessage(main_window_handle, WM_DESTROY,0,0);
DSound_Stop_All_Sounds();
Screen_Transitions(SCREEN_DARKNESS,NULL,0);
} // end if
return(1);
} // 主逻辑
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  粒子系统