飞机大战小游戏(C语言)

最近用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的分享

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值