运用C语言exayx图形库开发经典游戏《Flappy Bird》

        所谓游戏图像,就是不断绘制更新绘制更新的一个连续过程,所以我们的主函数为一个循环体。然后游戏代码分为 定义、初始化、更新数据、游戏结束判定几个过程。

        该游戏运用exayx图形库运行,请读者自行在官网进行环境配置(此游戏仅作为读者参考,作者并非专业开发者,若有不当请给与批评指正)。

        我们应该先找到游戏中使用的游戏素材,将找好的音乐、小鸟图片、背景等素材导入代码的文件夹中,以备调用,如图所示(图片对应的黑底白色图片是对应图片的掩码图,掩码图的使用目的是为了实现图像的透明,具体参见其他文章对掩码图的解释,百度就有,作者使用ps自行制作):

 

 然后我们使用结构体定义游戏各个组件的数据,定义窗口大小,声明调用函数。

        我们知道小鸟x坐标是不变的,其实只是图片中的地面(round.png)在不断向右边移动,所以小鸟的参数为坐标x,y、而且我们用一个g参数模拟重力,使得小鸟在不受控制的情况下不断下落,用结构体定义小鸟,而且你会发现,定义中还有一个IMAGE img2[3][2]数组,这个数组的作用是每个小鸟彩图对应一个其掩码图,如果不加上掩码图你会发现图像在运行的过程中给有黑框,会比较影响观感。

        其他物体的参数也是如此,具体读者请参考代码,里面我标注的非常详细。

        定义之后我们要先初始化,初始化的作用是把各个游戏部件的坐标、速度、游戏音乐播放的初始定义在一起,不管定义什么我们都要注意,一定要先行进行初始化!

        初始化之后我们就可以开始写绘制函数,绘制函数的作用就是控制动画,让地图和小鸟动起来的前提是先要绘制图像,绘制函数的用法请参考代码。注意:绘制的图像图层与代码的先后有一定关系,读者可以自行根据需要进行调节。

        之后我们就需要完成数据更新这一部分,我们知道,数据更新分为两部分,自动更新(动画自行演示,路面自己走动,小鸟煽动翅膀等等),另一部分为手动更新(鼠标操作之后小鸟往上飞),运用函数实现鼠标与计算机的交互,具体请参考代码。

        最后我们需要进行游戏结束的判断,运用if语句,当小鸟的坐标与管道、地面、边界重叠时,让小鸟的垂直速度瞬间变大,这样就模拟了小鸟死亡掉落画面,同时停止播放音乐,播放死亡音乐。

        文章只给读者提供一个大概思想,具体代码如下,请读者自行进行理解,注释标注得比较多,方便读者阅读。请多多支持作者,谢谢大家,有问题请给我留言,我会第一时间答复!

#include<stdio.h>
#include<graphics.h>
#include<easyx.h>
#include<time.h>
#include<mmsystem.h>
#include<windows.h>


#pragma comment(lib,"winmm.lib")//音乐

void GameInit();
void GameDraw();
void GameUpdata();
void hitFloor();

#define WIDTH 658
#define HIGHT 369 //窗口

unsigned long t1, t2; //定义时间

IMAGE backimg;	  //定义背景数据

struct round {		//定义地面道路
	int x, y;
	int xv;
	IMAGE img;
}round;

struct bird {		//定义鸟
	int x, y;
	int vy;
	int g;
	int frame;		//帧
	IMAGE img2[3][2];
}bird;

struct pipe {		//管道
	int x1, y1;
	int x2, y2;
	int x3, y3;
	int x4, y4;
	int x5, y5;
	int vx;
	IMAGE img3[4];
}pipe;

int main()
{
	GameInit(); //初始化
	for (;;)
	{
		GameDraw(); //绘制界面
		GameUpdata(); //数据更新
		hitFloor(); //判断游戏结束函数
	}
	return 0;
}


//初始化函数
void GameInit()
{
	//初始化bgm
	mciSendString("open images/bg.mp3", 0, 0, 0);
	mciSendString("play images/bg.mp3 repeat", 0, 0, 0);
	srand(time(0));
	//初始化窗口
	initgraph(WIDTH, HIGHT);
	//初始化图片
	loadimage(&backimg, "images/background.png");
	//初始化地面
	loadimage(&round.img, "images/round.png");
	round.x = 0;
	round.y = 320;
	round.xv = -1;
	//初始化时间
	t1 = GetTickCount();
	t2 = GetTickCount();
	//初始化小鸟
	loadimage(&bird.img2[0][0], "images/bird00.png");
	loadimage(&bird.img2[0][1], "images/bird01.png");
	loadimage(&bird.img2[1][0], "images/bird10.png");
	loadimage(&bird.img2[1][1], "images/bird11.png");
	loadimage(&bird.img2[2][0], "images/bird20.png");
	loadimage(&bird.img2[2][1], "images/bird21.png");
	bird.x = 20;
	bird.y = 100;
	bird.vy = 0;
	bird.g = 1;
	int frame = 0;		//帧数

	//初始化管道
	loadimage(&pipe.img3[0], "images/pipe00.png");
	loadimage(&pipe.img3[1], "images/pipe01.png");
	loadimage(&pipe.img3[2], "images/pipe10.png");
	loadimage(&pipe.img3[3], "images/pipe11.png");
	pipe.x1 = WIDTH;
	pipe.y1 = rand() % 180;			//第一根管道
	pipe.x2 = WIDTH + 200;
	pipe.y2 = rand() % 180;		//第二根管道
	pipe.x3 = WIDTH + 400;
	pipe.y3 = rand() % 180;			//第三根管道...
	pipe.x4 = WIDTH + 600;
	pipe.y4 = rand() % 180;
	pipe.x5 = WIDTH + 800;
	pipe.y5 = rand() % 180;
	pipe.vx = -4;			//管道移动速度

}


//界面绘制
void GameDraw()
{
	BeginBatchDraw();
	//绘制背景
	putimage(0, 0, &backimg);
	//绘制路面
	putimage(round.x, round.y, &round.img);
	//绘制管道
	putimage(pipe.x1, pipe.y1 + 260, &pipe.img3[0], SRCAND);
	putimage(pipe.x1, pipe.y1 + 260, &pipe.img3[1], SRCPAINT);
	putimage(pipe.x1, pipe.y1 - 130, &pipe.img3[2], SRCAND);
	putimage(pipe.x1, pipe.y1 - 130, &pipe.img3[3], SRCPAINT);

	putimage(pipe.x2, pipe.y2 + 260, &pipe.img3[0], SRCAND);
	putimage(pipe.x2, pipe.y2 + 260, &pipe.img3[1], SRCPAINT);
	putimage(pipe.x2, pipe.y2 - 130, &pipe.img3[2], SRCAND);
	putimage(pipe.x2, pipe.y2 - 130, &pipe.img3[3], SRCPAINT);

	putimage(pipe.x3, pipe.y3 + 260, &pipe.img3[0], SRCAND);
	putimage(pipe.x3, pipe.y3 + 260, &pipe.img3[1], SRCPAINT);
	putimage(pipe.x3, pipe.y3 - 130, &pipe.img3[2], SRCAND);
	putimage(pipe.x3, pipe.y3 - 130, &pipe.img3[3], SRCPAINT);

	putimage(pipe.x4, pipe.y4 + 260, &pipe.img3[0], SRCAND);
	putimage(pipe.x4, pipe.y4 + 260, &pipe.img3[1], SRCPAINT);
	putimage(pipe.x4, pipe.y4 - 130, &pipe.img3[2], SRCAND);
	putimage(pipe.x4, pipe.y4 - 130, &pipe.img3[3], SRCPAINT);

	putimage(pipe.x5, pipe.y5 + 260, &pipe.img3[0], SRCAND);
	putimage(pipe.x5, pipe.y5 + 260, &pipe.img3[1], SRCPAINT);
	putimage(pipe.x5, pipe.y5 - 130, &pipe.img3[2], SRCAND);
	putimage(pipe.x5, pipe.y5 - 130, &pipe.img3[3], SRCPAINT);

	//绘制小鸟
	putimage(bird.x, bird.y, &bird.img2[bird.frame][0], SRCPAINT);
	putimage(bird.x, bird.y, &bird.img2[bird.frame][1], SRCAND);

	EndBatchDraw();


}

//数据更新
void GameUpdata()
{
	//手动更新(玩家操作)

	MOUSEMSG msg = { 0 };
	if (MouseHit())	//判断是否有鼠标消息
	{
		msg = GetMouseMsg(); //获取鼠标消息
		if (msg.uMsg == WM_LBUTTONDOWN) //鼠标左键
		{
			bird.vy = -10;
		}
		else if (msg.uMsg == WM_RBUTTONDOWN)//鼠标右键
		{
			bird.vy = -10;
		}
	}

	//自动更新
	t2 = GetTickCount();//获取本地时间
	if (t2 - t1 > 30)	//地面移动,以及定义时间频度
	{
		round.x += round.xv;
		if (round.x < -40)
		{
			round.x = 0;	//刷新地面图像
		}
		if (++bird.frame >= 3)
		{
			bird.frame = 0;	//更新小鸟(拍动翅膀)
		}

		bird.vy += bird.g;	//模拟重力
		bird.y += bird.vy;	//小鸟下落

		pipe.x1 += pipe.vx;
		pipe.x2 += pipe.vx;
		pipe.x3 += pipe.vx;
		pipe.x4 += pipe.vx;
		pipe.x5 += pipe.vx;
		if (pipe.x1 < -96)
		{
			pipe.x1 = pipe.x2 + 659;
		}

		if (pipe.x2 < -96)
		{
			pipe.x2 = pipe.x3 + 659;
		}

		if (pipe.x3 < -96)
		{
			pipe.x3 = pipe.x4 + 659;
		}

		if (pipe.x4 < -96)
		{
			pipe.x4 = pipe.x5 + 659;
		}

		if (pipe.x5 < -96)
		{
			pipe.x5 = 760;
		}

		t1 = t2;

	}

}

//游戏结束判定
void hitFloor()
{
	if (bird.y <= 0 || bird.y >= 270)//碰到天花板和地板判定游戏结束
	{
		bird.vy = 110;
		mciSendString("stop images/bg.mp3", 0, 0, 0);
		mciSendString("close images/bg.mp3", 0, 0, 0);
		mciSendString("open images/remake.mp3", 0, 0, 0);
		mciSendString("play images/remake.mp3", 0, 0, 0);

	}

	if (bird.x >= pipe.x1 && bird.x <= pipe.x1 + 96 && bird.y <= pipe.y1 + 20)
	{
		if (bird.x >= pipe.x1 && bird.x <= pipe.x1 + 96 && bird.y >= pipe.y1 - 100)
		{
			bird.vy = 110;
			mciSendString("stop images/bg.mp3", 0, 0, 0);
			mciSendString("close images/bg.mp3", 0, 0, 0);
			mciSendString("open images/remake.mp3", 0, 0, 0);
			mciSendString("play images/remake.mp3", 0, 0, 0);

		}

	}

	if (bird.x >= pipe.x2 && bird.x <= pipe.x2 + 96 && bird.y <= pipe.y2 + 20)
	{
		if (bird.x >= pipe.x2 && bird.x <= pipe.x2 + 96 && bird.y >= pipe.y2 - 100)
		{
			bird.vy = 110;
			mciSendString("stop images/bg.mp3", 0, 0, 0);
			mciSendString("close images/bg.mp3", 0, 0, 0);
			mciSendString("open images/remake.mp3", 0, 0, 0);
			mciSendString("play images/remake.mp3", 0, 0, 0);

		}

	}

	if (bird.x >= pipe.x3 && bird.x <= pipe.x3 + 96 && bird.y <= pipe.y3 + 20)
	{
		if (bird.x >= pipe.x3 && bird.x <= pipe.x3 + 96 && bird.y >= pipe.y3 - 100)
		{
			bird.vy = 110;
			mciSendString("stop images/bg.mp3", 0, 0, 0);
			mciSendString("close images/bg.mp3", 0, 0, 0);
			mciSendString("open images/remake.mp3", 0, 0, 0);
			mciSendString("play images/remake.mp3", 0, 0, 0);

		}

	}


	if (bird.x >= pipe.x4 && bird.x <= pipe.x4 + 96 && bird.y <= pipe.y4 + 20)
	{
		if (bird.x >= pipe.x4 && bird.x <= pipe.x4 + 96 && bird.y >= pipe.y4 - 100)
		{
			bird.vy = 110;
			mciSendString("stop images/bg.mp3", 0, 0, 0);
			mciSendString("close images/bg.mp3", 0, 0, 0);
			mciSendString("open images/remake.mp3", 0, 0, 0);
			mciSendString("play images/remake.mp3", 0, 0, 0);

		}

	}

	if (bird.x >= pipe.x5 && bird.x <= pipe.x5 + 96 && bird.y <= pipe.y5 + 20)
	{
		if (bird.x >= pipe.x5 && bird.x <= pipe.x5 + 96 && bird.y >= pipe.y5 - 100)
		{
			bird.vy = 110;
			mciSendString("stop images/bg.mp3", 0, 0, 0);
			mciSendString("close images/bg.mp3", 0, 0, 0);
			mciSendString("open images/remake.mp3", 0, 0, 0);
			mciSendString("play images/remake.mp3", 0, 0, 0);

		}

	}

}

游戏运行画面:

Supongo que te gusta

Origin blog.csdn.net/xiao_hu__/article/details/121679750
Recomendado
Clasificación