学习内容:1.背景的运动
2.飞机爆炸效果
3.透明贴图工具
目录
一、需要用到的头文件、全局变量和一些函数、结构体
#include<graphics.h>
#include<stdio.h>
#include<easyx.h>
#include<time.h>
#include<stdlib.h>
#include<math.h>
#include<Windows.h>
#include"tool.h"
#pragma comment(lib,"winmm.lib")
#define WIDTH 480
#define HIGHT 700
IMAGE img_bk[2];//背景图
IMAGE img_myplane[2];//玩家飞机
IMAGE img_bullet;//子弹
IMAGE img_enemyplane;//敌机
IMAGE img_enemyboom[4];//敌机爆炸
int bkY[2] = { -700,0 };//两张背景位置
int score = 0;//得分
int enemy_num = 4;//敌机数量
int enemy_add = 0;//敌机需要增加数
//坐标结构体
struct Point
{
int x;
int y;
};
//玩家飞机结构坐标,速度,是否存在
struct Plane
{
int px;
int py;
int speed;
int frame;//用于切换玩家飞机图片
bool exis;
}gamer;
//子弹结构体包括坐标和是否存在(可自行添加子弹种类,速度,方向等属性)
typedef struct Bullet
{
int x;
int y;
bool exis;
Bullet* next;
}mybullet;
mybullet* one_mybullet;
//敌机结构体(可自行添加其他类飞机,boss等)
typedef struct Enemy
{
int posx;//坐标
int posy;
int boom;//爆炸图片帧数
//int type;//飞机类型
bool exis;//是否存活
Enemy* next;
}enemy;
enemy* one_enemy;
void loadImage(); //加载图片
void InitPlayer(Plane* pthis, int x, int y); //初始玩家飞机
void DrawImg(); //绘制图片
void DrawPlayer(Plane* pthis); //绘制玩家飞机
void PlayerMove(); //玩家飞机运动
void RunBk(); //背景运动
void CreateBullet(); //创建子弹
void AddMybullet(); //增加子弹并初始化
void MybulletMove(); //子弹移动
void CreatEnemy(); //创建敌机
void AddEnemy(); //增加敌机并初始化
void EnemyMove(); //敌机移动
void HitEnemy(); //击中敌机
void LoadBoom(); //加载爆炸图片
void AnimalBoom(IMAGE *picture, Enemy* pthis) //飞机爆炸效果
bool overlapationa(enemy* pthis); //判断是否飞机与飞机重叠
bool overlapationb(enemy* pthis); //判断是否子弹与飞机重叠
bool collisiona(enemy* e1, enemy* e2); //判断飞机与飞机是否碰撞
bool collisionb(mybullet* b, enemy* e); //判断飞机与子弹是否碰撞
二、数据初始化以及图片处理
//初始化数据
void InitData()
{
mciSendString("open music/bgm1.mp3 alias bkmusic", NULL, 0, NULL);
mciSendString("play bkmusic repeat", NULL, 0, NULL);
initgraph(WIDTH, HIGHT,SHOWCONSOLE);//图形窗口
InitPlayer(&gamer, (getwidth() - img_myplane->getwidth()) / 2, getheight() / 2 + img_myplane->getheight());
CreateBullet();
CreatEnemy();
loadImage();
}
//加载图片资源
void loadImage()
{
loadimage(img_bk + 0, "images/background1.png");//加载背景图片
loadimage(img_bk + 1, "images/background2.png");//加载背景图片
loadimage(img_myplane + 0, "images/me1.png");//加载玩家飞机
loadimage(img_myplane + 1, "images/me2.png");//加载玩家飞机
loadimage(&img_bullet, "images/bullet1.png");//加载玩家子弹
loadimage(&img_enemyplane, "images/enemy1.png");//加载敌机图片
LoadBoom();
}
//加载敌机爆炸效果(可以给多个飞机做接口,只演示一种飞机爆炸)
void LoadBoom()
{
for (int i = 0; i <= 3; i++)
{
char fileName[30] = {0};
sprintf(fileName, "images/enemy1_down%d.png", i);
loadimage(&img_enemyboom[i], fileName);
}
}
//敌机爆炸效果(避免使用循环语句、会出现卡顿现象)
void AnimalBoom(IMAGE *picture, Enemy* pthis)
{
if (pthis->exis == false && timer(20, 3))//控制帧率
{
drawImg(pthis->posx, pthis->posy, &picture[pthis->boom] );
if (pthis->boom == 3)//当三张爆炸图片全部显示完之后,归零
{
pthis->boom = 0;
pthis->exis = true;
//当创建的敌机与其他敌机重叠时,重新创建,直至不重合
do
{
pthis->posx = rand() % (getheight() / 2);
pthis->posy = -img_enemyplane.getheight();
} while ( overlapationa(pthis));
}
else
{
pthis->boom++;//用于切换下一张图片
}
}
}
//让背景连续滚动(用两张上下连接的背景图,循环运动)
void RunBk()
{
putimage(0, bkY[0], &img_bk[0]);
putimage(0, bkY[1], &img_bk[1]);//打印出背景图片
if (timer(50, 1))
{
bkY[0] ++;
bkY[1] ++;
if (bkY[0] >= 700)
{
bkY[0] = -700;
}
else if (bkY[1] >= 700)
{
bkY[1] = -700;
}
}
}
//绘制图片
void DrawImg()
{
RunBk();//背景滚动
DrawPlayer(&gamer);//打印玩家飞机图片
//打印子弹
mybullet* bt = one_mybullet->next;
while (bt != NULL&&bt->exis == true)
{
drawImg(bt->x, bt->y, &img_bullet);
//printf("%d %d\n", bt->x, bt->y);
bt = bt->next;
}
//打印敌机
enemy* et = one_enemy->next;
while (et != NULL)
{
if (et->exis == true)//飞机不存在时打印爆炸图片
{
drawImg(et->posx, et->posy, &img_enemyplane);
}
else
{
AnimalBoom(img_enemyboom, et);
}
et=et->next;
}
//显示得分
setcolor(BLACK);
settextstyle(25, 0, "黑体");
TCHAR s[5];
_stprintf(s,_T("%d"),score);
outtextxy(20, 20, s);
setbkmode(0);
}
三、主体
玩家、子弹和敌机的初始化,增加数量和运动等
//飞机初始化
void InitPlayer(Plane* pthis,int x,int y)
{
pthis->px = x;
pthis->py = y;
pthis->exis = true;
pthis->frame = 0;
}
//绘制玩家飞机,(让玩家飞机有运动效果)
void DrawPlayer(Plane* pthis)
{
drawImg(pthis->px, pthis->py, img_myplane + pthis->frame);//打印玩家飞机
pthis->frame = (pthis->frame + 1) % 2;
}
//玩家飞机移动
void PlayerMove()//用鼠标移动玩家飞机
{
int startclock = clock();
MOUSEMSG m;//鼠标结构体
while (MouseHit())
{
m = GetMouseMsg();
if (m.uMsg == WM_MOUSEMOVE)
{
gamer.px = m.x - img_myplane->getwidth() / 2;
gamer.py = m.y - img_myplane->getheight() / 2;
}
}
if (timer(200,0) )
{
AddMybullet();
}
}
//创建玩家飞机子弹
void CreateBullet()
{
one_mybullet = (mybullet*)malloc(sizeof(mybullet));
if (one_mybullet == NULL)
{
return;
}
else
{
one_mybullet->next = NULL;
}
}
//加载子弹
void AddMybullet()
{
mybullet* pnew = (mybullet*)malloc(sizeof(mybullet));
if (pnew == NULL)
{
return;
}
else
{
pnew->next = NULL;
pnew->x = gamer.px + 51;
pnew->y = gamer.py ;
pnew->exis = true;
pnew->next = one_mybullet->next;
one_mybullet->next = pnew;
}
}
//让玩家子弹移动
void MybulletMove()
{
mybullet* P1 = one_mybullet->next;
mybullet* P2 = one_mybullet;
for (; P1 != NULL; P1 = P1->next)
{
if (P1->y <= 0 || P1->exis == false)//当子弹越界或者死亡时,释放掉子弹
{
P2->next = P1->next;
free(P1);
return;
}
else
{
P1->y -= 9;
}
P2 = P1;
}
}
//创建敌机
void CreatEnemy()
{
one_enemy = (enemy*)malloc(sizeof(enemy));
if (one_enemy == NULL)
{
return;
}
one_enemy->next = NULL;
}
增加敌机并初始化
void AddEnemy()
{
//srand((unsigned int)time(NULL));
enemy* ep= (enemy*)malloc(sizeof(enemy));
if (ep == NULL)
{
return;
}
ep->next = NULL;
ep->exis = true;
ep->boom = 0;
ep->next = one_enemy->next;
one_enemy->next = ep;
do
{
ep->posx = rand() % (getheight() / 2);
ep->posy = -img_enemyplane.getheight() / 2;
} while (ep->next != NULL &&overlapationa(ep));//创建的飞机不与其他飞机重叠,否则重新生成
}
// 敌机移动
void EnemyMove()
{
srand((unsigned int)time(NULL));
enemy* pt = one_enemy->next;
while (pt)
{
if (pt->posy >= getheight())
{
do
{
pt->posx = rand() % (getheight() / 2);
pt->posy = -img_enemyplane.getheight();
} while (overlapationa(pt));
}
else
{
pt->posy += 2;
}
pt = pt->next;
}
}
四、物体碰撞
//击中敌机
void HitEnemy()
{
enemy* e = NULL;
mybullet* b = NULL;
e = one_enemy->next;
while (e != NULL)
{
if (e->exis == false)
{
e = e->next;
continue;
}
b = one_mybullet->next;
while (b != NULL)
{
if (b->exis == true && collisionb(b, e))
{
score++;
e->exis = false;
b->exis = false;
}
else
{
b = b->next;
continue;
}
b = b->next;
}
e = e->next;
}
}
//用碰撞物体的四个点的任意点判断是否在被碰撞物范围内
//判断飞机与飞机是否碰撞
bool collisiona(enemy* e1,enemy* e2 )
{
struct Point ap[4] =
{
{e1->posx,e1->posy},
{e1->posx,e1->posy+img_enemyplane.getheight()},
{e1->posx+img_enemyplane.getwidth(),e1->posy},
{ e1->posx + img_enemyplane.getwidth() ,e1->posy + img_enemyplane.getheight() }
};
for (int i = 0; i < 4; i++)
{
if (ap[i].x >= e2->posx&&ap[i].x <= e2->posx + img_enemyplane.getwidth() &&
ap[i].y >= e2->posy&&ap[i].y <= e2->posy + img_enemyplane.getwidth())
{
return true;
}
}
return false;
}
//判断子弹与飞机是否碰撞
bool collisionb(mybullet* b, enemy* e)
{
const struct Point ap[4] =
{
{ b->x ,b->y },
{ b->x,b->y + img_bullet.getheight() },
{ b->x + img_bullet.getwidth(),b->y },
{ b->x + img_bullet.getwidth() ,b->y + img_bullet.getheight() }
};
for (int i = 0; i < 4; i++)
{
if (ap[i].x >= e->posx&&ap[i].x <= e->posx + img_enemyplane.getwidth() &&
ap[i].y >= e->posy&&ap[i].y <= e->posy + img_enemyplane.getwidth())
{
return true;
}
}
return false;
}
//判断飞机与飞机是否重叠(避免生成飞机堆叠)
bool overlapationa(enemy* pthis)
{
enemy* a = NULL;
a = one_enemy->next;
while (a != NULL)
{
if (pthis == a || a->exis == false)
{
a = a->next;
continue;
}
if (collisiona(pthis, a))
{
return true;
}
a = a->next;
}
return false;
}
bool overlapationb(enemy* pthis)
{
mybullet* a = NULL;
a = one_mybullet->next;
while (a != NULL)
{
if (collisionb(a, pthis))
{
return true;
}
a = a->next;
}
return false;
}
五、游戏运行
//运行游戏
void RunGame()
{
BeginBatchDraw();//防止闪屏
if (enemy_add <= enemy_num)//可以设计关卡来增加敌机数量
{
AddEnemy();
enemy_add++;
}
DrawImg();
PlayerMove();
MybulletMove();
HitEnemy();
EnemyMove();
int startTime = clock();
int frameTime = clock() - startTime;
if (1000 / 60 - frameTime > 0)
{
Sleep(1000 / 30 - frameTime);//控制运行速度
}
EndBatchDraw();//结束绘制
}
//====================================== 主程序===============================
//主程序
int main()
{
InitData();
while (true)
{
RunGame();
}
getchar();
closegraph();
return 0;
}
六、透明贴图工具和定时器
drawImg()用于贴无背景图片与putimage()相似
#pragma once
#include<easyx.h>
void drawImg(int x, int y, IMAGE *src);
void drawImg(int x, int y,int dstw, int dstH, IMAGE *src, int srcX, int srcY);
void drawImg(int x, int y, IMAGE *src)
{
DWORD* pwin = GetImageBuffer();
DWORD* psrc = GetImageBuffer(src);
int win_w = getwidth();
int win_h = getheight();
int src_w = src->getwidth();
int src_h = src->getheight();
int real_w = (x + src_w > win_w) ? win_w - x : src_w;
int real_h = (y + src_h > win_h) ? win_h - y: src_h;
if (x < 0) { psrc += -x; real_w -= -x; x = 0; }
if (y < 0) { psrc += (src_w*-y); real_h -= -y; y = 0; }
pwin +=(win_w*y + x);
for (int iy = 0; iy < real_h; iy++)
{
for (int ix = 0; ix < real_w; ix++)
{
byte a = (byte)(psrc[ix] >> 24);
if (a > 100)
{
pwin[ix] = psrc[ix];
}
}
pwin += win_w;
psrc += src_w;
}
}
void drawImg(int x, int y, int dstW, int dstH, IMAGE* src, int srcX, int srcY)
{
DWORD* pwin = GetImageBuffer();
DWORD* psrc = GetImageBuffer(src);
int win_w = getwidth();
int win_h = getheight();
int src_w = src->getwidth();
int src_h = src->getheight();
int real_w = (x + dstW> win_w) ? win_w - x : dstW;
int real_h = (y + dstH > win_h) ? win_h - y : dstH;
if (x < 0) { psrc += -x; real_w -= -x; x = 0; }
if (y < 0) { psrc += (dstW*-y); real_h -= -y; y = 0; }
pwin += (win_w*y + x);
for (int iy = 0; iy < real_h; iy++)
{
for (int ix = 0; ix < real_w; ix++)
{
byte a = (byte)(psrc[ix + srcX + srcY*src_w] >> 24);
if (a > 100)
{
pwin[ix] = psrc[ix + srcX + srcY*src_w];
}
}
pwin += win_w;
psrc += src_w;
}
}
//定时器
bool timer(int ms, int id)
{
static int start[5];
int end = clock();
if (end - start[id] >= ms)
{
start[id] = end;
return true;
}
return false;
}
总结:实现的内容比较简单,冗余,仅用于学习参考,有兴趣的可以自行添加更多功能,多动手才能有更多的进步哟。
素材:链接:https://pan.baidu.com/s/1XDbXx3C83kyyzkQZL3mmiQ
提取码:65qv