(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)
今天我们来尝试用easyx图形库实现c语言小游戏-飞机大战(源代码和图片已经在结尾给出)
先引用头文件
#include
#include//用于后期制作定时器
#include//便于引入easyx窗口及其函数
#include//用于按键控制
接着就是一系列数据的准备工作,先创建一个窗口,为了后续方便,我们用枚举法定义窗口的变量
enum My{WIDTH=591,HEIGHT=864}
int main()
{
initgraph(WIDTH,HEIGHT);
}
然后对飞机进行定义
struct Plance//定义飞机
{
int x;
int y;
int live;//是否存活
}player;
接着就是对飞机的相关数据初始化
void gameInit()//初始化数据
{
player.x = WIDTH / 2;
player.y = HEIGHT - 120;
player.live = 1;}
别忘了将飞机的图片加载进去,在我之前的博客中有讲到如何插入图片http://t.csdn.cn/PcJiP
这里我们用两张图片对飞机进行绘制,这样的话才能更好得实现动态图画,减少图片背景的影响
IMAGE bk;//背景图片
IMAGE img_role[2];//飞机图片void loadImage()//加载图片
{
loadimage(&bk, "./background.jpg");
loadimage(&img_role[0], "./planeNormal_1.jpg");
loadimage(&img_role[1], "./planeNormal_2.jpg");}
void gameDraw()//输出图片
{
putimage(0, 0, &bk);
putimage(player.x, player.y, &img_role[0],NOTSRCERASE);//NOTSRCERASE贴掩码图
putimage(player.x, player.y, &img_role[1],SRCINVERT);//SRCINVERT贴背景图}
飞机的相关数据创建好后,就是如何移动飞机,我这里用两种移动方法,在我之前的博客中讲到如何使用键盘移动(http://t.csdn.cn/Nh8Tz) 注意:这里要注意边界情况,使飞机在触碰边缘后停止移动,防止飞机出界
void playerMove(int speed)//移动玩家
{
if (_kbhit())//判断是否有按键,有按键才处理,无按键就不处理
{
char input = _getch(); //控制台可以直接读取我按的键
switch (input)
{
case 'w':
player.y -= speed;
if (player.y <= 0) //边界处理,下面也是这样
player.y = 0;
break;
case 's':
player.y += speed;
if (player.y >= HEIGHT - 120)
player.y = HEIGHT - 120;
break;
case 'a':
player.x -= speed;
if (player.x <= -40)
player.x = -40;
break;
case 'd':
player.x += speed;
if (player.x >= WIDTH - (117 / 2))
player.x = WIDTH - (117 / 2);
break;
}
//与上一种按键方式相比,这一种更加丝滑流畅
if (GetAsyncKeyState(VK_UP))//上键
{
player.y -= speed;
if (player.y <= 0)
player.y = 0;
}
if (GetAsyncKeyState(VK_DOWN))//下键
{
player.y += speed;
if (player.y >= HEIGHT - 120)
player.y = HEIGHT - 120;
}
if (GetAsyncKeyState(VK_LEFT))//左键
{
player.x -= speed;
if (player.x <= -40)
player.x = -40;
}
if (GetAsyncKeyState(VK_RIGHT))//右键
{
player.x += speed;
if (player.x >= WIDTH - (117 / 2))
player.x = WIDTH - (117 / 2);
}
}
}
在飞机变量创建好以后,接下来就是对子弹的变量进行定义以及输出 ,这里为了方便(偷懒),我直接用之前飞机的定义来定义子弹
struct Plance//定义飞机
{
int x;
int y;
int live;//是否存活
}player,bull[BULLET_NUM]
然后就是子弹的初始化,这里我枚举了子弹的数量
enum My{WIDTH=591,HEIGHT=864,BULLET_NUM=15}
void gameInit()//初始化数据
{
player.x = WIDTH / 2;
player.y = HEIGHT - 120;
player.live = 1;
for (int i = 0; i < BULLET_NUM; i++)//初始化子弹
{
bull[i].x;
bull[i].y;
bull[i].live = 0;
}}
子弹的图片输出与飞机一样
IMAGE img_bull[2];
loadimage(&img_bull[0], "./bullet1.jpg");
loadimage(&img_bull[1], "./bullet2.jpg");
for (int i = 0; i < BULLET_NUM; i++)
{
if (bull[i].live == 1)
{
putimage(bull[i].x, bull[i].y, &img_bull[0], NOTSRCERASE);
putimage(bull[i].x, bull[i].y, &img_bull[1] , SRCINVERT);
}
}
然后就是对子弹的控制
if (GetAsyncKeyState(VK_SPACE))//空格发射子弹
{
createBullet();
}
接下来就是输出敌机的相关数据,这里我们用两种飞机,一个大飞机和一个小飞机,他们的血量,大小各有差异
enum My{WIDTH=591,HEIGHT=864,BULLET_NUM=15,ENEMY_NUM=10,BIG,SMALL};
struct Plance//定义飞机
{
int x;
int y;
int live;//是否存活
int width;
int height;
int hp;
int type;//敌机类型
}player,bull[BULLET_NUM],enemy[ENEMY_NUM];
这里我们对不同类型的飞机的血量,出现概率都要有所调整
void enemyHp(int i)
{
if (rand() % 10 == 2 )//十分之一的概率出现大飞机
{
enemy[i].type = BIG;
enemy[i].hp = 3;
enemy[i].width = 104;
enemy[i].height = 148;
}
else
{
enemy[i].type = SMALL;
enemy[i].hp = 1;
enemy[i].width = 52;
enemy[i].height = 39;
}
}for (int i = 0; i < ENEMY_NUM; i++)//初始化敌机
{
enemy[i].live = 0;
enemyHp(i);
}
因为上面用到了rand()函数,为了避免重复,我们在主函数中创建随机函数种子
srand((unsigned int)time(0));//生成随机数种子
初始化敌机后,接着就是敌机的绘制与移动,绘制与上面相同,这里我就具体讲述移动
void createEnemy()
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 0)
{
enemy[i].live = 1;
enemy[i].x = rand() % (WIDTH - 60);
enemy[i].y = 0;
enemyHp(i);//重置血量,防止一波打完以后飞机不在产生
break;
}
}
}
void enemyMove(int speed)
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 1)
{
enemy[i].y += speed;
if (enemy[i].y > HEIGHT)
{
enemy[i].live = 0;
}
}
}
}
因为飞机会同时下落,所以这里我们创建一个定时器,使飞机下落时有延续
定时器,顾名思义,肯定会用到时间的,所以,获取时间我们就要用到time库,所以要引用头文件
要做出定时器,首先就需要设定静态变量t1和t2,因为只有在设置静态变量的时候,t1和t2的值才不会在循环中每执行一次就变成原来初始化的值,我们要将其固定住,在下一次循环中执行的初始值为上一次执行后的结果值,所以,就自然地会考虑到使用静态变量这种方法。
接下来,就是在循环中设置一个条件,比如你想两次改变之间的间隔为500毫秒的时候,就可以在条件语句中写入t2 - t1 > 500,这样,就可以很好地表示出来。然后在条件语句中的语句块中先写你想要让程序执行的代码或自定义函数等,并将t2赋值给t1。如果在不满足条件语句的时候,或者执行完条件语句块之后,再将当前时间赋值给t2,所以,这里就需要用到clock函数来获取当前时间。这样,就可以把最基本的定时器很简单的就做出来了,具体的代码如下:
static DWORD t1, t2,t3,t4;//定时器
if (t2 - t1 > 500)
{
createEnemy();
t1 = t2;
}
t2 = clock();
if (t4 - t3 > 5)
{
enemyMove(1);
t3 = t4;
}
t4 = clock();
最后就是如何击落敌机了
void playPlance()//击落敌机
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 0)
{
continue;
}
for (int k = 0; k < BULLET_NUM; k++)
{
if (bull[k].live == 0)
{
continue;
}
if (bull[k].x > enemy[i].x && bull[k].x&& bull[k].y>enemy[i].y && bull[k].y < enemy[i].y + enemy[i].height)
{
bull[k].live = 0;
enemy[i].hp--;
}
}
if (enemy[i].hp <= 0)
{
enemy[i].live = 0;}
}
}
总体代码就是这样,如果大家发现bug或者有更好的方法 ,欢迎大家一起来讨论呀,一起加油
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include//用于后期制作定时器
#include//便于引入easyx窗口及其函数
#include//用于按键控制
IMAGE bk;
IMAGE img_role[2];
IMAGE img_bull[2];
IMAGE my_enemy[2];
IMAGE enemyPlane[2];
enum My{WIDTH=591,HEIGHT=864,BULLET_NUM=15,ENEMY_NUM=10,BIG,SMALL};
struct Plance//定义飞机
{
int x;
int y;
int live;//是否存活
int width;
int height;
int hp;
int type;//敌机类型
}player,bull[BULLET_NUM],enemy[ENEMY_NUM];
void loadImage()//加载图片
{
loadimage(&bk, "./background.jpg");
loadimage(&img_role[0], "./planeNormal_1.jpg");
loadimage(&img_role[1], "./planeNormal_2.jpg");
loadimage(&img_bull[0], "./bullet1.jpg");
loadimage(&img_bull[1], "./bullet2.jpg");
loadimage(&my_enemy[0], "./enemy_1.jpg");
loadimage(&my_enemy[1], "./enmey_2.jpg");
loadimage(&enemyPlane[0], "./enemyPlane1.jpg");
loadimage(&enemyPlane[1], "./enemyPlane2.jpg");
}
void enemyHp(int i)
{
if (rand() % 10 == 2 )//十分之一的概率出现大飞机
{
enemy[i].type = BIG;
enemy[i].hp = 3;
enemy[i].width = 104;
enemy[i].height = 148;
}
else
{
enemy[i].type = SMALL;
enemy[i].hp = 1;
enemy[i].width = 52;
enemy[i].height = 39;
}
}
void gameInit()//初始化数据
{
player.x = WIDTH / 2;
player.y = HEIGHT - 120;
player.live = 1;
for (int i = 0; i < BULLET_NUM; i++)//初始化子弹
{
bull[i].x;
bull[i].y;
bull[i].live = 0;
}
for (int i = 0; i < ENEMY_NUM; i++)//初始化敌机
{
enemy[i].live = 0;
enemyHp(i);
}
}
void createBullet()
{
for (int i = 0; i < BULLET_NUM; i++)
{
if (bull[i].live == 0)
{
bull[i].x = player.x+60 ;
bull[i].y = player.y ;
bull[i].live = 1;
break;
}
}
}
void BulletMove()
{
for (int i = 0; i < BULLET_NUM; i++)
{
if (bull[i].live == 1)
{
bull[i].y -= 1;
if (bull[i].y < 0)
{
bull[i].live = 0;
}
}
}
}
void gameDraw()//输出图片
{
BeginBatchDraw();//开始绘图
putimage(0, 0, &bk);
putimage(player.x, player.y, &img_role[0],NOTSRCERASE);
putimage(player.x, player.y, &img_role[1],SRCINVERT);
//绘制子弹
for (int i = 0; i < BULLET_NUM; i++)
{
if (bull[i].live == 1)
{
putimage(bull[i].x, bull[i].y, &img_bull[0], NOTSRCERASE);
putimage(bull[i].x, bull[i].y, &img_bull[1] , SRCINVERT);
}
}
//绘制敌机
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 1)
{
if (enemy[i].type == BIG)
{
putimage(enemy[i].x, enemy[i].y, &enemyPlane[0], NOTSRCERASE);
putimage(enemy[i].x, enemy[i].y, &enemyPlane[1], SRCINVERT);
}
else
{
putimage(enemy[i].x, enemy[i].y, &my_enemy[0], SRCINVERT);
putimage(enemy[i].x, enemy[i].y, &my_enemy[1], NOTSRCERASE);
}
}
}
EndBatchDraw();// 结束绘图
}
void createEnemy()
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 0)
{
enemy[i].live = 1;
enemy[i].x = rand() % (WIDTH - 60);
enemy[i].y = 0;
enemyHp(i);//重置血量,防止一波打完以后飞机不在产生
break;
}
}
}
void enemyMove(int speed)
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 1)
{
enemy[i].y += speed;
if (enemy[i].y > HEIGHT)
{
enemy[i].live = 0;
}
}
}
}
void playerMove(int speed)//移动玩家
{
if (_kbhit())//判断是否有按键,有按键才处理,无按键就不处理
{
char input = _getch(); //控制台可以直接读取我按的键
switch (input)
{
case 'w':
player.y -= speed;
if (player.y <= 0) //边界处理
player.y = 0;
break;
case 's':
player.y += speed;
if (player.y >= HEIGHT - 120)
player.y = HEIGHT - 120;
break;
case 'a':
player.x -= speed;
if (player.x <= -40)
player.x = -40;
break;
case 'd':
player.x += speed;
if (player.x >= WIDTH - (117 / 2))
player.x = WIDTH - (117 / 2);
break;
}
//与上一种按键方式相比,这一种更加丝滑流畅
if (GetAsyncKeyState(VK_UP))//上键
{
player.y -= speed;
if (player.y <= 0)
player.y = 0;
}
if (GetAsyncKeyState(VK_DOWN))//下键
{
player.y += speed;
if (player.y >= HEIGHT - 120)
player.y = HEIGHT - 120;
}
if (GetAsyncKeyState(VK_LEFT))//左键
{
player.x -= speed;
if (player.x <= -40)
player.x = -40;
}
if (GetAsyncKeyState(VK_RIGHT))//右键
{
player.x += speed;
if (player.x >= WIDTH - (117 / 2))
player.x = WIDTH - (117 / 2);
}
if (GetAsyncKeyState(VK_SPACE))//空格发射子弹
{
createBullet();
}
}
}
int Timer(int ms)
{
static DWORD t1, t2;//定时器
if (t2 - t1 > ms)
{
t1 = t2;
return 1;
}
t2 = clock();
}
void playPlance()//击落敌机
{
for (int i = 0; i < ENEMY_NUM; i++)
{
if (enemy[i].live == 0)
{
continue;
}
for (int k = 0; k < BULLET_NUM; k++)
{
if (bull[k].live == 0)
{
continue;
}
if (bull[k].x > enemy[i].x && bull[k].xenemy[i].y && bull[k].y < enemy[i].y + enemy[i].height)
{
bull[k].live = 0;
enemy[i].hp--;
}
}
if (enemy[i].hp <= 0)
{
enemy[i].live = 0;
}
}
}
int main()
{
srand((unsigned int)time(0));//生成随机数种子
//创建一个窗口
initgraph(WIDTH,HEIGHT);
loadImage();
gameInit();
//双缓冲绘图
BeginBatchDraw();
while (1)
{
gameDraw();
FlushBatchDraw();//刷新一下,防止黑屏
playerMove(30);
BulletMove();
static DWORD t1, t2,t3,t4;//定时器
if (t2 - t1 > 500)
{
createEnemy();
t1 = t2;
}
t2 = clock();
if (t4 - t3 > 5)
{
enemyMove(1);
t3 = t4;
}
t4 = clock();
playPlance();
}
EndBatchDraw();
return 0;
}