【游戏程序设计】完整二维游戏开发-飞机大战

学习了前面的许多知识,现在可以真正做个可以玩的游戏了。

之前是用MFC做的飞机大战,但是有许多的问题。这次我们用Win32程序来开发,所有的代码都是自己写成的,可以控制更多的细节。这次的游戏就没有之前崩溃的问题了,并且绘制血条也很简单,直接GDI绘图完成。具体实现的时候没有用面向对象的思想,完全是面向过程的,感觉自己写的太臃肿了。事后想想如果是大型游戏的话这样肯定不行,还好规模小。先看下效果把。

【游戏程序设计】完整二维游戏开发-飞机大战

游戏多个关卡,多个boss,多种道具,可玩行还可以吧。跟之前的差不太多。

运行环境:vs2010

此游戏为飞机大战,胜利条件为玩家消灭最终boss,失败条件为玩家被消灭。

游戏玩法:

开始界面:点击鼠标进入普通模式,按下空格键进入无敌模式,按下Esc键退出游戏。

游戏界面:玩家鼠标控制飞机的移动与射击,A键发射激光,S键使用防护罩,按下Esc键退出游戏,P键暂停。

结束界面:鼠标点击返回主界面回到开始界面。

主要使用技术的实现方式和对应的代码:

血条:使用GDI绘图函数进行绘制,主要使用了Rectangle函数和CreateSolidBrush。【游戏程序设计】完整二维游戏开发-飞机大战

【游戏程序设计】完整二维游戏开发-飞机大战

镂空贴图:使用了TransparentBlt完成镂空贴图。TransparentBlt在贴图时可以把源位图中的某种颜色设定为透明色,这样与背景融合时就可以将源位图的部分像素隐藏掉。

【游戏程序设计】完整二维游戏开发-飞机大战

位图显示:从文件中加载位图建立与窗口DC兼容的内存DC,内存DC装载创建好的位图对象,将内存DC的内容映射到窗口DC中以完成显像操作

【游戏程序设计】完整二维游戏开发-飞机大战

【游戏程序设计】完整二维游戏开发-飞机大战

【游戏程序设计】完整二维游戏开发-飞机大战

动画:用连续贴图的方法完成动画,通过消息循环的方法

【游戏程序设计】完整二维游戏开发-飞机大战

【游戏程序设计】完整二维游戏开发-飞机大战

【游戏程序设计】完整二维游戏开发-飞机大战

鼠标与键盘交互:通过消息循环实现键盘鼠标与游戏的交互。

【游戏程序设计】完整二维游戏开发-飞机大战

【游戏程序设计】完整二维游戏开发-飞机大战

子弹与道具运动:

运动物体的速度保持不变,每次画面更新,利用物体速度

计算下次物体出现的位置,产生物体移动的效果。

【游戏程序设计】完整二维游戏开发-飞机大战

碰撞检测:框架检测碰撞,使用物体贴图的外围表示方框,

只要检测其中一个矩形的 四个定点是否在另一个矩形内即可。

【游戏程序设计】完整二维游戏开发-飞机大战

源代码:

#include <windows.h>
#include <tchar.h>
#pragma comment(lib, "winmm.lib")									//调用PlaySound函数所需库文件t
#pragma comment(lib, "Msimg32.lib")

#define WINDOW_WIDTH 500
#define WINDOW_HEIGHT 700
#define WINDOW_TITLE L"飞机大战"
const int SceneNum = 5;												//关卡数
const int BulletSpeed = 10;											//子弹速度
const int GoodsSpeed = 6;											//道具速度
const int BulletNum = 3;											//子弹种类
const int LaserNum = 2;												//激光种类
const int MaxEnemyCount = 30;										//最大敌机数目
const int MaxBulletCount = 100;										//最大子弹数目
const int MaxLaserCount = 20;										//最大激光数目
const int MaxExplosionCount = 40;									//最大特效数目
const int MaxBuffTime = 30;											//最大双倍弹药时间
const int MaxProtectedTime = 30;									//最大防护罩时间
const int BossCreateTime = 1;										//boss创建时间
const int BackGroundSpeed = 5;										//背景滚动速度
//玩家弹道间隔距离数组(单倍,双倍弹药)
const int IntervalP[2][10] =										
{
	{19,38},
	{0, 19, 38, 57}
};
//敌机、Boss弹道间隔距离数组
const int IntervalE[6][10] =										
{
	{15,65},
	{35,70,110},
	{15,45,90,120},
	{12,100},
	{15,60,105,150,195,240},
	{16}
};
//飞机
struct Plane
{
	int x, y;												//位置
	int hp, maxHp;											//生命值,最大生命值
	int w, h;												//宽度与长度
	int hBullet;											//子弹
	int hLaser;												//激光
	int LaserNum;											//激光数目
	int ProtectedNum;										//防护罩数目
	bool Protected;											//是否处于防护状态
	bool exist;												//是否存在
	int wait, waitTime;										//发射经过时间,发射间隔时间
	int motionX, motionY;									//每帧移动距离
	int speed;												//速度
	bool buff;												//是否有双倍弹药状态
	int BulletNum;											//同时发射子弹数目
	const int *Interval;									//弹道距离数组
	//初始化
	void Init(int _w, int _h, int _hBullet, int _hLaser,int _waitTime, 
		int _motionX, int _motionY, int _maxHp, int _BulletNum, const int *_Interval)
	{
		w = _w;
		h = _h;
		hBullet = _hBullet;
		hLaser = _hLaser;
		waitTime = _waitTime;
		motionX = _motionX;
		motionY = _motionY;
		exist = false;
		maxHp = _maxHp;
		BulletNum = _BulletNum;
		Interval = _Interval;
	}
	//创建飞机
	void Create(int _x, int _y, int _speed)
	{
		x = _x;
		y = _y;
		hp = maxHp;
		exist = true;
		speed = _speed;
		buff = false;
		Protected = false;
		LaserNum = 0;
		ProtectedNum = 0;
		Protected = false;
		wait = 0;
	}
};
//游戏对象(道具,子弹)
struct Objects
{
	int x, y;														//位置
	int w, h;														//宽度与长度
	int motionY;													//每帧移动距离
	int power;														//伤害
	bool exist;														//是否存在
	//初始化
	void Init(int _w, int _h, int _motionY, int _power)
	{
		w = _w;
		h = _h;
		motionY = _motionY;
		exist = false;
		power = _power;
	}
	//创建对象
	void Create(int _x, int _y)
	{
		x = _x;
		y = _y;
		exist = true;
	}
};
//爆炸特效
struct Explosion
{
	int x, y;													//位置
	int w, h;													//宽度与长度
	int iNum;													//爆炸图片索引
	bool exist;													//是否存在
	//初始化
	void Init(int _w, int _h)
	{
		w = _w;
		h = _h;
		exist = false;
	}
	//创建对象
	void Create(int _x, int _y)
	{
		iNum = 0;
		x = _x;
		y = _y;
		exist = true;
	}
};

HINSTANCE hInst;
HDC hdc, mdc, bufdc;
bool start, over, super, pause;																	//是否开始,结束,无敌模式,暂停
int scene, score;																				//场景数,分数
HBITMAP hStart, hOver, hScore[SceneNum], hScene[SceneNum], hBullet[BulletNum], hExplosion, hGetHp, hPlus, 
hGetLaser, hGetBuff, hGetProtect, hLaser[LaserNum], hPlayer, hProtect, hBoss[SceneNum], hEnemy, hReturn;
HFONT hFont[2];											
HBRUSH hBrush;
DWORD g_tNow, g_tPre;
HWND hwnd;
Plane player, enemy[MaxEnemyCount], boss[SceneNum];												//玩家,敌机,boss
//子弹,激光,游戏道具(恢复生命,双倍弹药,防护罩,激光武器)
Objects bullet[BulletNum][MaxBulletCount], laser[LaserNum][MaxLaserCount], getHp, getBuff, getProtect, getLaser; 
Explosion explosion[MaxExplosionCount];															//爆炸特效
int g_iBGOffset;																				//背景滚动量
int ProtectedTime, BuffTime, BossTime;								//防护罩、双倍弹药存在时间,Boss未产生时间
RECT rect;																						//获取客户区窗口	

int  MyWindowClass(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void PlaneGame(HWND);
void MyDraw(HDC);
void StartGame();
void OverGame();
void Clear();
void AI_Motion();
void AI_Create();
void AI_Fire(Plane &);
void CollisionDetection();
void CreateExplosion(int, int);
bool isCollision(int, int, int, int, int, int, int, int);
void LoadMusic();

/*****************************************************************************************************************
在不同的应用程序中,在此处添加相关的全局变量
******************************************************************************************************************/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstace,
									LPSTR lpCmdLine, int nCmdShow) 
{
	MyWindowClass(hInstance);

	if(!InitInstance(hInstance, nCmdShow))
		return FALSE;
	MSG msg = {0};														//初始化
	while(msg.message != WM_QUIT) 
	{
		//检测目前是否有需要处理的消息,有返回非0,否则返回0
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			g_tNow = GetTickCount();									//获取当前系统时间
			if(g_tNow - g_tPre >= 40)									//当此次循环运行与上次绘图时间相差0.1s进行重绘操作
			{
				PlaneGame(hwnd);
				g_tPre = GetTickCount();								//记录此次绘图时间,供下次游戏循环中判断是否达到画面更新操作
			}
		}	
	}

	return msg.wParam;
}

int MyWindowClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = (HICON)LoadImage(hInstance, L"Textures\\icon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = L"gamebase";
	wcex.hIconSm = NULL;

	return RegisterClassEx(&wcex); 
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	hInst = hInstance;
	hwnd = CreateWindow(L"gamebase", WINDOW_TITLE, WS_OVERLAPPEDWINDOW, 
		CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
	if(!hwnd)
		return FALSE;
	MoveWindow(hwnd, 400, 10, WINDOW_WIDTH, WINDOW_HEIGHT, true);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	LoadMusic();																											//加载音乐资源
	mciSendString(L"play bgm repeat", NULL, 0, NULL);																		//重复播放背景音乐
	hdc = GetDC(hwnd);
	mdc = CreateCompatibleDC(hdc);
	bufdc = CreateCompatibleDC(hdc);

	//位图的加载
	wchar_t filename[50] = {};
	hStart = (HBITMAP)LoadImage(NULL, L"Textures\\start.bmp", IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT, LR_LOADFROMFILE);	//开始界面
	hOver = (HBITMAP)LoadImage(NULL, L"Textures\\over.bmp", IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT, LR_LOADFROMFILE);	//结束界面
	hBoss[0] = (HBITMAP)LoadImage(NULL, L"Textures\\boss0.bmp", IMAGE_BITMAP, 88, 50, LR_LOADFROMFILE);						//boss
	hBoss[1] = (HBITMAP)LoadImage(NULL, L"Textures\\boss1.bmp", IMAGE_BITMAP, 151, 94, LR_LOADFROMFILE);  
	hBoss[2] = (HBITMAP)LoadImage(NULL, L"Textures\\boss2.bmp", IMAGE_BITMAP, 134, 91, LR_LOADFROMFILE);	
	hBoss[3] = (HBITMAP)LoadImage(NULL, L"Textures\\boss3.bmp", IMAGE_BITMAP, 126, 106, LR_LOADFROMFILE);
	hBoss[4] = (HBITMAP)LoadImage(NULL, L"Textures\\boss4.bmp", IMAGE_BITMAP, 260, 197, LR_LOADFROMFILE);
	hBullet[0] = (HBITMAP)LoadImage(NULL, L"Textures\\bullet0.bmp", IMAGE_BITMAP, 21, 50, LR_LOADFROMFILE);					//子弹
	hBullet[1] = (HBITMAP)LoadImage(NULL, L"Textures\\bullet1.bmp", IMAGE_BITMAP, 20, 23, LR_LOADFROMFILE);
	hBullet[2] = (HBITMAP)LoadImage(NULL, L"Textures\\bullet2.bmp", IMAGE_BITMAP, 8, 8, LR_LOADFROMFILE);
	hExplosion = (HBITMAP)LoadImage(NULL, L"Textures\\explosion.bmp", IMAGE_BITMAP, 528, 66, LR_LOADFROMFILE);				//爆炸特效
	hGetHp = (HBITMAP)LoadImage(NULL, L"Textures\\getHp.bmp", IMAGE_BITMAP, 40, 34, LR_LOADFROMFILE);						//恢复生命值(道具)
	hGetLaser = (HBITMAP)LoadImage(NULL, L"Textures\\getLaser.bmp", IMAGE_BITMAP, 60, 56, LR_LOADFROMFILE);					//激光武器(道具)
	hGetBuff = (HBITMAP)LoadImage(NULL, L"Textures\\getBuff.bmp", IMAGE_BITMAP, 84, 52, LR_LOADFROMFILE);					//双倍弹药(道具)
	hGetProtect = (HBITMAP)LoadImage(NULL, L"Textures\\getProtect.bmp", IMAGE_BITMAP, 34, 32, LR_LOADFROMFILE);				//防护罩(道具)
	hLaser[0] = (HBITMAP)LoadImage(NULL, L"Textures\\laser0.bmp", IMAGE_BITMAP, 40, 500, LR_LOADFROMFILE);					//激光
	hLaser[1] = (HBITMAP)LoadImage(NULL, L"Textures\\laser1.bmp", IMAGE_BITMAP, 50, 500, LR_LOADFROMFILE);  
	hPlayer = (HBITMAP)LoadImage(NULL, L"Textures\\player.bmp", IMAGE_BITMAP, 75, 60, LR_LOADFROMFILE);						//玩家
	hProtect = (HBITMAP)LoadImage(NULL, L"Textures\\protect.bmp", IMAGE_BITMAP, 100, 100, LR_LOADFROMFILE);					//防护罩
	hEnemy = (HBITMAP)LoadImage(NULL, L"Textures\\enemy.bmp", IMAGE_BITMAP, 70, 35, LR_LOADFROMFILE);						//敌机
	hReturn = (HBITMAP)LoadImage(NULL, L"Textures\\return.bmp", IMAGE_BITMAP, 200, 39, LR_LOADFROMFILE);					//返回主菜单
	hPlus = (HBITMAP)LoadImage(NULL, L"Textures\\plus.bmp", IMAGE_BITMAP, 46, 34, LR_LOADFROMFILE);							//+号
	for(int i = 0; i < SceneNum; ++i)
	{
		//加载场景、评分等级位图
		swprintf_s(filename, L"Textures\\scene%d.bmp", i);
		hScene[i] = (HBITMAP)LoadImage(NULL, filename, IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT, LR_LOADFROMFILE);
		swprintf_s(filename, L"Textures\\score%d.bmp", i);
		hScore[i] = (HBITMAP)LoadImage(NULL, filename, IMAGE_BITMAP, 100, 100, LR_LOADFROMFILE);
	}
	HBITMAP bmp = CreateCompatibleBitmap(hdc, WINDOW_WIDTH, WINDOW_HEIGHT);
	SelectObject(mdc, bmp);
	//字体
	hFont[0] = CreateFont(70,30, 0,0,0,0,0,0,GB2312_CHARSET,0,0,0,0,TEXT("微软雅黑"));
	hFont[1] = CreateFont(20,0, 0,0,0,0,0,0,GB2312_CHARSET,0,0,0,0,TEXT("微软雅黑"));
	//画刷
	hBrush = CreateSolidBrush(RGB(255,0,0));

	SetBkMode(mdc, TRANSPARENT);
	GetClientRect(hwnd, &rect);
	//游戏对象的初始化
	player.Init(75, 60, 0, 1, 0,  0, 0, 200, 2, IntervalP[0]);						//玩家
	boss[0].Init(88, 50, 2, -1, 20, 1, 1, 88, 2, IntervalE[0]);						//boss
	boss[1].Init(151, 94, 2, -1,  10, 1, 1, 151, 3, IntervalE[1]);
	boss[2].Init(134, 91, 2, -1,   5, 1, 1, 134, 4, IntervalE[2]);
	boss[3].Init(126, 106, 1, -1, 10, 1, 1, 126, 2, IntervalE[3]);
	boss[4].Init(260, 197, 2, 0,  3, 1, 1, 260, 6, IntervalE[4]);
	for(int i = 0; i < MaxEnemyCount; ++i)											//敌机
		enemy[i].Init(35, 35, 2, -1, 20, 0, 1, 1, 1, IntervalE[5]);
	for(int i = 0; i < MaxBulletCount; ++i)											//子弹
	{
		bullet[0][i].Init(21, 50, -1, 1);
		bullet[1][i].Init(20, 23, 1, 6);
		bullet[2][i].Init(8, 8, 1, 2);
	}
	for(int i = 0; i < MaxLaserCount; ++i)											//激光
	{
		laser[0][i].Init(40, 500, 1, 1);
		laser[1][i].Init(50, 500, -1, 1);
	}
	for(int i = 0; i < MaxExplosionCount; ++i)										//爆炸特效
		explosion[i].Init(66, 66);
	//游戏道具
	getHp.Init(40, 34, 1, 0);
	getProtect.Init(34, 32, 1, 0);
	getBuff.Init(84, 52, 1, 0);
	getLaser.Init(60, 56, 1, 0);

	start = false;
	over = false;

	return TRUE;
}
//飞机大战游戏主函数
void PlaneGame(HWND hwnd)
{
	if(!start)				
		StartGame();												//游戏没有开始,绘制开始界面
	else if(over)													//游戏结束,绘制结束界面
		OverGame();
	else
	{
		if(pause)													//暂停,直接退出
			return;
		MyDraw(hdc);					
		AI_Motion();
		AI_Create();
		for(int i = 0; i < MaxEnemyCount; ++i)
			AI_Fire(enemy[i]);
		for(int i = 0; i < SceneNum; ++i)
			AI_Fire(boss[i]);
		CollisionDetection();
	}

	BitBlt(hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, mdc, 0, 0, SRCCOPY);
}
//开始界面绘制
void StartGame()
{
	SelectObject(bufdc, hStart);
	BitBlt(mdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, bufdc, 0, 0, SRCCOPY);

	SelectObject(mdc, hFont[0]);
	SetTextColor(mdc, RGB(255, 255, 255));
	TextOut(mdc, 120, 250, L"飞机大战", 4);
	
	SelectObject(mdc, hFont[1]);
	SetTextColor(mdc, RGB(84, 142, 239));
	TextOut(mdc, 170, 400, L"点击鼠标左键进入普通模式", 12);
	TextOut(mdc, 170, 420, L"按下空格键进入无敌模式", 11);
	TextOut(mdc, 170, 440, L"按下Esc键退出", 8);
	//重置全局变量
	super = false;
	pause = false;
	score = 0;
	scene = 0;
	g_iBGOffset = 0;
	BuffTime = MaxBuffTime;
	ProtectedTime = MaxProtectedTime;
	BossTime = 0;
	ShowCursor(false);     
	SetCursorPos(600, 420);													//限制鼠标位置
}
//清空游戏对象
void Clear()
{
	player.exist = false;
	for(int i = 0; i < SceneNum; ++i)
		boss[i].exist = false;
	for(int i = 0; i < MaxEnemyCount; ++i)
		enemy[i].exist = false;
	for(int i = 0; i < BulletNum; ++i)
		for(int j = 0; j < MaxBulletCount; ++j)
			bullet[i][j].exist = false;
	for(int i = 0; i < LaserNum; ++i)
		for(int j = 0; j < MaxLaserCount; ++j)
			laser[i][j].exist = false;
	for(int i = 0; i < MaxExplosionCount; ++i)
		explosion[i].exist = false;
	//清空定时器
	KillTimer(hwnd, 1);
	KillTimer(hwnd, 2);
	KillTimer(hwnd, 3);
	KillTimer(hwnd, 4);
	//初始化玩家子弹状态
	player.BulletNum = 2;
	player.Interval = IntervalP[0];
}
//结束画面绘制
void OverGame()
{
	SelectObject(bufdc, hOver);
	BitBlt(mdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, bufdc, 0, 0, SRCCOPY);
	SelectObject(bufdc, hScore[scene]);
	TransparentBlt(mdc, 200, 100, 100, 100, bufdc, 0, 0, 100, 100, RGB(255,255,255));
	SelectObject(bufdc, hReturn);
	TransparentBlt(mdc, 130, 400, 200, 39, bufdc, 0, 0, 200, 39, RGB(255,255,255));
	//如果玩家通关,绘制+号
	if(scene == SceneNum-1 && player.hp > 0)
	{
		SelectObject(bufdc, hPlus);
		TransparentBlt(mdc, 300, 100, 46, 34, bufdc, 0, 0, 46, 34, RGB(255,255,255));
	}

	SelectObject(mdc, hFont[0]);
	SetTextColor(mdc, RGB(255, 255, 0));
	if(scene == SceneNum-1 && player.hp > 0)
		TextOut(mdc, 20, 220, L"恭喜您获得胜利", 7);
	else
		TextOut(mdc, 100, 220, L"再接再厉", 4);
	wchar_t name[20] = {};
	swprintf_s(name, L"获得积分:%d", score);
	TextOut(mdc, 20, 280, name, wcslen(name));

	Clear();
	//显示鼠标光标
	ShowCursor(true);
}
//---------------------------------------------【LoadMusic()()函数】-----------------------------------------
//描述:进行游戏音乐资源的加载
//---------------------------------------------------------------------------------------------------------------
void LoadMusic()
{
	mciSendString(L"open Musics\\background.mp3 alias bgm", NULL, 0, NULL);
	mciSendString(L"open Musics\\dead.mp3 alias dead", NULL, 0, NULL);
	mciSendString(L"open Musics\\getObject.mp3 alias getObject", NULL, 0, NULL);
	mciSendString(L"open Musics\\victory.mp3 alias victory", NULL, 0, NULL);
	mciSendString(L"open Musics\\boom.wav alias boom", NULL, 0, NULL);
	mciSendString(L"open Musics\\laser.wav alias laser", NULL, 0, NULL);
}

//游戏对象AI:移动
void AI_Motion()
{
	for(int i = 0; i < SceneNum; ++i)
		if(boss[i].exist)
		{
			boss[i].x += rand()%2 * boss[i].motionX;
			boss[i].y += rand()%2 * boss[i].motionY;
			//如果越界,就销毁
			if(boss[i].x < 0)
			{
				boss[i].x = 0;
				boss[i].motionX = -boss[i].motionX;
			}
			else if(boss[i].x > rect.right-boss[i].w)
			{
				boss[i].x = rect.right-boss[i].w;
				boss[i].motionX = -boss[i].motionX;
			}
			if(boss[i].y < 0)
			{
				boss[i].y = 0;
				boss[i].motionY = -boss[i].motionY;
			}
			else if(boss[i].y > rect.bottom-boss[i].h)
			{
				boss[i].y = rect.bottom-boss[i].h;
				boss[i].motionY = -boss[i].motionY;
			}
		}

	for(int i = 0; i < MaxEnemyCount; ++i)
		if(enemy[i].exist)
		{
			enemy[i].y +=  enemy[i].speed * enemy[i].motionY;
			if(enemy[i].y >  rect.bottom - enemy[i].h)
				enemy[i].exist = false;
		}
			
	for(int i = 0; i < BulletNum; ++i)
		for(int j = 0; j < MaxBulletCount; ++j)
			if(bullet[i][j].exist)
			{
				bullet[i][j].y += BulletSpeed * bullet[i][j].motionY;
				if(bullet[i][j].y < 0 || bullet[i][j].y > rect.bottom - bullet[i][j].h)
					bullet[i][j].exist = false;
			}
	for(int i = 0; i < LaserNum; ++i)
		for(int j = 0; j < MaxLaserCount; ++j)
			if(laser[i][j].exist)
			{
				laser[i][j].y += BulletSpeed * laser[i][j].motionY;
				if(laser[i][j].y < -laser[i][j].h || laser[i][j].y > rect.bottom)
					laser[i][j].exist = false;
			}
	if(getProtect.exist)
	{
		getProtect.y += GoodsSpeed;
		if(getProtect.y > rect.bottom)
			getProtect.exist = false;
	}
	if(getBuff.exist)
	{
		getBuff.y += GoodsSpeed;
		if(getBuff.y > rect.bottom)
			getBuff.exist = false;
	}
	if(getHp.exist)
	{
		getHp.y += GoodsSpeed;
		if(getHp.y > rect.bottom)
			getHp.exist = false;
	}
	if(getLaser.exist)
	{
		getLaser.y += GoodsSpeed;
		if(getLaser.y > rect.bottom)
			getLaser.exist = false;
	}
}
//游戏对象AI:创建
void AI_Create()
{
	//数字越大,几率越小
	static int EnemyCreate = rand() % 5 + 10;											//有关敌人生成随机数字
	static int ProtectCreate = rand() % 900 + 1400;										//有关防护罩(道具)生成随机数字
	static int BuffCreate = rand() % 700 + 1200;										//有关双倍弹药(道具)生成随机数字
	static int HpCreate = rand() % 700 + 1200;											//有关恢复生命(道具)生成随机数字
	static int LaserCreate = rand() % 900 + 1400;										//有关激光(道具)生成随机数字

	--EnemyCreate;
	if(EnemyCreate <= 0)
	{
		EnemyCreate = rand() % 5 + 10;
		for(int i = 0; i < MaxEnemyCount; ++i)
			if(!enemy[i].exist)
			{
				int x = rand() % rect.right;
				int speed = rand() % 5 + BackGroundSpeed;
				enemy[i].Create(x, 0, speed);
				break;
			}
	}
	--ProtectCreate;
	if(ProtectCreate <= 0)
	{
		int x = rand() % rect.right;
		ProtectCreate = rand() % 900 + 1400;
		getProtect.Create(x, 0);
	}

	--BuffCreate;
	if(BuffCreate <= 0)
	{
		int x = rand() % rect.right;
		BuffCreate = rand() % 700 + 1200;
		getBuff.Create(x, 0);
	}

	--HpCreate;
	if(HpCreate <= 0)
	{
		int x = rand() % rect.right;
		HpCreate = rand() % 700 + 1200;
		getHp.Create(x, 0);
	}

	--LaserCreate;
	if(LaserCreate <= 0)
	{
		int x = rand() % rect.right;
		LaserCreate = rand() % 900 + 1400; 
		getLaser.Create(x, 0);
	}
}
//游戏对象AI:发射子弹与激光
void AI_Fire(Plane &p)
{
	int cnt = 0;
	int bul = p.hBullet;
	if(p.exist)
	{
		//如果玩家在敌人攻击范围内
		if(player.x > p.x-player.w && player.x < p.x+p.w && !p.wait)
		{
			for(int j = 0; j < MaxBulletCount; ++j)
				if(!bullet[bul][j].exist)
				{
					bullet[bul][j].Create(p.x+ p.Interval[cnt], p.y+p.h);
					++cnt;
					if(cnt == p.BulletNum)										//子弹数到了最大同时发射子弹数目
						break;
				}
		}

		if(p.hLaser>=0 && player.x > p.x+100 && player.x < p.x+150 && !p.wait)
		{
			int las = p.hLaser;
			for(int j = 0; j < MaxLaserCount; ++j)
				if(!laser[las][j].exist)
				{
					laser[las][j].Create(p.x+110, p.y+p.h);
					break;
				}
		}

		++p.wait;												//增加冷却时间
		if(p.wait == p.waitTime)								//冷却时间已到
			p.wait = 0;
	}
}

/***************************************************************************************************************
在函数MyDraw()中进行相关绘制工作
****************************************************************************************************************/
void MyDraw(HDC hdc)
{
	//背景绘制
	SelectObject(bufdc, hScene[scene]);
	BitBlt(mdc, 0, 0, WINDOW_WIDTH, g_iBGOffset, bufdc, 0, WINDOW_HEIGHT-g_iBGOffset, SRCCOPY);
	BitBlt(mdc, 0, g_iBGOffset, WINDOW_WIDTH, WINDOW_HEIGHT-g_iBGOffset, bufdc, 0, 0, SRCCOPY);
	g_iBGOffset += BackGroundSpeed;
	if(g_iBGOffset >= WINDOW_HEIGHT)
		g_iBGOffset =  0;
	//玩家绘制
	if(player.exist)
	{
		SelectObject(bufdc, hPlayer);
		TransparentBlt(mdc, player.x, player.y, player.w, player.h,		
											bufdc, 0, 0, player.w, player.h, RGB(255,255,255));
	}
	//boss绘制
	for(int i = 0; i < SceneNum; ++i)
		if(boss[i].exist)
		{
			SelectObject(bufdc, hBoss[i]);
			TransparentBlt(mdc, boss[i].x, boss[i].y, boss[i].w, boss[i].h, 
											bufdc, 0, 0, boss[i].w, boss[i].h, RGB(255,255,255));
			//boss血条的绘制
			SelectObject(mdc, hBrush);
			Rectangle(mdc, boss[i].x, boss[i].y+boss[i].h, boss[i].x+boss[i].hp,  boss[i].y+boss[i].h+5*(i+1));
		}
	//敌机绘制
	SelectObject(bufdc, hEnemy);
	for(int i = 0; i < MaxEnemyCount; ++i)
		if(enemy[i].exist)
			TransparentBlt(mdc, enemy[i].x, enemy[i].y, enemy[i].w, enemy[i].h, 
													bufdc, 0, 0, enemy[i].w, enemy[i].h, RGB(0,0,0));
	//子弹绘制
	for(int i = 0; i < BulletNum; ++i)
	{
		SelectObject(bufdc, hBullet[i]);
		for(int j = 0; j < MaxBulletCount; ++j)
			if(bullet[i][j].exist)
			{
				if(i == 2)
				TransparentBlt(mdc, bullet[i][j].x, bullet[i][j].y, bullet[i][j].w, bullet[i][j].h, 
										bufdc, 0, 0, bullet[i][j].w, bullet[i][j].h, RGB(0,0,0));
				else
					TransparentBlt(mdc, bullet[i][j].x, bullet[i][j].y, bullet[i][j].w, bullet[i][j].h, 
										bufdc, 0, 0, bullet[i][j].w, bullet[i][j].h, RGB(255,255,255));
			}			
	}
	//激光绘制
	for(int i = 0; i < LaserNum; ++i)
	{
		SelectObject(bufdc, hLaser[i]);
		for(int j = 0; j < MaxLaserCount; ++j)
			if(laser[i][j].exist)
				TransparentBlt(mdc, laser[i][j].x, laser[i][j].y, laser[i][j].w, laser[i][j].h, 
											bufdc, 0, 0, laser[i][j].w, laser[i][j].h, RGB(255,255,255));
	}
	//爆炸特效绘制
	SelectObject(bufdc, hExplosion);
	for(int i = 0; i < MaxExplosionCount; ++i)
		if(explosion[i].exist)
		{
			TransparentBlt(mdc, explosion[i].x, explosion[i].y, explosion[i].w, explosion[i].h, 
				bufdc, explosion[i].iNum*explosion[i].w, 0, explosion[i].w, explosion[i].h, RGB(0,0,0));
			++explosion[i].iNum;
			if(explosion[i].iNum == 8)
				explosion[i].exist = false;
		}
	//恢复生命(道具)绘制
	if(getHp.exist)
	{
		SelectObject(bufdc, hGetHp);
		TransparentBlt(mdc, getHp.x, getHp.y, getHp.w, getHp.h, bufdc, 0, 0, getHp.w, getHp.h, RGB(255,255,255));
	}
	//双倍弹药(道具)绘制
	if(getBuff.exist)
	{
		SelectObject(bufdc, hGetBuff);
		TransparentBlt(mdc, getBuff.x, getBuff.y, getBuff.w, getBuff.h, 
								bufdc, 0, 0, getBuff.w, getBuff.h, RGB(255,255,255));
	}
	//激光武器(道具)绘制
	if(getLaser.exist)
	{
		SelectObject(bufdc, hGetLaser);
		TransparentBlt(mdc, getLaser.x, getLaser.y, getLaser.w, getLaser.h, 
								bufdc, 0, 0, getLaser.w, getLaser.h, RGB(255,255,255));
	}
	//防护罩(道具)绘制
	if(getProtect.exist)
	{
		SelectObject(bufdc, hGetProtect);
		BitBlt(mdc, getProtect.x, getProtect.y, getProtect.w, getProtect.h, bufdc, 0, 0, SRCCOPY);
	}
		
	wchar_t name[20] = {};
	//文字描述绘制
	SetTextColor(mdc, RGB(255,0,0));
	SelectObject(mdc, hFont[1]);
	TextOut(mdc, 0, 0, L"HP", 2);
	swprintf_s(name, L"获得积分:%d", score);
	TextOut(mdc, 0, 30, name, wcslen(name));
	swprintf_s(name, L"防护罩数目:%d", player.ProtectedNum);
	TextOut(mdc, 0, 60, name, wcslen(name));
	swprintf_s(name, L"激光数目:%d", player.LaserNum);
	TextOut(mdc, 0, 90, name, wcslen(name));
	TextOut(mdc, 0, 580, L"暂停/开始(P键)", 9);
	TextOut(mdc, 0, 600, L"激光(A键)", 6);
	TextOut(mdc, 0, 620, L"防护罩(S键)", 7);
	TextOut(mdc, 0, 640, L"退出(Esc键)", 8);
	//防护罩绘制
	if(player.Protected)
	{
		swprintf_s(name, L"防护罩时间:%d", ProtectedTime);
		TextOut(mdc, 0, 120, name, wcslen(name));
		SelectObject(bufdc, hProtect);
		TransparentBlt(mdc, player.x-13, player.y-13, 100, 100, bufdc, 0, 0, 100, 100, RGB(0,0,0));
	}
	if(player.buff)
	{
		swprintf_s(name, L"双倍弹药时间:%d", BuffTime);
		TextOut(mdc, 0, 150, name, wcslen(name));
	}
	//玩家血条绘制
	SelectObject(mdc, hBrush);
	Rectangle(mdc, 20, 5, 20 + player.hp, 20);
}
//位置在x1,y1,宽度w1,长度h1的矩形,与位置在x2,y2,宽度w2,长度h2的矩形,是否发生碰撞
bool isCollision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
{
	return x1 > x2-w1 && x1 < x2+w2 && y1 > y2-h1 && y1 < y2+h2;
}
//在x,y位置产生爆炸特效
void CreateExplosion(int x, int y)
{
	for(int k = 0; k < MaxExplosionCount; ++k)
		if(!explosion[k].exist)
		{
			explosion[k].Create(x, y); 
			break;
		}
	//mciSendString(L"play boom from 0", NULL, 0, NULL);
}
//攻击敌人
//子弹bul,las为是否是激光
void AttackToEnemy(Objects &bul, bool las)
{
	for(int i = 0; i < MaxEnemyCount; ++i)
		if(enemy[i].exist && isCollision(bul.x, bul.y, bul.w,bul.h, enemy[i].x, enemy[i].y ,enemy[i].w, enemy[i].h))
			{
				CreateExplosion(enemy[i].x, enemy[i].y);
				if(!las)
					bul.exist = false;
				enemy[i].exist = false;
				score += 10;																//分数增加
			}
	for(int i = 0; i < SceneNum; ++i)
		if(boss[i].exist && isCollision(bul.x, bul.y,bul.w,bul.h, boss[i].x, boss[i].y ,boss[i].w, boss[i].h))
		{
			if(!las)
				CreateExplosion(bul.x, bul.y-bul.h/2);
			else
				CreateExplosion(bul.x, boss[i].y+boss[i].h/2);
			if(!las)
				bul.exist = false;
			boss[i].hp -= bul.power;														//boss受伤
			if(boss[i].hp <= 0)																//boss死亡
			{
				boss[i].exist = false;
				score += (i+1)*100;  
				if(i == SceneNum-1)															//如果是最后一关,游戏结束
				{
					//播放胜利音效
					mciSendString(L"stop bgm", NULL, 0, NULL);
					mciSendString(L"play victory from 0", NULL, 0, NULL);
					over = true;
					return;
				}
				if(i == scene)																//如果是本关的boss,通往下一关
				{
					++scene;
					BossTime = 0;
					SetTimer(hwnd, 2, 30000, NULL);											//设置boss生成定时器
				}
			}
		}
}
//攻击玩家
//子弹bul,las为是否是激光
void AttackToPlayer(Objects &bul, bool las)
{
	if(isCollision(bul.x, bul.y, bul.w, bul.h, player.x, player.y ,player.w, player.h))
	{
		if(!las)
			CreateExplosion(bul.x-33, bul.y);
		else
			CreateExplosion(bul.x, player.y);
		if(!las)
			bul.exist = false;
		if(!player.Protected)																	//玩家受伤
			player.hp -= bul.power;
		if(player.hp <= 0)																		//玩家死亡,游戏结束
		{
			player.exist = false;
			over = true;
			//播放死亡音效
			mciSendString(L"stop bgm", NULL, 0, NULL);
			mciSendString(L"play dead from 0", NULL, 0, NULL);
		}
	}
}
//碰撞检测:
void CollisionDetection()
{
	//子弹与飞机碰撞
	for(int i = 0; i < BulletNum; ++i)
		for(int j = 0; j < MaxBulletCount; ++j)
			if(bullet[i][j].exist)
			{
				if(i == player.hBullet)
					AttackToEnemy(bullet[i][j], false);
				else
					AttackToPlayer(bullet[i][j], false);
			}
	//激光与飞机碰撞
	for(int i = 0; i < LaserNum; ++i)
		for(int j = 0; j < MaxLaserCount; ++j)
			if(laser[i][j].exist)
			{
				if(i == player.hLaser)
					AttackToEnemy(laser[i][j], true);
				else
					AttackToPlayer(laser[i][j], true);
			}
	//获得防护罩(道具)
	if(getProtect.exist && isCollision(getProtect.x, getProtect.y, getProtect.w, getProtect.h,
		player.x, player.y ,player.w, player.h))
	{
		++player.ProtectedNum;
		getProtect.exist = false;
		//播放获得道具的音效
		mciSendString(L"play getObject from 0", NULL, 0, NULL);
	}
	//获得激光武器(道具)
	if(getLaser.exist && isCollision(getLaser.x, getLaser.y, getLaser.w, getLaser.h,
		player.x, player.y ,player.w, player.h))
	{
		++player.LaserNum;
		getLaser.exist = false;
		//播放获得道具的音效
		mciSendString(L"play getObject from 0", NULL, 0, NULL);
	}
	//获得双倍弹药(道具)	
	if(getBuff.exist && isCollision(getBuff.x, getBuff.y, getBuff.w, getBuff.h,
		player.x, player.y ,player.w, player.h))
	{
		if(!super)
		{
			//如果不是无敌状态,改变弹道距离与最大同时发射子弹数目,重置双倍弹药时间,设置定时器
			player.buff = true;
			player.BulletNum = 4;
			player.Interval = IntervalP[1];
			BuffTime = MaxBuffTime;
			SetTimer(hwnd, 4, 1000, NULL);
		}
		getBuff.exist = false;
		//播放获得道具的音效
		mciSendString(L"play getObject from 0", NULL, 0, NULL);
	}
	//获得恢复生命(道具)
	if(getHp.exist && isCollision(getHp.x, getHp.y, getHp.w, getHp.h,
		player.x, player.y ,player.w, player.h))
	{
		//恢复满HP
		player.hp = player.maxHp;
		getHp.exist = false;
		//播放获得道具的音效
		mciSendString(L"play getObject from 0", NULL, 0, NULL);
	}
			
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int x, y, cnt, bul;
	switch(message)
	{
	//时间消息
	case WM_TIMER:
		if(pause)														//暂停,不处理消息
			return true;
		switch(wParam)
		{
		case 1:															//玩家子弹发射
			cnt = 0;
			bul = player.hBullet;
			for(int i = 0; i < MaxBulletCount; ++i)
				if(!bullet[bul][i].exist)
				{
					bullet[bul][i].Create(player.x + player.Interval[cnt], player.y - bullet[bul][i].h);
					++cnt;
					if(cnt == player.BulletNum)
						break;
				}
		break;
		case 2:															//boss创建
			if(BossTime == BossCreateTime)								//boss创建时间到达
			{
				x = rand() % rect.right;
				boss[scene].Create(x, 0, 0);
				KillTimer(hwnd, 2);										//消除定时器
			}
			else if(scene && !BossTime)									//否则,如果不是第一关,产生一个上一关的boss
			{
				x = rand() % rect.right;
				if(!boss[scene-1].exist)
					boss[scene-1].Create(x, 0, 0);
				if(scene > 1)											//如果大于第二关,产生一个上两关的boss
				{
					x = rand() % rect.right;
					if(!boss[scene-2].exist)
						boss[scene-2].Create(x, 0, 0);
				}
			}
			++BossTime;													//递增boss未创建时间
			break;
		case 3:
			--ProtectedTime;											//防护罩时间递减
			if(!ProtectedTime)											//防护罩消失
			{
				player.Protected = false;
				KillTimer(hwnd, 3);										//消除定时器
			}
			break;
		case 4:
			--BuffTime;													//双倍弹药时间递减
			if(!BuffTime)												//双倍弹药效果消失,恢复弹道与子弹数并消除定时器
			{
				player.buff = false;
				player.BulletNum = 2;
				player.Interval = IntervalP[0];
				KillTimer(hwnd, 4);
			}
			break;
		}
		break;
	case WM_LBUTTONDOWN:
		if(pause)
			return true;
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		if(!start)															//进入游戏
		{
			start = true;
			player.Create(200, 400, 0);
			SetTimer(hwnd, 2, 30000, NULL);									//设置boss产生定时器
		}
		else if(over)
		{
			if(x >= 130 && x<= 330 && y>= 400 && y<= 439)					//返回主菜单按钮范围
			{
				start = false;
				over = false;
				mciSendString(L"play bgm repeat", NULL, 0, NULL);			//重复播放背景音乐
			}
		}
		else
			SetTimer(hwnd, 1, 200, NULL);									//设置定时器,处理玩家子弹的发射
		break;
	case WM_LBUTTONUP:														//鼠标左键抬起
		if(pause)         
			return true;
		if(start && !over)													//如果正在游戏中
			KillTimer(hwnd, 1);												//清除玩家子弹发射定时器
		break;
	case WM_MOUSEMOVE:
		if(pause)
			return true;
		//玩家位置跟随鼠标移动
		if(start && !over)
		{
			x = LOWORD(lParam);
			y = HIWORD(lParam);
			player.x = x;
			player.y = y;

			if(player.x < 0)
				player.x = 0;
			else if(player.x > rect.right-player.w)
				player.x = rect.right-player.w;
			if(player.y < 0)
				player.y = 0;
			else if(player.y > rect.bottom-player.h)
				player.y = rect.bottom-player.h;
		}
		break;
	case WM_KEYDOWN:
		switch(wParam)
		{
			case VK_SPACE:															//空格键
			if(!start)
			{
				start = true;
				player.Create(200, 400, 0);
				SetTimer(hwnd, 2, 30000, NULL); 
				super = true;														//开启无敌模式

				player.buff = true;													//开启双倍弹药
				player.BulletNum = 4;
				player.Interval = IntervalP[1];
				player.LaserNum = 999;												//初始激光数目为999
				player.Protected = true;											//开启防护罩
			}
			break;
			case VK_ESCAPE:															//esc键退出游戏
				DestroyWindow(hwnd);
				break;
		}
	case WM_CHAR:
		switch(wParam)
		{
		case 'A':																	//发射激光武器
		if(pause)
			return true;
			if(player.LaserNum) 
			{
				--player.LaserNum;													//激光武器数目减1
				int las = player.hLaser;
				for(int i = 0; i < MaxLaserCount; ++i)
					if(!laser[las][i].exist)
					{
						laser[las][i].exist = true;
						laser[las][i].Create(player.x+30, player.y-laser[las][i].h);
						break;
					}
				mciSendString(L"play laser from 0", NULL, 0, NULL);
			}
			break;
		case 'S':
			if(pause)
				return true;
			if(!super && player.ProtectedNum)										//如果不是无敌模式且防护罩数目大于0
			{
				--player.ProtectedNum;
				player.Protected = true;
				ProtectedTime = MaxProtectedTime;
				SetTimer(hwnd, 3, 1000, NULL);										//设置激光时间定时器
			}
			break;
		case 'P': 
			if(start && !over)														//如果正在游戏中,暂停/开始
				pause = !pause;
		}
		break;
/**************************************************************************************************************
在退出程序前,往往在此处删除创建的相关资源
***************************************************************************************************************/
	case WM_DESTROY:
		DeleteDC(mdc);
		DeleteDC(bufdc);
		ReleaseDC(hwnd, hdc);
		DeleteObject(hStart);
		DeleteObject(hOver);
		DeleteObject(hExplosion);
		DeleteObject(hGetHp);
		DeleteObject(hPlus);
		DeleteObject(hGetLaser);
		DeleteObject(hGetBuff);
		DeleteObject(hGetProtect);
		DeleteObject(hPlayer);
		DeleteObject(hProtect);
		DeleteObject(hEnemy);
		DeleteObject(hReturn);
		DeleteObject(hFont[0]);
		DeleteObject(hFont[1]);
		DeleteObject(hBrush);
		for(int i = 0; i < LaserNum; ++i)
			DeleteObject(hLaser[i]);
		for(int i = 0; i < SceneNum; ++i)
		{
			DeleteObject(hScore[i]);
			DeleteObject(hScene[i]);
			DeleteObject(hBoss[i]);
		}
		for(int i = 0; i < BulletNum; ++i)
			DeleteObject(hBullet[i]);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}