最近用C语言写了个小游戏,现在分享出来,完整的程序请直接点击文末的链接。
本文将从VS的安装,Easyx图形库的安装以及使用,一点点的讲起,每一步尽量详细,希望能对读者有所帮助。
1.VS的安装
我使用的是VS2022版本,下载可以去官网下载,链接放在这里,点击即可。也有读者从别处下载,但我建议还是从官网下载即可。官网提供社区版、专业版、企业版,社区版是免费的,所以选择社区版即可。
Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com)
安装界面如图所示,注意,图中标注的两个地方一定要勾选,也可以把扩展选项勾选上,其他的则根据需要进行勾选。然后是安装位置尽量选择C盘以外的盘。等待片刻后安装便完成了。
2.安装Easyx图形库
读者可能发现了,在我们平时学习C语言的时候,总是在一个黑乎乎的窗口运行的,好像C语言只能在这个窗口运行一样,只能用于一些简单的命令的输出,那么这时候就有人问了,可不可以用C语言创造一些其他的窗口,做些其他的事呢?答案是当然可以的。细心的读者可能也曾看到过说Windows操作系统便是用C语言写的,Windows的窗口是丰富的,而且交互性很高。那么这是如何实现的呢?接下来我所要提到的就登场了----Easyx图形库,Easyx提供很多的窗口操作,以及绘图函数,在程序的头文件中调用它便能使用它创造各种各样的图形以及窗口,自然也能做一个小小的游戏了。
下面介绍一下如何安装。EasyX Graphics Library for C++
直接进其官网下载进行,然后点击其安装包按提示进行安装。
Easyx会自动检测安装的VS版本,然后点击安装即可。它的整个安装过程是很简单的而且安装包也很小。之后就能在VS中直接使用其头文件了。
笔者额外的说几句,市面上有很多图形库,但我认为easyx简单易上手,供初学者使用很方便。所以本文选择了使用easyx用来开发一个小游戏。而且在看文本文之后,应该能初步的了解游戏的开发以及图形操作界面的设计的过程。
3.VS中创建项目
点击创建新项目时,便会弹出如上的窗口,有这么多的类型,那么该选择哪一个呢。如果之前接触过VS的读者一定知道该选择哪一个,如果没接触过的读者也不用着急,我接下来也会介绍。
在所有的2语言一栏,选择C++语言(C++语言兼容C语言),然后选择创建一个空项目。随便为项目取一个名字,便能成功创建。
在解决方案一栏,鼠标右击源文件,选择添加,新建项。至于程序写完之后可点击菜单栏里的调试,选择开始执行不调试。至此,程序编写的前期准备工作已经完成,接下来便是程序的编写。
4.进入正文
前面主要介绍了VS的安装创建等基础内容,已经掌握这部分内容的读者请跳过。
4.1简单的窗口以及图形的绘制
#include<stdio.h>
#include<graphics.h>
int main()
{
initgraph(400, 500);//创建一个宽400,高500的窗口。
setbkcolor(BLUE);//设置背景色为蓝色
cleardevice();//用蓝色填充背景色
circle(50, 100, 60);//在坐标50,100的位置绘制一个半径为60的圆。
while(1)
{ }
return 0;
}
以上的代码便是创造一个背景色为蓝色的窗口,并绘制一个圆。看到这里,读者会发现,原来使用这几行代码便能创建窗口以及图形,看来制作一个小游戏也会非常简单的,也令你信心大增。事实也的确如此,整个小游戏的制作也是比较简单的。
当然,细心的读者可能会发现了,这几个函数的后面的参数是什么呢,坐标是怎么规定的呢,这里要提供几个知识点了,使用easyx库进行图形的创建的时候,坐标是以窗口的左上角为基准。
所以上述代码中的坐标值皆以此为基准。另外有的读者自己创建窗口的时候发现,怎么我的代码明明没有报错,为什么没有创建窗口呢,或者窗口一闪而过。解决的办法很简单,只需要添加一个循环函数就行,或者使用_getch()(这是一个阻塞函数,其功能与scanf的功能类似,当没有检测到用户的输入,便会一直运行。当然使用这个函数需要添加conio.h这个头文件)。
如果有读者想深入了解Easyx的函数使用,可研读官网文档EasyX 文档 - 基本说明
4.2飞机大战小游戏的设计
以上便是游戏的整个设计,当然笔者只是提供了基础的设计,读者可按照自己的想法来进行设计。其实现的代码原理是相通的。
4.3创建对应的函数
为使代码简洁,多采用函数的形式。
4.3.1实现背景以及飞机和子弹的绘制
IMAGE bk;
IMAGE img_player[2];
IMAGE img_bullet[2];
IMAGE img_enemy[2][2];
void loadImg()//加载背景图片
{
loadimage(&bk, "./images/background.png");//./表示相对路径的写法 \\绝对路径的写法。
loadimage(&img_player[0], "./images/me2222.png");//加载玩家飞机图片掩码图
loadimage(&img_player[1], "./images/me212.png");// 加载玩家飞机原图
loadimage(&img_bullet[0], "./images/bullet11.png");//加载子弹掩码图以及原图
loadimage(&img_bullet[1], "./images/bullet1.png");
//加载敌机的图片
loadimage(&img_enemy[0][0], "./images/enemy11.png");
loadimage(&img_enemy[0][1], "./images/enemy111.png");
loadimage(&img_enemy[1][0], "./images/enemy22.png");
loadimage(&img_enemy[1][1], "./images/enemy222.png");
}
void windraw()//显示图片
{
loadImg();
putimage(0, 0, &bk);//显示背景图片
putimage(playerp.x,playerp.y, &img_player[0],NOTSRCERASE);//显示玩家飞机图片,利用三元光栅操作符。
putimage(playerp.x,playerp.y, &img_player[1],SRCINVERT);//SRCINVET
for (int i = 0; i < bullet_max; i++)
{
if (bullet[i].live)
{
putimage(bullet[i].x, bullet[i].y, &img_bullet[0],NOTSRCERASE);//显示子弹图片
putimage(bullet[i].x, bullet[i].y, &img_bullet[1], SRCINVERT);
}
}
for (int j = 0; j < enemy_num; j++)
{
if (enemy[j].live)
{
if (enemy[j].type == BIG)
{
putimage(enemy[j].x, enemy[j].y, &img_enemy[1][0], NOTSRCERASE);//显示敌机图片
putimage(enemy[j].x, enemy[j].y, &img_enemy[1][1], SRCINVERT);
}
else
{
putimage(enemy[j].x, enemy[j].y, &img_enemy[0][0], NOTSRCERASE);
putimage(enemy[j].x, enemy[j].y, &img_enemy[0][1], SRCINVERT);
}
}
}
}
飞机的透明贴图以及如何制作掩码图请参考笔者的这篇文章https://mp.csdn.net/mp_blog/creation/editor/135708223
接下来考虑,飞机的移动,以及子弹的发射。
struct Plane//创建一个结构体
{
int x;
int y;
bool live;
int width_en;//敌机的图片的宽度
int height_en;//敌机的图片的高度
int hv;//敌机血量
int type;//敌机的类型
}playerp,bullet[bullet_max],enemy[enemy_num];void playermove(int speed)//玩家的移动
{
if (GetAsyncKeyState(VK_UP))//当从键盘识别到“上键”的按住时,便移动飞机的Y坐标,下面的类似。
{
if (playerp.y >= 0)//限制飞机的移动范围在窗口的大小之内
{
playerp.y -= speed;
}
}
if (GetAsyncKeyState(VK_DOWN))
{
if (playerp.y < hight-80)
{
playerp.y += speed;
}
}
if (GetAsyncKeyState(VK_LEFT))
{
if (playerp.x > -50)
{
playerp.x -= speed;
}
}
if (GetAsyncKeyState(VK_RIGHT))
{
if (playerp.x < width-50)
{
playerp.x += speed;
}
}
if (GetAsyncKeyState(VK_SPACE)&&Timer(300,2))
{
creatbullet();
}
}
void creatbullet()
{
for (int i = 0; i < bullet_max; i++)
{
if (!bullet[i].live)
{
bullet[i].x = playerp.x + 50;
bullet[i].y = playerp.y ;
bullet[i].live = true;
break;
}
}
}
void bulletmove(int speed)
{
for (int i = 0; i < bullet_max; i++)
{
if (bullet[i].live)
{
bullet[i].y -= speed;
if (bullet[i].y < -5)
{
bullet[i].live = false;
}
}
}
return;
}
这里需要注意到的地方有三点,第一是飞机的移动范围必须在窗口范围内,要不然飞机就会移动到窗口外面去。第二就是子弹的移动,也必须有个范围,当子弹移动到窗口的上方后,就判定子弹的LIVE为假,自然子弹也就消失了,然后是子弹的发射速度,所以用了个形参,可以控制子弹的发射速度。
GetAsyncKeyState()是一个当检测到键盘有输入时,便返回TRUE,然后便执行if后面的语句,使得玩家能够用键盘控制飞机的移动,这里我采用的是上下左右这四个键,当然也可以设定WASD这四个键来控制飞机的移动。
4.3.2敌机的·产生
敌机的产生跟玩家飞机和子弹的产生是一样的道理,先考虑创建敌机,包括敌机的类型,血量,然后是敌机的移动。
void createnemy()//创建敌机
{
for (int i = 0; i < enemy_num; i++)
{
if (!enemy[i].live)
{
enemy[i].live = true;
enemy[i].x = rand() % (width - 60);//随机函数,x的坐标为0到窗口宽度减去60.
enemy[i].y = 0;
enemy_hv(i);
break;
}
}
}
void enemymove(int speed)//敌机ID移动
{
for (int i = 0; i < enemy_num; i++)
{
if (enemy[i].live)
{
enemy[i].y += speed;
if (enemy[i].y > hight)
{
enemy[i].live =false;
}
}
}
}
void enemy_hv(int i)//敌机的血量
{
int flag = rand() % 10;
if (flag>=0&&flag<=2)//设定大飞机的产生范围概率为3/10
{
enemy[i].type = BIG;
enemy[i].hv = 3;
enemy[i].width_en = 69;
enemy[i].height_en = 99;
}
else
{
enemy[i].type = SMALL;
enemy[i].hv = 1;
enemy[i].width_en = 57;
enemy[i].height_en = 43;
}
}
在创建敌机的过程中用到了随机函数。用于随机产生敌机
4.3.3子弹击中飞机
void hit()
{
for (int i = 0; i < enemy_num; i++)
{
if (!enemy[i].live)
continue;
for (int k = 0; k < bullet_max; k++)
{
if (!bullet[k].live)
continue;
if (bullet[k].x > enemy[i].x && bullet[k].x<(enemy[i].x + enemy[i].width_en)
&& bullet[k].y>enemy[i].y && bullet[k].y < (enemy[i].y + enemy[i].height_en)) //子弹在敌机图片的坐标范围内,判定为击中
{
bullet[k].live = false;
enemy[i].hv--;
}
}
if (enemy[i].hv <= 0)
{
enemy[i].live = false;
}
}
}
4.3.4主函数的编写
bool Timer(int ms, int id)//定义了一个定时器
{
static DWORD t[10];
if (clock() - t[id] > ms)
{
t[id] = clock();
return true;
}
return false;
}
int main()
{
initgraph(width, hight,SHOWCONSOLE);
//setbkcolor(BLACK);
//cleardevice();
gameinit();
BeginBatchDraw();//双缓冲加载
while (1)
{
windraw();
FlushBatchDraw();
playermove(2);
bulletmove(4);
if (Timer(300,1))
{
createnemy();
}
if (Timer(20, 3))
{
enemymove(1);
}
hit();
//printf("hei\n");
}
EndBatchDraw();
return 0;
}
4.5结束
好了,以上便是全部内容,整个程序还是比较简单,使用了函数、枚举类型、结构体等只是。程序的编写不难,可能比较麻烦的一点就是掩码图的创造。希望这篇文章对读者有所帮助。
链接:https://pan.baidu.com/s/1ptCN_Q5y2QT4xIjWWxUlMg
提取码:p6xa
--来自百度网盘超级会员V5的分享