粒子系统实现
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);
} // 主逻辑
给每个粒子定义属性
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);
} // 主逻辑
相关文章推荐
- 粒子系统-烟花效果的实现
- 粒子系统实现与用户交互的特效
- Cocos2d-x 粒子系统----实现下雪效果
- openGL粒子系统实现(面向对象风格)
- 《MFC游戏开发》笔记八 游戏特效的实现(二):粒子系统
- Unity3D游戏开发之粒子系统实现详解
- 一个简单的粒子系统的实现
- 【Visual C++】游戏开发五十 浅墨DirectX教程十八 雪花飞扬:实现唯美的粒子系统
- 基于Direct3D实现简单的粒子系统
- /LGC图形渲染/图形学系列 -- 粒子系统概述及其实现
- opengles实现简单的粒子系统
- 改进粒子系统-GPU实现
- Unity Shader:用几何着色器实现图元转换,细分,以及粒子系统
- 《MFC游戏开发》笔记八 游戏特效的实现(二):粒子系统
- 用DirectX实现粒子系统(三)
- OpenGL粒子系统详解及编程实现
- J2ME 手机游戏粒子系统实现的1个思路
- 第24章 让唯美的雪花飘扬——三维粒子系统的实现
- DirectX学习笔记(十五):粒子系统实现
- Cocos2d-x—粒子系统的实现