你们好呀!
相信大家学习着c/c++的时候,总是感觉黑黑的运行界面感觉不太漂亮
本人作为一条大学咸鱼,也这么觉得。后来啊接触到了Easyx库。毕竟咸鱼嘛,学的不怎么样。但是这个库请各位和我一样的小白看一看,个人觉得不错(咸鱼的认可HHHH),对新手很友好。
废话不多说!看下面咱们进入正题↓
这就是用Easyx做出来的一个简单小游戏啦,自己做的不太好 因为是懒虫(HHHH),飞机和敌人什么的图片都没有弄好,都是在网上随便找的图片。敌人我就随便放了个照片 相信大家一定比我设计的好!!(飞机是真的丑哈哈哈)。
接下来呢是我的开始界面↓
大家可以根据自己的想象随意设计就行啦
看到这里如果你有动手写一个简单小游戏的冲动 吼吼吼 !先看完再说 嘿嘿
先把Easyx的官网给各位!!
小弟双手奉上Easyx官网
进入官网里面就可以下载了 对了别忘记下载那个帮助文档
里面就是easyx库的函数基本用法大家有什么不明白的可以在里面查找
也可以参考https://blog.csdn.net/sandalphon4869/article/details/80862023
这里先和大家说一下Easyx的头文件
#include
使用这个就可以用Easyx图形库了 当然基本的头文件也不能忘记!
1.开始界面的设计
就我个人而言的开始界面设计是很偷懒的,看完之后相信有艺术细胞的你一定可以设计的很好看
简单绘制的基本Easy函数不多说了咱们直接上代码
void startshow()
{
MOUSEMSG m;//鼠标
settextcolor(YELLOW);
settextstyle(70, 0, _T("宋体"));
outtextxy(170, 50, _T("飞机小游戏"));
settextcolor(YELLOW);
setfillcolor(BLUE);
solidrectangle(250, 200, 500, 250);
settextstyle(40, 0, _T("黑体"));
setbkmode(TRANSPARENT);
outtextxy(300, 200, _T("开始游戏"));
setlinecolor(GREEN);
line(251, 201, 501, 201);
line(251, 201, 251, 251);
line(251, 251, 501, 251);
line(501, 251, 501, 201);
while (1)
{
m = GetMouseMsg();
if (m.x >= 250 && m.x <= 500 && m.y >= 200 && m.y <= 250)
{
setlinecolor(RED);
rectangle(240, 190, 510, 260);
if (m.uMsg == WM_LBUTTONDOWN )//鼠标按下
{
//closegraph();
break;
}
}
else
{
//鼠标不在框框内就覆盖之前的绿色框
setlinecolor(BLACK);
rectangle(240, 190, 510, 260);
}
}
在这里我要说的问题↓
settextstyle(40, 0, _T("黑体"));
settextstyle(40, 0, "黑体"); 也可以这样子写
那么为什么能这么写呢 我本人使用vs2019编辑器
你需要找到项目再找到属性
然后进入到属性页了把字符集改成我下面这个就可以啦
之后咱们就可以直接这样子写了
settextstyle(40, 0, "黑体");
咱们再来说一下鼠标的问题
这就是Easyx的鼠标函数 大家可以看一下 记一下
咱们再来看一下之前的关于鼠标函数的代码段并附一张开始界面的图片
while (1)
{
m = GetMouseMsg();
if (m.x >= 250 && m.x <= 500 && m.y >= 200 && m.y <= 250)
{
setlinecolor(RED);
rectangle(240, 190, 510, 260);
if (m.uMsg == WM_LBUTTONDOWN )//鼠标按下
{
//closegraph();
break;
}
}
else
{
//鼠标不在框框内就覆盖之前的绿色框
setlinecolor(BLACK);
rectangle(240, 190, 510, 260);
}
细心的人一定发现了在开始游戏的框框外出现了红色的框子,这是为什么呢?因为我把鼠标放在了开始游戏上,所以就有了如上的效果,具体的代码呢也在上边,实现比较简单,这里也不多说了。接下来咱们要进入游戏的设计了
我们先来定义
#define max_e 15//敌机最大数量
#define max_gun_num 25 //子弹夹最大数量
DWORD t1, t2;
int hight = 720;//x
int weight = 480;//y
int num=0;
IMAGE player[4];
IMAGE img;
IMAGE m[2];
不必太纠结这几个定义的究竟是来干嘛的,往后看聪明的你自然会明白的。
大家一定都学过结构体,就算是没学好也一定听过的(毕竟我都听过哈哈)
那好,咱们就接下来定义结构体
struct plane //飞机
{
int plane_x;//x坐标
int plane_y;//y坐标
bool flag;//飞机存活标志
}plane, enemy[max_e];
struct gun //子弹
{
int gun_x;
int gun_y;
bool flag;
}gun[max_gun_num];
已经定义完结构体咱们接下来“建飞机”
void creatplane()
{
loadimage(&player[0], "D:\\迅雷下载\\redship1.png");
loadimage(&player[1], "D:\\迅雷下载\\redship.png");
//数据初始化
plane.plane_x = weight / 2;
plane.plane_y = hight / 2;
plane.flag = true;
putimage(plane.plane_x, plane.plane_y, &player[0]);
putimage(plane.plane_x, plane.plane_y, &player[1]);
//数据初始化
}
关于loadimage函数和putimage函数就不多说,上面的参考文章和easyx自带的文档里都有的。可以看出我们已经建立了我们的飞机开始的位置。
接下来就是飞机的移动问题
void playerplanemove(int speed)//玩家飞机的移动
{
//GetAsyncKeyState
if (GetAsyncKeyState(VK_UP))
{
plane.plane_y -= speed;
if (plane.plane_y < 0)
{
plane.plane_y = speed;
}
}
if (GetAsyncKeyState(VK_DOWN))
{
plane.plane_y += speed;
if (plane.plane_y > 470)
{
plane.plane_y -= speed;
}
}
if (GetAsyncKeyState(VK_RIGHT))
{
plane.plane_x += (speed);
if (plane.plane_x > 700)
{
plane.plane_x -= speed;
}
}
if (GetAsyncKeyState(VK_LEFT))
{
plane.plane_x -= (speed);
if (plane.plane_x < 0)
{
plane.plane_x += speed;
}
}
}
提一下GetAsyncKeyState(),如果大家学过windowsAPI函数应该会知道这个,就是调用GetAsyncKeyState函数得到某个键的状态,然后判断返回值的最高位是不是1,如果是则表示这个处于按下状态,当然你也可以用_getch() 看你的喜好再看上边的代码是不是就很清楚了,plane.plane_y是飞机的y坐标,plane.plane_x是飞机的x坐标,我们通过改变这些坐标来改变飞机的位置,同时图片的位置也得到了改变。而plane.plane_y < 0,plane.plane_y > 470等是用来定义边界的防止飞机飞出去消失,我定义了一个initgraph(720, 480)函数。 作用就是初始化图形系统。所以就有了720x480的屏幕
↓↓↓↓↓↓↓
接下来就是创建咱们的敌人了。因为敌人肯定是会动的,而且是随机出现的这里呢就用到了随机函数rand() 但是呢如果只是用着一个rand()第一次是会随机出现,但是第二次就会和上次出现的位置一样了我们还需要设置一个函数srand(),如果不清楚的可以参考此文章
这样子我们的随机种子,随机函数就都完成了,我们的敌机就这样产生随机的坐标,别忘记设置敌机的flag。
void randCoordinate(int* x, int* y)
{
y[0] = rand() % 71;
x[0] = rand() % 671;
}
for (int i = 0; i < max_e; i++)
{
randCoordinate(&enemy[i].plane_x, &enemy[i].plane_y);
enemy[i].flag = true;
}
loadimage(&m[0], "D:\\1558533806269.jpg", 50, 50, 1)
for (int i = 0; i < max_e; i++)
{
putimage(enemy[i].plane_x, enemy[i].plane_y, &m[0]);
}
这样子敌机就随机出来了。接下来就是敌机的移动了
void enemymove(int speed)
{
for (int i = 0; i < max_e; i++)
{
if (enemy[i].flag == true)
{
if (i > (max_e - 5))
{
enemy[i].plane_y += (speed +1);
}
else
enemy[i].plane_y += speed;
if(enemy[i].plane_y > weight || hit(&enemy[i]))
enemy[i].flag=false;
}
else
{
enemy[i].flag = true;
randCoordinate(&enemy[i].plane_x, &enemy[i].plane_y);
}
}
}
if (i > (max_e - 5))
{
enemy[i].plane_y += (speed +1);
}
因为敌机虽然会出现,但是它们随机下降的速度是一样
所以我们要设置几台快速下降的飞机来达到有快有慢的效果
if(enemy[i].plane_y > weight || hit(&enemy[i]))
这个if判断语句里的hit函数大家先不用管,马上在后边会给大家说明,hit函数大家从字面上就可以理解,就是碰撞的意思(子弹与敌机碰撞,敌机死亡,子弹死亡)
那么关于敌机和自己的飞机就创建出来了,而我们还缺一个东西,是什么呢就是上边提到了一点的子弹
所以我们继续创建子弹。
在上边我们建立了子弹的结构体,所以不需要再创建了。
loadimage(&player[2], "D:\\迅雷下载\\font_049.png", 10, 10, 1);
for (int j = 0; j < max_gun_num; j++)//子弹的初始化
{
gun[j].flag = false;
gun[i].gun_x = weight / 2;
gun[i].gun_y = hight / 2;
}
for (int j = 0; j < max_gun_num; j++)//子弹的显示
{
if (gun[j].flag == true)
{
putimage(gun[j].gun_x, gun[j].gun_y, &player[2]);
}
}
处理好以上这些就可以创建子弹了 代码如下
void creatgun()//创建子弹;
{
for (int j = 0; j < max_gun_num; j++)
{
if (gun[j].flag == false)
{
gun[j].flag = true;
gun[j].gun_x = plane.plane_x + 20;
gun[j].gun_y = plane.plane_y - 10;
break;
}
}
}
gun[j].gun_x = plane.plane_x + 20; 这里是为了和我的飞机图片的中间对齐,
gun[j].gun_y = plane.plane_y - 10;也就是子弹要在枪口出去
接下来就是子弹的移动
void gunmove(int speed)
{
for (int j = 0; j < max_gun_num; j++)
{
if (gun[j].flag == true)
{
gun[j].gun_y -= speed;
}
if (gun[j].gun_y < 0)
{
gun[j].flag = false;
}
}
return;
}
和自己飞机的移动差不多,但是又差了点什么,到底差什么呢?还有按键对吧
好接下来我们再设置按键,在按键里会给大家一个重要的函数设置
DWORD t1, t2;
if (GetAsyncKeyState(VK_SPACE) && (t2 - t1) > 200)//子弹按键
{
creatgun();
t1 = t2;
}
t2 = GetTickCount64();
gunmove(2);
}
DWORD大家可以去找一下用法,在windows API函数中很常见的。
而GetTickCount()的用法就是获取从电脑开机后,开始计时的毫秒数。,关于这个函数呢是在b站上c语言编程plus老师那里得到的灵感 自己用了感觉可以,当然也可以有别的用法那么我为什么要用这个函数呢,因为就是我个人在学windows程序设计,里面有很多的api函数的概念,所以我感觉这个函数很好。正好加深一遍我的理解(白学了,当时都没想到哈哈哈),这里呢大家也可以不加这个函数试一下,你就会发现你的子弹就会变成了一颗,结合上面代码,我觉得你能懂为什么。
那么碰撞函数是干嘛的呢,上文已经提到了,就是判断敌机和子弹是否碰撞到,如果子弹和敌机碰撞到呢,敌机与子弹的存活标志就是死亡,也就是false,然后再结合咱们上面写的,void creatgun()就可以达到近似无限生成子弹的效果,咱们看代码
bool hit(struct plane* enemy)
{
for (int k = 0; k < max_gun_num; k++)
{
if (gun[k].flag == false)
continue;
if (gun[k].gun_y>enemy->plane_y&&gun[k].gun_y<enemy->plane_y +50&&
gun[k].gun_x>enemy->plane_x&& gun[k].gun_x < enemy->plane_x + 50)
{
gun[k].flag = false;
num++;
return true;
}
}
return false;
}
当时我写的时候卡在了这个碰撞函数这里,然后接下来!!当然是找万能的舍友了哈哈哈哈,我俩弄了很长时间,然后他帮助我写了下来,毕竟是我班的学霸!
最后就是我们的分数啦
void showscore()
{
char s[100];
sprintf_s(s, "score:%d", num);
outtextxy(0, 0, s);
}
也很简单
那么这个简单的小游戏核心差不多就到这里了,还有就是main函数里面
BeginBatchDraw();FlushBatchDraw();EndBatchDraw();这三个函数的位置,这三个函数也是easyx里面的,大家可以去看看,还比较重要的。看了之后你自然会明白的,还有一些显示函数就没有写了,在下面的源码里面都有的。这里只是和大家说一下我认为的核心思路
能改进的地方还有很多很多,例如你可以再增加敌机的hp也就是血量,还有自己飞机的血量,
还有敌人boss,敌人boss的攻击啦…等等。快动动你聪明的大脑吧!第一次写博客感觉写的不太详细,各位见谅!抱拳了,欢迎大神来指点小弟!
没错!1000的分数,不要以为是我自己玩的,我这么懒,才不会玩呢哈哈哈哈,这是根据内存改的,不知道大家听过CE没,就是一个游戏作弊软件,用windowsAPI函数就可以达到以上找内存,然后修改数值的功能,当然肯定没有CE那么好,我也只是看书的时候看到了,就弄了一下午感觉还很有意思,如果你对这个也有兴趣的话,可以关注我后续的文章,谢谢啦
最后呢祝大家学业有成,不要再浑浑噩噩下去,大学四年的时光转瞬即逝,即使你混了几年也别灰心,加油加油!
加油追赶吧,曾经那个少年!
附上源码
https://pan.baidu.com/s/1mFZhIkjzfM3jSHhFaR9qGg
提取码qtji