学习了前面的许多知识,现在可以真正做个可以玩的游戏了。
之前是用MFC做的飞机大战,但是有许多的问题。这次我们用Win32程序来开发,所有的代码都是自己写成的,可以控制更多的细节。这次的游戏就没有之前崩溃的问题了,并且绘制血条也很简单,直接GDI绘图完成。具体实现的时候没有用面向对象的思想,完全是面向过程的,感觉自己写的太臃肿了。事后想想如果是大型游戏的话这样肯定不行,还好规模小。先看下效果把。
游戏多个关卡,多个boss,多种道具,可玩行还可以吧。跟之前的差不太多。
运行环境:vs2010
此游戏为飞机大战,胜利条件为玩家消灭最终boss,失败条件为玩家被消灭。
游戏玩法:
开始界面:点击鼠标进入普通模式,按下空格键进入无敌模式,按下Esc键退出游戏。
游戏界面:玩家鼠标控制飞机的移动与射击,A键发射激光,S键使用防护罩,按下Esc键退出游戏,P键暂停。
结束界面:鼠标点击返回主界面回到开始界面。
主要使用技术的实现方式和对应的代码:
血条:使用GDI绘图函数进行绘制,主要使用了Rectangle函数和CreateSolidBrush。
镂空贴图:使用了TransparentBlt完成镂空贴图。TransparentBlt在贴图时可以把源位图中的某种颜色设定为透明色,这样与背景融合时就可以将源位图的部分像素隐藏掉。
位图显示:从文件中加载位图建立与窗口DC兼容的内存DC,内存DC装载创建好的位图对象,将内存DC的内容映射到窗口DC中以完成显像操作
动画:用连续贴图的方法完成动画,通过消息循环的方法
鼠标与键盘交互:通过消息循环实现键盘鼠标与游戏的交互。
子弹与道具运动:
运动物体的速度保持不变,每次画面更新,利用物体速度
计算下次物体出现的位置,产生物体移动的效果。
碰撞检测:框架检测碰撞,使用物体贴图的外围表示方框,
只要检测其中一个矩形的 四个定点是否在另一个矩形内即可。
源代码:
#include
#include
#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;
}
下载地址:
下载地址