赞
踩
//所有飞机的结构体
typedef struct aircraft{
int type;//飞机类型
int HP;//剩余血量
int bomb_supply;//炸弹数量
clock_t bullet_supply;//弹药补给的开始时间
int x;
int y;
int height;
int width;
int speed;
int state;//飞机当前的状态
int statemax;//飞机的最大状态号
int cnt[4];//四个计帧器
}Aircraft;
//子弹和空投补给的结构体
typedef struct ammo{
int type;
bool isExist;//是否存在
int x;
int y;
int speed;
int height;
int width;
}Ammo;
//枚举类型定义各种类型,状态
//定义了全局结构体数组
extern Aircraft Plane;//玩家战机
extern Aircraft Enemys[ENEMYNUMAX];//敌机
extern Ammo Supplys[SUPPLYNUM];//空投补给
extern Ammo Bullets[BULLETNUM];//子弹
//创建游戏结构体管理控制游戏的难度
typedef struct game{
int gamelevel;
int score;
int enemynum;//敌机数量
int enemyPCT1;
int enemyPCT2;//控制大中小敌机的比例
int speedPCT;//控制高速敌机的占比
int supplyPCT1;
int supplyPCT2;//控制空投出现的比例
int FPS;//储存游戏当前的FPS
}Game;
//游戏画面的绘制,难度控制,音乐播放,游戏的开始暂停结束
void LoadImages(); //加载图片到内存
void InitGame(); //初始化游戏数据
void DrawINTF(); //绘制按钮,血条,炸弹等
void DrawPlane(); //绘制战机和敌机
void DrawAmmo(); //绘制子弹和空投补给
void LevelChack(); //根据得分情况控制游戏的难度
void Textout(); //一些文本内容的输出
void GameStart(); //绘制游戏开始界面
int GameOver(); //游戏结束等待用户选择
void Pause(); //游戏暂停
void PlayMusic(int type); //播放音乐
//处理游戏角色的创建,移动,碰撞和摧毁
void CreatBullet(); //创建子弹
void CreatSupply(); //创建空投补给
void CreatEnemy(); //创建敌机
void PlayerMove(); //玩家移动
void BulletMove(); //子弹移动
void SupplyMove(); //空投移动
void EnemyMove(); //敌机移动
void PlaneFight(); //打飞机
void CrashPlane(); //撞飞机
void Destroy(); //摧毁飞机
void GetSupply(); //获得空投
void Bombing(); //炸弹轰炸
//一些游戏相关的工具
void CtrlFPS(clock_t start_time);
bool Timer(clock_t *ts, clock_t td);
bool Framer(int *cnt, int fd);
bool IsCollision(int x, int y, int type);
bool IsinTriangle(int x, int y);
void drawAlpha(IMAGE *dstimg, int x, int y, IMAGE *srcimg);
int main(){
clock_t start_time = 0;
srand((unsigned int)time(NULL));
InitGame();
LoadImages();
initgraph(WIDTH, HEIGHT);
GameStart();
BeginBatchDraw();
while (1)
{
start_time = clock();
//绘制游戏图形
DrawPlane();
DrawAmmo();
DrawINTF();
//创建游戏角色
CreatEnemy();
CreatSupply();
CreatBullet();
//游戏角色移动
PlayerMove();
EnemyMove();
SupplyMove();
BulletMove();
//游戏角色互动
PlaneFight();
CrashPlane();
GetSupply();
Pause();
LevelChack();
Destroy();
switch (GameOver())
{
case RESUME:
break;
case AGAIN:
InitGame();
break;
case GAMEOVER:
goto END;
break;
default:
break;
}
Textout();//必须放在绘图函数之后防止被图层覆盖
CtrlFPS(start_time);
FlushBatchDraw();
}
END:
EndBatchDraw();
closegraph();
return 0;
}
void LoadImages(){
//加载背景图片
loadimage(&Img_bk, TEXT("res/background.png"), WIDTH, HEIGHT);
loadimage(&Img_temp, TEXT("res/background.png"), WIDTH, HEIGHT);
//加载按钮图片
loadimage(&Img_buttons_nor[0], TEXT("res/pause_nor.png"), BUTTONSW, BUTTONSH);
loadimage(&Img_buttons_nor[1], TEXT("res/resume_nor.png"), BUTTONSW, BUTTONSH);
loadimage(&Img_buttons_nor[2], TEXT("res/again.png"), AGAINW, AGAINH);
loadimage(&Img_buttons_nor[3], TEXT("res/gameover.png"), AGAINW, AGAINH);
loadimage(&Img_buttons_pressed[0], TEXT("res/pause_pressed.png"), BUTTONSW, BUTTONSH);
loadimage(&Img_buttons_pressed[1], TEXT("res/resume_pressed.png"), BUTTONSW, BUTTONSH);
//加载一些杂七杂八的图片
loadimage(&Img_states[0], TEXT("res/life.png"), LIFEW, LIFEH);
loadimage(&Img_states[1], TEXT("res/bomb.png"), BOMBW, BOMBH);
loadimage(&Img_bullets[0], TEXT("res/bullet1.png"), BULLETW, BULLETH);
loadimage(&Img_bullets[1], TEXT("res/bullet2.png"), BULLETW, BULLETH);
loadimage(&Img_supplys[0], TEXT("res/bullet_supply.png"), BULLET_SUPPLYW, BULLET_SUPPLYH);
loadimage(&Img_supplys[1], TEXT("res/bomb_supply.png"), BULLET_SUPPLYW, BULLET_SUPPLYH);
//加载战机
loadimage(&Img_plane[0], TEXT("res/me1.png"), PLANEW, PLANEH);
loadimage(&Img_plane[1], TEXT("res/me2.png"), PLANEW, PLANEH);
loadimage(&Img_plane_destroy[0], TEXT("res/me_destroy_1.png"), PLANEW, PLANEH);
loadimage(&Img_plane_destroy[1], TEXT("res/me_destroy_2.png"), PLANEW, PLANEH);
loadimage(&Img_plane_destroy[2], TEXT("res/me_destroy_3.png"), PLANEW, PLANEH);
loadimage(&Img_plane_destroy[3], TEXT("res/me_destroy_4.png"), PLANEW, PLANEH);
//ENEMYA
loadimage(&Img_enemya, TEXT("res/enemy1.png"), ENEMYAW, ENEMYAH);
loadimage(&Img_enemya_destroy[0], TEXT("res/enemy1_down1.png"), ENEMYA_DESTROYW, ENEMYA_DESTROYH);
loadimage(&Img_enemya_destroy[1], TEXT("res/enemy1_down2.png"), ENEMYA_DESTROYW, ENEMYA_DESTROYH);
loadimage(&Img_enemya_destroy[2], TEXT("res/enemy1_down3.png"), ENEMYA_DESTROYW, ENEMYA_DESTROYH);
loadimage(&Img_enemya_destroy[3], TEXT("res/enemy1_down4.png"), ENEMYA_DESTROYW, ENEMYA_DESTROYH);
//ENEMYB
loadimage(&Img_enemyb[0], TEXT("res/enemy2.png"), ENEMYBW, ENEMYBH);
loadimage(&Img_enemyb[1], TEXT("res/enemy2_hit.png"), ENEMYBW, ENEMYBH);
loadimage(&Img_enemyb_destroy[0], TEXT("res/enemy2_down1.png"), ENEMYB_DESTROYW, ENEMYB_DESTROYH);
loadimage(&Img_enemyb_destroy[1], TEXT("res/enemy2_down2.png"), ENEMYB_DESTROYW, ENEMYB_DESTROYH);
loadimage(&Img_enemyb_destroy[2], TEXT("res/enemy2_down3.png"), ENEMYB_DESTROYW, ENEMYB_DESTROYH);
loadimage(&Img_enemyb_destroy[3], TEXT("res/enemy2_down4.png"), ENEMYB_DESTROYW, ENEMYB_DESTROYH);
//ENEMYC
loadimage(&Img_enemyc[0], TEXT("res/enemy3_n1.png"), ENEMYCW, ENEMYCH);
loadimage(&Img_enemyc[1], TEXT("res/enemy3_n2.png"), ENEMYCW, ENEMYCH);
loadimage(&Img_enemyc[2], TEXT("res/enemy3_hit.png"), ENEMYCW, ENEMYCH);
loadimage(&Img_enemyc_destroy[0], TEXT("res/enemy3_down1.png"), ENEMYC_DESTROYW, ENEMYC_DESTROYH);
loadimage(&Img_enemyc_destroy[1], TEXT("res/enemy3_down2.png"), ENEMYC_DESTROYW, ENEMYC_DESTROYH);
loadimage(&Img_enemyc_destroy[2], TEXT("res/enemy3_down3.png"), ENEMYC_DESTROYW, ENEMYC_DESTROYH);
loadimage(&Img_enemyc_destroy[3], TEXT("res/enemy3_down4.png"), ENEMYC_DESTROYW, ENEMYC_DESTROYH);
loadimage(&Img_enemyc_destroy[4], TEXT("res/enemy3_down5.png"), ENEMYC_DESTROYW, ENEMYC_DESTROYH);
loadimage(&Img_enemyc_destroy[5], TEXT("res/enemy3_down6.png"), ENEMYC_DESTROYW, ENEMYC_DESTROYH);
}
void InitGame(){
//初始化游戏结构体
Mygame.gamelevel = 1;
Mygame.score = 0;
Mygame.enemynum = 10;
Mygame.enemyPCT1 = 80;
Mygame.enemyPCT2 = 100;
Mygame.speedPCT = 30;
Mygame.supplyPCT1 = 10;
Mygame.supplyPCT2 = 20;
Mygame.FPS = 0;
//初始化战机结构体
Plane.HP = 5;
Plane.type = PLANE;
Plane.speed = PLANESPEED;
Plane.bomb_supply = 0;
Plane.bullet_supply = 0;
Plane.width = PLANEW;
Plane.height = PLANEH;
Plane.x = (WIDTH - Plane.width) / 2;
Plane.y = HEIGHT / 3 * 2;
Plane.state = 0;
Plane.statemax = 6;
memset(Plane.cnt, 0, sizeof(Plane.cnt));
int i = 0;
//初始化敌机结构体数组
for (i = 0; i < ENEMYNUMAX; i++)
{
//将敌机初始化为“完全摧毁”状态
Enemys[i].statemax = DESTROY;
Enemys[i].state = Enemys[i].statemax + 1;
memset(Enemys[i].cnt, 0, sizeof(Enemys[i].cnt));
}
//初始化补给、子弹的结构体数组
for (i = 0; i < SUPPLYNUM; i++)
{
Supplys[i].isExist = false;
Supplys[i].speed = SUPPLYSPEED;
}
for (i = 0; i < BULLETNUM; i++)
{
Bullets[i].isExist = false;
Bullets[i].speed = BULLETSPEED;
Bullets[i].width = BULLETW;
Bullets[i].height = BULLETH;
}
}
//将敌机初始化为“完全摧毁”状态
Enemys[i].statemax = DESTROY;//判断敌机已经被摧毁的标志
Enemys[i].state = Enemys[i].statemax + 1;//判断敌机已经完全消失的标志(播放完爆炸场景)
void DrawINTF(){
int i = 0;
static int cnt = 0;
//暂停按钮
drawAlpha(&Img_temp, SPACING, SPACING, &Img_buttons_nor[0]);
//继续按钮
if (ResumeBottonDown)
{
if (!Framer(&cnt, 12))
{
//继续按钮被按下
drawAlpha(&Img_temp, BUTTONSW + SPACING, SPACING, &Img_buttons_pressed[1]);
}
else
{
ResumeBottonDown = false;
}
}
else
{
//继续按钮
drawAlpha(&Img_temp, BUTTONSW + SPACING, SPACING, &Img_buttons_nor[1]);
}
//生命值
for (i = 1; i <= Plane.HP; i++)
{
drawAlpha(&Img_temp, WIDTH - i * (LIFEW + SPACING), 10, &Img_states[0]);
}
//炸弹量
for (i = 1; i <= Plane.bomb_supply; i++)
{
drawAlpha(&Img_temp, 10, HEIGHT - TEXT5 - i * (BOMBH + SPACING), &Img_states[1]);
}
putimage(0, 0, &Img_temp);
}
void DrawPlane(){
int i = 0;
//绘制战机
switch (Plane.state)
{
case NORMAL1://普通状态
drawAlpha(&Img_temp, Plane.x, Plane.y, &Img_plane[0]);
break;
case NORMAL2://向前移动,加速状态
drawAlpha(&Img_temp, Plane.x, Plane.y, &Img_plane[1]);
//松开按键10帧后回到普通状态
if (Framer(&Plane.cnt[NORMAL2], 10))
{
Plane.state = NORMAL1;
}
break;
default:
break;
}
//绘制敌机
for (i = 0; i < Mygame.enemynum; i++)
{
//ENEMYA
if (Enemys[i].type == ENEMYA && Enemys[i].state == NORMAL1)
{
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemya);
}
//ENEMYB
if (Enemys[i].type == ENEMYB)
{
switch (Enemys[i].state)
{
case NORMAL1:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyb[0]);
break;
case UNDERATTACK://受击状态
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyb[1]);
if (Framer(&Enemys[i].cnt[UNDERATTACK], 10))
{
Enemys[i].state = NORMAL1;
}
break;
default:
break;
}
}
//ENEMYC
if (Enemys[i].type == ENEMYC)
{
switch (Enemys[i].state)
{
case NORMAL1:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyc[0]);
if (Framer(&Enemys[i].cnt[NORMAL1], 10))
{
Enemys[i].state = NORMAL2;
}
break;
case NORMAL2:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyc[1]);
if (Framer(&Enemys[i].cnt[NORMAL2], 10))
{
Enemys[i].state = NORMAL1;
}
break;
case UNDERATTACK:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyc[2]);
if (Framer(&Enemys[i].cnt[UNDERATTACK], 10))
{
Enemys[i].state = NORMAL1;
}
break;
default:
break;
}
}
}//end of for
putimage(0, 0, &Img_temp);
}
void DrawAmmo(){
int i = 0;
//绘制子弹
for (i = 0; i < BULLETNUM; i++)
{
if (Bullets[i].isExist)
{
if (Bullets[i].type == BULLET1)
{
drawAlpha(&Img_temp, Bullets[i].x, Bullets[i].y, &Img_bullets[0]);
}
else
{
drawAlpha(&Img_temp, Bullets[i].x, Bullets[i].y, &Img_bullets[1]);
}
}
}//end of for
//绘制空投补给
for (i = 0; i < SUPPLYNUM; i++)
{
if (Supplys[i].isExist)
{
if (Supplys[i].type == BULLET_SUPPLY)
{
drawAlpha(&Img_temp, Supplys[i].x, Supplys[i].y, &Img_supplys[0]);
}
else
{
drawAlpha(&Img_temp, Supplys[i].x, Supplys[i].y, &Img_supplys[1]);
}
}
}//end of for
putimage(0, 0, &Img_temp);
}
void Textout(){
wchar_t text[50] = { 0 };
settextstyle(TEXT4, 0, TEXT("微软雅黑"));
//得分
swprintf(text, TEXT("SCORE : %-d"), Mygame.score);
outtextxy(BUTTONSW * 2 + SPACING * 2, SPACING, text);
//游戏等级
swprintf(text, TEXT("LEVEL : %-d"), Mygame.gamelevel);
outtextxy(WIDTH - textwidth(text) - SPACING, HEIGHT - textheight(text) - SPACING, text);
//FPS
settextstyle(TEXT5, 0, TEXT("黑体"));
swprintf_s(text, TEXT("FPS:%d"), Mygame.FPS);
outtextxy(SPACING, HEIGHT - textheight(text) - SPACING, text);
}
注意:文本输出函数必须在所有绘图函数后调用,防止被其他图层遮盖。
void GameStart(){
int textsize = TEXT3 / 6 * 5;
int step = -1;
wchar_t text[50];
clock_t ts = 0;
setbkmode(TRANSPARENT);
BeginBatchDraw();
do
{
if (Timer(&ts, 50))
{
//绘制背景和logo
drawAlpha(&Img_temp, 0, 0, &Img_bk);
drawAlpha(&Img_temp, (WIDTH - LOGOW) / 2, HEIGHT / 10, &Img_logo);
putimage(0, 0, &Img_temp);
//显示操作说明
settextstyle(TEXT2, 0, TEXT("华文琥珀"));
settextcolor(RGB(118, 123, 124));
swprintf_s(text, TEXT("%s"), TEXT("W、S、A、D移动"));
outtextxy((WIDTH - textwidth(text)) / 2, HEIGHT / 5 * 2, text);
swprintf_s(text, TEXT("%s"), TEXT("空格键请求轰炸"));
outtextxy((WIDTH - textwidth(text)) / 2, HEIGHT / 5 * 2 + textheight(text), text);
//动态显示开始游戏的字样
settextstyle(textsize, 0, TEXT("微软雅黑"));
settextcolor(BLACK);
swprintf_s(text, TEXT("%s"), TEXT("按空格键开始游戏!"));
outtextxy((WIDTH - textwidth(text)) / 2, HEIGHT / 3 * 2 - textheight(text) / 2, text);
if (textsize >= TEXT3 || textsize <= TEXT3 / 6 * 5)
{
step = -step;
}
textsize += step;
FlushBatchDraw();
}
} while (!GetAsyncKeyState(VK_SPACE));
EndBatchDraw();
PlayMusic(GAMESTART);
}
void Pause(){
clock_t ts = 0;
//两个按钮的坐标
int x1 = SPACING;
int y1 = SPACING;
int x2 = BUTTONSW + SPACING;
int y2 = SPACING;
//接受鼠标消息
ExMessage msg;
bool pause, resume;
peekmessage(&msg, EX_MOUSE);
//点击暂停按钮
pause = msg.message == WM_LBUTTONDOWN && msg.x >= x1 && msg.y >= y1 && msg.x <= x1 + BUTTONSW && msg.y <= y1 + BUTTONSH;
//点击继续按钮
resume = msg.message == WM_LBUTTONDOWN && msg.x >= x2 && msg.y >= y2 && msg.x <= x2 + BUTTONSW && msg.y <= y2 + BUTTONSH;
if (pause)
{
PlayMusic(BUTTON);
PlayMusic(PAUSE);
//暂停按钮被按下
drawAlpha(&Img_temp, SPACING, SPACING, &Img_buttons_pressed[0]);
putimage(0, 0, &Img_temp);
ts = clock();//记录按钮按下的时刻,50ms后弹起
FlushBatchDraw();
wchar_t text[20] = TEXT("游戏暂停");
settextstyle(TEXT3, 0, TEXT("微软雅黑"));
do
{
if (Timer(&ts, 50))//每50ms刷新一帧
{
drawAlpha(&Img_temp, SPACING, SPACING, &Img_buttons_nor[0]);
putimage(0, 0, &Img_temp);
outtextxy((WIDTH - textwidth(text)) / 2, HEIGHT / 3 - textheight(text) / 2, text);
FlushBatchDraw();
}
peekmessage(&msg, EX_MOUSE);
resume = msg.message == WM_LBUTTONDOWN && msg.x >= x2 && msg.y >= y2 && msg.x <= x2 + BUTTONSW && msg.y <= y2 + BUTTONSH;
} while (!resume);
}//end of if
if (resume)
{
PlayMusic(BUTTON);
PlayMusic(RESUME);
//继续按钮被按下
ResumeBottonDown = true;
}
}
void PlayMusic(int type){
switch (type)
{
case GAMESTART:
//重复播放BGM
mciSendString(TEXT("open res/game_music.mp3 alias bgm"), 0, 0, 0);
mciSendString(TEXT("setaudio bgm volume to 500"), 0, 0, 0);//将BGM音量调小(阈值0-1000)
mciSendString(TEXT("play bgm repeat"), 0, 0, 0);
//重复播放子弹音效
mciSendString(TEXT("open res/bullet.mp3 alias bullet"), 0, 0, 0);
mciSendString(TEXT("play bullet repeat"), 0, 0, 0);
break;
case BUTTON:
mciSendString(TEXT("close button"), 0, 0, 0);
mciSendString(TEXT("open res/button.mp3 alias button"), 0, 0, 0);
mciSendString(TEXT("play button"), 0, 0, 0);
break;
case ENEMYA_DOWN:
mciSendString(TEXT("close enemy1_down"), 0, 0, 0);
mciSendString(TEXT("open res/enemy1_down.mp3 alias enemy1_down"), 0, 0, 0);
mciSendString(TEXT("play enemy1_down"), 0, 0, 0);
break;
case ENEMYB_DOWN:
mciSendString(TEXT("close enemy2_down"), 0, 0, 0);
mciSendString(TEXT("open res/enemy2_down.mp3 alias enemy2_down"), 0, 0, 0);
mciSendString(TEXT("play enemy2_down"), 0, 0, 0);
break;
case ENEMYC_DOWN:
mciSendString(TEXT("close enemy3_down"), 0, 0, 0);
mciSendString(TEXT("open res/enemy3_down.mp3 alias enemy3_down"), 0, 0, 0);
mciSendString(TEXT("play enemy3_down"), 0, 0, 0);
break;
case ENEMYC_FLYING:
mciSendString(TEXT("close enemy3_flying"), 0, 0, 0);
mciSendString(TEXT("open res/enemy3_flying.mp3 alias enemy3_flying"), 0, 0, 0);
mciSendString(TEXT("play enemy3_flying"), 0, 0, 0);
break;
case GET_BULLET:
mciSendString(TEXT("close get_bullet"), 0, 0, 0);
mciSendString(TEXT("open res/get_bullet.mp3 alias get_bullet"), 0, 0, 0);
mciSendString(TEXT("play get_bullet"), 0, 0, 0);
break;
case GET_BOMB:
mciSendString(TEXT("close get_bomb"), 0, 0, 0);
mciSendString(TEXT("open res/get_bomb.mp3 alias get_bomb"), 0, 0, 0);
mciSendString(TEXT("play get_bomb"), 0, 0, 0);
break;
case ME_DOWN:
mciSendString(TEXT("close me_down"), 0, 0, 0);
mciSendString(TEXT("open res/me_down.mp3 alias me_down"), 0, 0, 0);
mciSendString(TEXT("play me_down"), 0, 0, 0);
break;
case SUPPLY:
mciSendString(TEXT("close supply"), 0, 0, 0);
mciSendString(TEXT("open res/supply.mp3 alias supply"), 0, 0, 0);
mciSendString(TEXT("play supply"), 0, 0, 0);
break;
case UPGRADE:
mciSendString(TEXT("close upgrade"), 0, 0, 0);
mciSendString(TEXT("open res/upgrade.mp3 alias upgrade"), 0, 0, 0);
mciSendString(TEXT("play upgrade"), 0, 0, 0);
break;
case USE_BOMB:
mciSendString(TEXT("close use_bomb"), 0, 0, 0);
mciSendString(TEXT("open res/use_bomb.mp3 alias use_bomb"), 0, 0, 0);
mciSendString(TEXT("play use_bomb"), 0, 0, 0);
break;
case PAUSE:
mciSendString(TEXT("pause bgm"), 0, 0, 0);
mciSendString(TEXT("pause bullet"), 0, 0, 0);
break;
case RESUME:
mciSendString(TEXT("resume bgm"), 0, 0, 0);
mciSendString(TEXT("resume bullet"), 0, 0, 0);
break;
case GAMEOVER:
mciSendString(TEXT("close bgm"), 0, 0, 0);
mciSendString(TEXT("close bullet"), 0, 0, 0);
mciSendString(TEXT("close button"), 0, 0, 0);
mciSendString(TEXT("close enemy1_down"), 0, 0, 0);
mciSendString(TEXT("close enemy2_down"), 0, 0, 0);
mciSendString(TEXT("close enemy3_down"), 0, 0, 0);
mciSendString(TEXT("close enemy3_flying"), 0, 0, 0);
mciSendString(TEXT("close get_bullet"), 0, 0, 0);
mciSendString(TEXT("close get_bomb"), 0, 0, 0);
mciSendString(TEXT("close me_down"), 0, 0, 0);
mciSendString(TEXT("close supply"), 0, 0, 0);
mciSendString(TEXT("close upgrade"), 0, 0, 0);
mciSendString(TEXT("close use_bomb"), 0, 0, 0);
break;
default:
break;
}
}
void PlayerMove(){
if (Plane.state >= DESTROY)
{
return;
}
if (GetAsyncKeyState('W') || GetAsyncKeyState(VK_UP))
{
if (Plane.y > 0)
{
Plane.y -= Plane.speed;
Plane.state = NORMAL2;
}
}
if (GetAsyncKeyState('S') || GetAsyncKeyState(VK_DOWN))
{
if (Plane.y < HEIGHT - Plane.height)
{
Plane.y += Plane.speed;
}
}
if (GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT))
{
if (Plane.x > -(Plane.width / 2 - 5))//此处-5是微调飞机的位置
{
Plane.x -= Plane.speed;
}
}
if (GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT))
{
if (Plane.x < WIDTH - Plane.width / 2 - 5)
{
Plane.x += Plane.speed;
}
}
if (GetAsyncKeyState(VK_SPACE) && Plane.bomb_supply > 0)
{
Bombing();
}
}
void Bombing(){
int i = 0;
static clock_t ts = 0;
//2秒钟的延时避免了按一次空格连续使用轰炸
if (!Timer(&ts, 2000))
{
return;
}
PlayMusic(USE_BOMB);
//摧毁存活的所有敌机
for (i = 0; i < Mygame.enemynum; i++)
{
if (Enemys[i].state < DESTROY)
{
Enemys[i].state = DESTROY;
}
}
Plane.bomb_supply--;
}
void CreatBullet(){
int i = 0;
int offset = 25;//两排子弹的偏移量
static clock_t ts = 0;
//控制先后发射的子弹的间距
if (!Timer(&ts, 250))
{
return;
}
if (Plane.bullet_supply != 0)
{
//弹药补给持续20秒
if (clock() - Plane.bullet_supply >= 20000)
{
Plane.bullet_supply = 0;
}
//获得弹药补给发射两排子弹
for (size_t k = 0; k < 2; k++)
{
for (i = 0; i < BULLETNUM; i++)
{
if (!Bullets[i].isExist)
{
Bullets[i].isExist = true;
Bullets[i].type = BULLET2;
Bullets[i].x = Plane.x + Plane.width / 2 + offset;
Bullets[i].y = Plane.y + 5;
offset = -offset;
break;
}
}
}//end of for
}//end of if
else
{
for (i = 0; i < BULLETNUM; i++)
{
if (!Bullets[i].isExist)
{
Bullets[i].isExist = true;
Bullets[i].type = BULLET1;
Bullets[i].x = Plane.x + Plane.width / 2;
Bullets[i].y = Plane.y + 5;
return;
}
}
}//end of else
}
此处Plane.bullet_supply的使用有些混乱,它即用于判断是否获得弹药补给,又用来记录获得弹药补给的时刻控制持续时间。
void CreatSupply(){
int i = 0;
int randnum = rand() % 100;
static clock_t ts = 0;
//每过3秒有几率产生一次空投
if (!Timer(&ts, 3000) || randnum >= Mygame.supplyPCT2)
{
return;
}
PlayMusic(SUPPLY);
for (i = 0; i < SUPPLYNUM; i++)
{
if (!Supplys[i].isExist)
{
Supplys[i].isExist = true;
//空投补给类型随机
if (randnum >= 0 && randnum < Mygame.supplyPCT1)
{
Supplys[i].type = BULLET_SUPPLY;
Supplys[i].width = BULLET_SUPPLYW;
Supplys[i].height = BULLET_SUPPLYH;
Supplys[i].x = rand() % (WIDTH - Supplys[i].width);
Supplys[i].y = -Supplys[i].height;
}
else if (randnum >= Mygame.supplyPCT1 && randnum < Mygame.supplyPCT2)
{
Supplys[i].type = BOMB_SUPPLY;
Supplys[i].width = BOMB_SUPPLYW;
Supplys[i].height = BOMB_SUPPLYH;
Supplys[i].x = rand() % (WIDTH - Supplys[i].width);
Supplys[i].y = -Supplys[i].height;
}
return;
}//end of if
}//end of for
}
void EnemyMove(){
int i = 0;
for (i = 0; i < Mygame.enemynum; i++)
{
if (Enemys[i].state < DESTROY)
{
Enemys[i].y += Enemys[i].speed;
if (Enemys[i].y > HEIGHT)
{
Enemys[i].state = Enemys[i].statemax + 1;
}
}
}
}
void BulletMove(){
int i = 0;
for (i = 0; i < BULLETNUM; i++)
{
if (Bullets[i].isExist)
{
Bullets[i].y -= Bullets[i].speed;
if (Bullets[i].y <= -Bullets[i].height)
{
Bullets[i].isExist = false;
}
}
}
}
void SupplyMove(){
int i = 0;
for (i = 0; i < SUPPLYNUM; i++)
{
if (Supplys[i].isExist)
{
Supplys[i].y += Supplys[i].speed;
if (Supplys[i].y >= HEIGHT)
{
Supplys[i].isExist = false;
}
}
}
}
void PlaneFight(){
int i = 0;
int j = 0;
for (i = 0; i < Mygame.enemynum; i++)
{
if (Enemys[i].state >= DESTROY)
{
continue;
}
for (j = 0; j < BULLETNUM; j++)
{
if (Bullets[j].isExist)
{
//判断子弹是否与敌机碰撞
if (Bullets[j].x >= Enemys[i].x && Bullets[j].y >= Enemys[i].y
&&Bullets[j].x <= Enemys[i].x + Enemys[i].width
&&Bullets[j].y <= Enemys[i].y + Enemys[i].height)
{
Bullets[j].isExist = false;
Enemys[i].HP--;
if (Enemys[i].HP <= 0)
{
Enemys[i].state = DESTROY;
//摧毁敌机加分并播放音效
switch (Enemys[i].type)
{
case ENEMYA:
PlayMusic(ENEMYA_DOWN);
Mygame.score += 100;
break;
case ENEMYB:
PlayMusic(ENEMYB_DOWN);
Mygame.score += 300;
break;
case ENEMYC:
PlayMusic(ENEMYC_DOWN);
Mygame.score += 1000;
break;
default:
break;
}
}
else
{
Enemys[i].state = UNDERATTACK;
}
}
}
}//end of for
}//end of for
}
void CrashPlane(){
int i = 0;
if (Plane.state >= DESTROY)
{
return;
}
for (i = 0; i < Mygame.enemynum; i++)
{
if (Enemys[i].state >= DESTROY)
{
continue;
}
if (IsCollision(Enemys[i].x, Enemys[i].y, Enemys[i].type))
{
Enemys[i].state = DESTROY;
Plane.HP--;
if (Plane.HP <= 0)
{
Plane.state = DESTROY;
PlayMusic(ME_DOWN);
}
}
}//end of for
}
void GetSupply(){
int i = 0;
for (i = 0; i < SUPPLYNUM; i++)
{
if (!Supplys[i].isExist)
{
continue;
}
if (IsCollision(Supplys[i].x, Supplys[i].y, Supplys[i].type))
{
Supplys[i].isExist = false;
if (Supplys[i].type == BULLET_SUPPLY)
{
Plane.bullet_supply = clock();
PlayMusic(GET_BULLET);
}
else
{
PlayMusic(GET_BOMB);
if (Plane.bomb_supply < 5)
{
Plane.bomb_supply++;
}
}
}
}//end of for
}
bool IsCollision(int x, int y, int type){
//根据类型计算出对象的所有碰撞点,再一个一个进行判断
switch (type)
{
case ENEMYA:
if (IsinTriangle(x + ENEMYAW / 2, y + ENEMYAH)
|| IsinTriangle(x, y + ENEMYAH / 3)
|| IsinTriangle(x + ENEMYAW, y + ENEMYAH / 3)
|| IsinTriangle(x + ENEMYAW / 2, y))
{
return true;
}
break;
case ENEMYB:
if (IsinTriangle(x + ENEMYBW / 3, y + ENEMYBH)
|| IsinTriangle(x + ENEMYBW / 3 * 2, y + ENEMYBH)
|| IsinTriangle(x, y + ENEMYBH / 3)
|| IsinTriangle(x, y + ENEMYBH / 3 * 2)
|| IsinTriangle(x + ENEMYBW, y + ENEMYBH / 3)
|| IsinTriangle(x + ENEMYBW, y + ENEMYBH / 3 * 2))
{
return true;
}
break;
case ENEMYC:
if (IsinTriangle(x + ENEMYCW / 3, y + ENEMYCH)
|| IsinTriangle(x + ENEMYCW / 3 * 2, y + ENEMYCH)
|| IsinTriangle(x, y)
|| IsinTriangle(x + ENEMYCW, y)
|| IsinTriangle(x, y + ENEMYCH / 3)
|| IsinTriangle(x, y + ENEMYCH / 3 * 2)
|| IsinTriangle(x + ENEMYCW, y + ENEMYCH / 3)
|| IsinTriangle(x + ENEMYCW, y + ENEMYCH / 3 * 2))
{
return true;
}
break;
case BULLET_SUPPLY:
if (IsinTriangle(x, y)
|| IsinTriangle(x + BULLET_SUPPLYW, y)
|| IsinTriangle(x + BULLET_SUPPLYW, y + BULLET_SUPPLYH)
|| IsinTriangle(x, y + BULLET_SUPPLYH)
|| IsinTriangle(x, y + BULLET_SUPPLYH / 2)
|| IsinTriangle(x + BULLET_SUPPLYW, y + BULLET_SUPPLYH / 2))
{
return true;
}
break;
case BOMB_SUPPLY:
if (IsinTriangle(x, y)
|| IsinTriangle(x + BOMB_SUPPLYW, y)
|| IsinTriangle(x + BOMB_SUPPLYW, y + BOMB_SUPPLYH)
|| IsinTriangle(x, y + BOMB_SUPPLYH)
|| IsinTriangle(x, y + BOMB_SUPPLYH / 2)
|| IsinTriangle(x + BOMB_SUPPLYW, y + BOMB_SUPPLYH / 2))
{
return true;
}
break;
default:
break;
}
return false;
}
bool IsinTriangle(int x, int y){
//定义6个变量记录三角形战机3个点的坐标
int x1 = Plane.x + Plane.width / 2;
int y1 = Plane.y;
int x2 = Plane.x;
int y2 = Plane.y + Plane.height;
int x3 = Plane.x + Plane.width;
int y3 = Plane.y + Plane.height;
//计算这3个点构成的三角形的面积
double s1 = 0.5 * fabs((double)((x1*y2 - x2*y1) + (x2*y3 - x3*y2) + (x3*y1 - x1*y3)));
//计算待判断点和其中2个点构成的三角形的面积
double s2 = 0.5 * fabs((double)((x*y2 - x2*y) + (x2*y3 - x3*y2) + (x3*y - x*y3)));
double s3 = 0.5 * fabs((double)((x1*y - x*y1) + (x*y3 - x3*y) + (x3*y1 - x1*y3)));
double s4 = 0.5 * fabs((double)((x1*y2 - x2*y1) + (x2*y - x*y2) + (x*y1 - x1*y)));
if (fabs(s1 - s2 - s3 - s4) < DBL_EPSILON)
{
return true;
}
else
{
return false;
}
}
void CtrlFPS(clock_t start_time){
clock_t running_time = clock() - start_time;
int sleep_time = 13 - running_time;
if (sleep_time > 0)
{
Sleep(sleep_time);
}
//记录经过动态休眠调整后的FPS
Mygame.FPS = 1000 / (clock() - start_time);
}
void CreatEnemy(){
int i = 0;
int randnum = 0;
static clock_t ts = 0;
//每半秒钟产生一架敌机
if (!Timer(&ts, 500))
{
return;
}
for (i = 0; i < Mygame.enemynum; i++)
{
//必须是完全消失的敌机(爆炸动画播放完毕)
if (Enemys[i].state == Enemys[i].statemax + 1)
{
Enemys[i].state = NORMAL1;
//设置敌机类型,并根据类型设置其他属性
randnum = rand() % 100;
if (randnum >= 0 && randnum < Mygame.enemyPCT1)
{
Enemys[i].type = ENEMYA;
Enemys[i].HP = 1;
Enemys[i].width = ENEMYAW;
Enemys[i].height = ENEMYAH;
Enemys[i].statemax = 6;
Enemys[i].x = rand() % (WIDTH - Enemys[i].width);
Enemys[i].y = -Enemys[i].height;
}
else if (randnum >= Mygame.enemyPCT1 && randnum < Mygame.enemyPCT2)
{
Enemys[i].type = ENEMYB;
Enemys[i].HP = 3;
Enemys[i].width = ENEMYBW;
Enemys[i].height = ENEMYBH;
Enemys[i].statemax = 6;
Enemys[i].x = rand() % (WIDTH - Enemys[i].width);
Enemys[i].y = -Enemys[i].height;
}
else
{
Enemys[i].type = ENEMYC;
Enemys[i].HP = 8;
Enemys[i].width = ENEMYCW;
Enemys[i].height = ENEMYCH;
Enemys[i].statemax = 8;
Enemys[i].x = rand() % (WIDTH - Enemys[i].width);
Enemys[i].y = -Enemys[i].height;
PlayMusic(ENEMYC_FLYING);
}
//设置敌机速度
randnum = rand() % 100;
switch (Enemys[i].type)
{
case ENEMYA:
if (randnum >= 0 && randnum < Mygame.speedPCT)
{
Enemys[i].speed = ENEMYASPEED2;
}
else
{
Enemys[i].speed = ENEMYASPEED1;
}
break;
case ENEMYB:
if (randnum >= 0 && randnum < Mygame.speedPCT)
{
Enemys[i].speed = ENEMYBSPEED2;
}
else
{
Enemys[i].speed = ENEMYBSPEED1;
}
break;
case ENEMYC:
Enemys[i].speed = ENEMYCSPEED;
break;
default:
break;
}//end of switch
return;
}//end of if (Enemys[i].state == Enemys[i].statemax + 1)
}//end of for
}
如何控制敌机各类型产生的比例?
速度和补给的比例原理也是如此
void LevelChack(){
//查看是否需要升级,不需升级直接返回
switch (Mygame.gamelevel)
{
case 1:
if (Mygame.score >= 1000)
{
Mygame.gamelevel = 2;
break;
}
return;
case 2:
if (Mygame.score >= 5000)
{
Mygame.gamelevel = 3;
break;
}
return;
case 3:
if (Mygame.score >= 20000)
{
Mygame.gamelevel = 4;
break;
}
return;
case 4:
if (Mygame.score >= 50000)
{
Mygame.gamelevel = 5;
break;
}
return;
default:
return;
}//end of switch
PlayMusic(UPGRADE);
//升级后改变游戏难度
switch (Mygame.gamelevel)
{
case 2:
Mygame.enemynum = 15;
Mygame.enemyPCT1 = 60;
Mygame.enemyPCT2 = 100;
Mygame.speedPCT = 50;
Mygame.supplyPCT1 = 10;
Mygame.supplyPCT2 = 20;
break;
case 3:
Mygame.enemynum = 20;
Mygame.enemyPCT1 = 60;
Mygame.enemyPCT2 = 98;
Mygame.speedPCT = 50;
Mygame.supplyPCT1 = 10;
Mygame.supplyPCT2 = 20;
break;
case 4:
Mygame.enemynum = 25;
Mygame.enemyPCT1 = 50;
Mygame.enemyPCT2 = 95;
Mygame.speedPCT = 60;
Mygame.supplyPCT1 = 15;
Mygame.supplyPCT2 = 30;
break;
case 5:
Mygame.enemynum = 30;
Mygame.enemyPCT1 = 40;
Mygame.enemyPCT2 = 90;
Mygame.speedPCT = 60;
Mygame.supplyPCT1 = 20;
Mygame.supplyPCT2 = 40;
break;
default:
break;
}//end of switch
}
分数达到指定标准后,等级提升。同时,修改游戏结构体中难度相关的参数,实现难度自动调整。
若分数未达到指定标准,函数直接返回不进行任何操作。
int GameOver(){
clock_t ts = 0;
if (Plane.state <= Plane.statemax)
{
return RESUME;
}
mciSendString(TEXT("close bgm"), 0, 0, 0);
mciSendString(TEXT("close bullet"), 0, 0, 0);
//两个按钮的位置
int x1 = (WIDTH - AGAINW) / 2;
int y1 = HEIGHT / 4;
int x2 = (WIDTH - AGAINW) / 2;
int y2 = y1 + AGAINH + 20;
//绘制两个按钮
drawAlpha(&Img_temp, x1, y1, &Img_buttons_nor[2]);
drawAlpha(&Img_temp, x2, y2, &Img_buttons_nor[3]);
putimage(0, 0, &Img_temp);
//接受鼠标消息,死循环等待用于点击
ExMessage msg;
bool again, gameover;
do
{
if (Timer(&ts, 50))
{
//每50ms刷新一次画面,使窗口实现重绘
FlushBatchDraw();
}
peekmessage(&msg, EX_MOUSE);
again = (msg.message == WM_LBUTTONDOWN && msg.x >= x1 && msg.y >= y1 && msg.x <= x1 + AGAINW && msg.y <= y1 + AGAINH);
gameover = (msg.message == WM_LBUTTONDOWN && msg.x >= x2 && msg.y >= y2 && msg.x <= x2 + AGAINW && msg.y <= y2 + AGAINH);
} while (!again && !gameover);
//结束???继续???
if (again)
{
PlayMusic(BUTTON);
PlayMusic(GAMESTART);
return AGAIN;
}
else
{
PlayMusic(BUTTON);
//关闭所有音乐
Sleep(500);//休眠半秒,等待音乐播放完毕
PlayMusic(GAMEOVER);
return GAMEOVER;
}
}
接受鼠标消息的一般步骤
- 定义 ExMessage类型的消息结构体msg.
- peekmesage();函数从消息队列中拉取一条消息
- switch语句选择指定的消息类型,如WM_LBUTTONDOWN(鼠标左击)
- 根据对应类型的消息做出相应的操作。
void Destroy(){
int i = 0;
//飞机已被摧毁,但未播放完爆炸动画
if (Plane.state >= DESTROY && Plane.state <= Plane.statemax)
{
drawAlpha(&Img_temp, Plane.x, Plane.y, &Img_plane_destroy[Plane.state - DESTROY]);//数组下标从0开始
//10帧后,绘制下一状态的贴图
if (Framer(&Plane.cnt[DESTROY], 10))
{
Plane.state++;
}
}
for (i = 0; i < Mygame.enemynum; i++)
{
if (Enemys[i].state >= DESTROY && Enemys[i].state <= Enemys[i].statemax)
{
switch (Enemys[i].type)
{
case ENEMYA:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemya_destroy[Enemys[i].state - DESTROY]);
break;
case ENEMYB:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyb_destroy[Enemys[i].state - DESTROY]);
break;
case ENEMYC:
drawAlpha(&Img_temp, Enemys[i].x, Enemys[i].y, &Img_enemyc_destroy[Enemys[i].state - DESTROY]);
break;
default:
break;
}
if (Framer(&Enemys[i].cnt[DESTROY], 10))
{
Enemys[i].state++;
}
}
}//end of for
putimage(0, 0, &Img_temp);
}
在绘制飞机摧毁爆炸的连续画面时,用到了计帧器。使每一个状态的画面完整连续的呈现出来。这里我尝试使用过计时器但丢失画面的问题很难解决(除非将所有的时间戳都设置为全局变量)。
bool Timer(clock_t *ts, clock_t td){
if (clock() - *ts >= td)
{
*ts = clock();
return true;
}
else
{
return false;
}
}
计时器的优点
- 由于时间的长度是固定的,因此不受硬件环境和运行环境的干扰。
- 时间给程序员的感觉更直观。
计时器的缺点
- 在计时动作开始时需要调用clock();函数记录开始的时刻(时间戳)。
- 但记录时刻的位置和调用计时器函数的位置往往不在同一个函数中。
- 遇到这种情况解决方法只能是将时间戳定义为全局变量
- 但如果代码中使用计时器的地方很多,就会使全局变量过多,从而变得混乱。
使用场景
- 独立在帧数控制(CtrlFPS)之外的循环语句中,如Pause();、GameOver();等
- 对连续和完整要求不高的动作,如CreatEnemy();,CreatBullet();
bool Framer(int *cnt, int fd){
if (*cnt == (fd-1))
{
*cnt = 0;
return true;
}
else
{
(*cnt)++;//易错点
return false;
}
}
计帧器的优点
- 使用简单方便,只需要在计帧器函数被调用的地方定义一个静态的帧数计数器即可。
- 正确的使用会使画面完整连续。
计帧器的缺点
- 游戏的帧数受不同硬件环境和运行环境的干扰。
- 也就是说,同样绘制10帧画面不同的电脑和运行环境使用的时间不同。
- 使得游戏画面的FPS不稳定,时而卡顿,时而丢失画面(FPS太高)
- 因此,计帧器的使用必须在帧数控制(CtrlFPS)的范围内,将游戏的FPS稳定控制在75左右
使用场景
- 在帧数控制(CtrlFPS)的范围内。例如:DrawINTF();
- 需要绘制连续完整的动画。例如:Destroy();
void drawAlpha(IMAGE *dstimg, int x, int y, IMAGE *srcimg)
{
// 变量初始化
DWORD *dst = GetImageBuffer(dstimg);
DWORD *src = GetImageBuffer(srcimg);
int src_width = srcimg->getwidth();
int src_height = srcimg->getheight();
int dst_width = (dstimg == NULL ? getwidth() : dstimg->getwidth());
int dst_height = (dstimg == NULL ? getheight() : dstimg->getheight());
// 计算贴图的实际长宽
int iwidth = (x + src_width > dst_width) ? dst_width - x : src_width; // 处理超出右边界
int iheight = (y + src_height > dst_height) ? dst_height - y : src_height; // 处理超出下边界
if (x < 0) { src += -x; iwidth -= -x; x = 0; } // 处理超出左边界
if (y < 0) { src += src_width * -y; iheight -= -y; y = 0; } // 处理超出上边界
// 修正贴图起始位置
dst += dst_width * y + x;
// 实现透明贴图
for (int iy = 0; iy < iheight; ++iy)
{
for (int i = 0; i < iwidth; ++i)
{
int sa = ((src[i] & 0xff000000) >> 24);//获取阿尔法值
if (sa != 0)//假如是完全透明就不处理
if (sa == 255)//假如完全不透明则直接拷贝
dst[i] = src[i];
else//真正需要阿尔法混合计算的图像边界才进行混合
dst[i] = ((((src[i] & 0xff0000) >> 16) + ((dst[i] & 0xff0000) >> 16) * (255 - sa) / 255) << 16) | ((((src[i] & 0xff00) >> 8) + ((dst[i] & 0xff00) >> 8) * (255 - sa) / 255) << 8) | ((src[i] & 0xff) + (dst[i] & 0xff) * (255 - sa) / 255);
}
dst += dst_width;
src += src_width;
}
}
一些常见错误:
- 图片的格式不是.png格式,或者图片没有透明背景。
- 没有为临时图片变量(Img_temp),加载背景图片。(背景为空)
- 在绘图循环中没有调用drawAlpha();函数,将背景图片加载在临时图片变量(Img_temp)中。(背景只绘制一次,对象移动会留下残影)
- 调用完drawAlpha();函数后没有将临时图片变量(Img_temp)绘制在绘图窗口上(利用putimage();函数)。
- 没有及时刷新批量绘图(FlushBatchDraw();函数)
- 没有控制好图层显示的先后顺序,造成图层的遮盖。
- 注意:文本输出函数必须在所有绘图函数后调用,防止被其他图层遮盖。
【C语言游戏】微信飞机大战 | PlaneFight
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。