基于C语言和EasyX图形库的简单飞行射击小游戏

本文介绍了一款使用C语言和EasyX图形库编写的飞行射击小游戏,详细阐述了游戏逻辑,包括主菜单交互、角色移动、敌机与子弹碰撞检测等,并分享了在开发过程中遇到的挑战,如闪烁问题、暂停功能实现、透明贴图及多线程音效播放等。代码分为三个文件,适合初学者学习参考。
摘要由CSDN通过智能技术生成

基于C语言和EasyX图形库的简单飞行射击小游戏

基本游戏逻辑介绍

  本次编写的游戏是一个空战小游戏,实现了一些简单的游戏功能。如主菜单和子菜单游戏界面均通过鼠标点击进行操作,通过按键来控制游戏角色的移动以及技能的释放。
  下面简单的介绍一下游戏的大概流程和思路。 首先,对于游戏中的个体,存在着很多的信息,所以我们定义不同的结构体来存储这些个体的各种信息,如是否存活、子弹等级、敌机的种类、PH值等。关于easyx图形库就不做过多的介绍,大家可以自行去了解
  游戏先进入主菜单,通过循环来实现获取鼠标信息,在通过判定鼠标是否点击以及点击的位置,达到选择不同功能的效果。选择开始游戏后跳出主菜单循环,进入游戏循环,通过定义不同的功能函数来实现角色的移动、图片的刷新、以及一部分简单的动态效果。通过GetKeyState()函数来判断用户是否按下移动键。进行移动,改变图片坐标位置。再产生敌机和敌机子弹,敌机和子弹的移动(通过改变坐标实现移动),判定是否被子弹击中或者与敌机碰撞。最后调用画地图的函数,将所有的图片画出。游戏死亡之后、暂停、退出等的功能与主菜单同理,通过鼠标控制(使用goto语句实现返回主菜单、重新开始游戏)。游戏还有能保存游戏分数的最好历史记录,涉及一些简单的文件操作。

在编写过程中遇见的一些坑
  • 在图片的打印中会有闪烁的问题,这里使用双缓冲解决BeginBatchDraw();FlushBatchDraw();
  • 在实现暂停功能时,会有暂停的图片呈现,建议将图片设置成窗口大小,当你暂停时的图片比地图更小时,会有鼠标操作不灵敏的问题(若能解决建议还是比地图小一些),即需要点击多次才可能触发你的判断。
  • 敌机和子弹需要使用透明贴图,所以需要掩码图和原图来实现透明贴图。如
putimage(minePlan.x, minePlan.y, &minebit[0], NOTSRCERASE);
putimage(minePlan.x, minePlan.y, &mine[0], SRCINVERT);

参考链接:如何制作掩码图

  • 多种音效同时播放需要使用简单的多线程,否则会导致游戏卡顿。如背景音效和爆炸音效同时播放。请读者自行查阅资料。
  • 动态爆炸的实现,当使用多线程实现时,有爆炸闪烁的效果(这个问题还未解决),可以在打印图片的函数中实现。原理:每次图刷新都会打印爆炸的图片(地图刷新的速度很快),通过控制打印不同的图片实现爆炸效果(需要控制图片更新的时间),类似于通过快速翻动画在纸上的图片达到动态的效果,具体操作,参考代码。

头文件代码

头文件定义代码,函数的声明较少是因为在主文件中用到的函数较少,大多数函数的调用在AirGame.cpp中

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>//文件操作的头文件sprintf()函数
#include<stdlib.h>
#include<conio.h>#include<windows.h> //GetKeyState()函数需要
#include<mmsystem.h>//playSound()函数需要.包含多媒体设备接口库
#include<math.h>//rand()随机函数需要#pragma 
comment(lib,"Winmm.lib")//导入Winmm.lib库,让歌曲可以播放
#include<graphics.h>//easyx图形库的头文件
#include<process.h>#define NUM 20 //敌机数组数量
#define WIDTH 600	//地图宽600  
#define HEIGHT 700	//高700		
#define N  15//子弹数量
#define BOSSBUL 100//boss子弹量
#undef UNICODE //解决loadimage()的加载出问题的问题  
//定义我方飞机和敌方飞机坐标血量,子弹等...
typedef struct role//我方飞机
{
   	
int x;	//确定在图中的坐标	
int y;	
int type;	//我方飞机子弹等级	
int kind;	//我方飞机种类	
int live;	//飞机是否存活	
int score; //存储分数
}Role;
typedef struct enemy  //敌机
{
   	
int x;	//确定在图中的坐标	
int y;	
int type;	//敌机飞机种类.	
int live;	//飞机是否存活	
int HP;		//血量
char shot;	//表示飞机是否发射过子弹
}Enemy; 
typedef struct _bullte //子弹
{
   	int x;	//确定在图中的坐标	
int y;	
int live;	//子弹是否存活	
char shot;	//导弹是否发射
}Bullte; 
typedef struct ico	//图标
{
   	
int x;	//确定在图中的坐标	
int y;	
int live;	//飞机、子弹是否存活	
}Ico;
char PlayKeyboard(char c);
void texiao();
void image(IMAGE *map1, Role *minePlan);//图片加载//飞机移动
int keyDown(IMAGE* map1, int speed);//画出飞机移动后的地图
void drowGame();
void planMove(char c,IMAGE *mine);
 char GameOver();//游戏结束后 
 void DataConserve();//保存数据并退出 
 void  OutScore(int number);//输出数字图片 
 int timeout();//暂停函数 
 int  Datadir();//记录查看

Main.cpp代码

#include"airHead.h"
//定义图片变量
IMAGE map1, mine[2],map[7],tubiao[10], shuzi[3], shuziyama[3];
Role minePlan;//定义我方飞机
DWORD t1, t2,t3,t4,t5,t6,t7,t8;//什么变量
IMAGE enemy2[5], enemy2yanma[5];
int img_x, img_y;
//IMAGE tu[2];
int Num[7], count = 0;
int MainMenu()
{
   
	MOUSEMSG m, m2, m3, m4;//定义鼠标变量
	while (1)//主菜单操作
	{
   
		putimage(0, 0, &map[0]);
		FlushBatchDraw();
		m = GetMouseMsg();
		if (m.uMsg == WM_LBUTTONDOWN)//当鼠标左键按下
		{
   
			if (m.x > 230 && m.x < 371 && m.y>46 && m.y < 83)//选择角色
			{
   
				putimage(0, 0, &map[5]);//角色选择界面
				FlushBatchDraw();
				while (1)
				{
   
					m3 = GetMouseMsg();
					if (m3.uMsg == WM_LBUTTONDOWN)//当鼠标左键按下
					{
   
						if (m3.x < 147 && m3.x>57 && m3.y > 210 && m3.y < 291)//1号机
						{
   
							minePlan.kind = 1;
							break;
						}
						if (m3.x < 537 && m3.x>449 && m3.y > 210 && m3.y < 291)//2号机
						{
   
							minePlan.kind = 2;
							break;
						}
					}
				}
			}
			if (m.x > 230 && m.x < 371 && m.y>154 && m.y < 193)//开始游戏
			{
   
				break;
			}
			if (m.x > 230 && m.x < 371 && m.y>270 && m.y < 306)//退出游戏
			{
   
				DataConserve();
				return 0;
			}
			if (m.x > 230 && m.x < 371 && m.y>381 && m.y < 421)//游戏记录
			{
   
				putimage(0, 0, &map[6]);//显示历史最高纪录
				putimage(0, 0, &tubiao[0], NOTSRCERASE);
				putimage(0, 0, &tubiao[1], SRCINVERT);
				int score = Datadir();
				//输出位置
				img_x = 196;
				img_y = 275;
				count = 0;//数组下标归零
				if (score == -1)
					OutScore(0);
				else
					OutScore(score);
				FlushBatchDraw();
				while (1)
				{
   
					m4 = GetMouseMsg();
					if (m4.uMsg == WM_LBUTTONDOWN)//当鼠标左键按下
					{
   
						if (m4.x < 50 && m4.x>0 && m4.y > 0 && m4.y < 50)
						{
   
							break;
						}
					}
				}
			}
			if (m.x > 230 && m.x < 371 && m.y>484 && m.y < 3524)//游戏帮助
			{
   
				putimage(0, 0, &map[1]);//显示游戏操作说明
				putimage(0, 0, &tubiao[0], NOTSRCERASE);
				putimage(0, 0, &tubiao[1], SRCINVERT);
				FlushBatchDraw();
				while (1)
				{
   
					m2 = GetMouseMsg();
					if (m2.uMsg == WM_LBUTTONDOWN)//当鼠标左键按下
					{
   
						if (m2.x < 50 && m2.x>0 && m2.y > 0 && m2.y < 50)
						{
   
							break;
						}
					}
				}
			}
		}

	}
}
int main()
{
   
	minePlan.kind = 1;//默认为1号机
	D://返回主菜单
	t1 = t2 = t3 = t4 = t5 = t6 = t7 = t8 = GetTickCount();//获取系统开机到当前所经过的毫秒数
	//图形化窗口
	initgraph(WIDTH, HEIGHT);
	//地图
	image(&map1, &minePlan);
	mciSendString("open startMusic.mp3 alias start", NULL, 0, NULL);//alias 起别为 mymusic;
	mciSendString("play start repeat", NULL, 0, NULL);
	putimage(0, 0, &map[0]);
	FlushBatchDraw();
	//主菜单
	if (MainMenu() == 0)//在主菜单中选择退出游戏时,结束游戏
		return 0;
	R://游戏重新开始
	image(&map1, &minePlan);
	mciSendString("close start ", NULL, 0, NULL);
	mciSendString("close time ", NULL, 0, NULL);
	mciSendString("open 倒计时.wav alias time", NULL, 0, NULL);//倒计时音乐
	mciSendString("play time ", NULL, 0, NULL);
	int jishu=3;
	while (jishu>0)//游戏开始倒计时
	{
   
		putimage(0, 0, &map1);
		putimage(300, 350, 17, 22, &shuziyama[0], jishu * 17, 0, NOTSRCERASE);
		putimage(300, 350, 17, 22, &shuzi[0], jishu * 17, 0, SRCINVERT);
		FlushBatchDraw();
		Sleep(1000);
		jishu--;
	}
	putimage(280, 350, &tubiao[5], NOTSRCERASE);
	putimage(280, 350,&tubiao[4], SRCINVERT);
	FlushBatchDraw();
	Sleep(1000);
	mciSendString("close start ", NULL, 0, NULL);
	mciSendString("open ll.mp3 alias mymusic", NULL, 0, NULL);//alias 起别为 mymusic;
	mciSendString("play mymusic repeat", NULL, 0, NULL);
	while (1)
	{
   
		BeginBatchDraw();//开启双缓冲绘图,在内存中画好,再显示出来
		int b = keyDown(&map1, 10);
		drowGame();	//更新当前的地图,飞机,子弹,重复执行子弹移动的函数,实现子弹的移动
		if (b == 1)//退出游戏
		{
   
			mciSendString("stop mymusic", NULL, 0, NULL);//关掉背景音乐
			PlaySound(NULL, NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);//关掉子弹音效
			break;
		}
		else if (b == 2)//暂停
		{
   
			if (timeout() == 1)//返回1,代表返回主菜单
			{
   
				mciSendString("stop mymusic", NULL, 0, NULL);//关掉背景音乐
				goto D;
			}
		}
		else if (b == 3)//我方死亡,boss死亡
		{
   
			mciSendString("stop mymusic", NULL, 0, NULL);//关掉背景音乐
			PlaySound(NULL, NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);//关掉子弹音效
			break;
		}
		FlushBatchDraw();
		Sleep(20);//延时函数
	}
	//判断游戏结束后的选择
	DataConserve();//每次退出游戏界面后保存数据
	char how;
	how = GameOver();//游戏结束结束界面的选择
	if (how == 'D')
		goto D;
	else if (how == 'R')
		goto R;
	return 0;
}

AirGame.cpp

#define _CRT_SECURE_NO_WARNINGS
#include"airHead.h"
#pragma comment(lib,"Winmm.lib")//导入Winmm.lib库,让歌曲可以播放

//图片加载函数
extern int s = 0;//boss选择
extern IMAGE map1, mine[2], map[7] ,tubiao[10], shuzi[3], shuziyama[3];//声明外部变量
extern Role minePlan ;				//定义我方飞机
Ico ico[5], danger;					//danger危险图标,ico图标
Enemy enemyPlan[NUM], boss[2];		//定义敌方飞机
Bullte  bullet[N], enemyBult[N], BossBul[BOSSBUL], developBul[15], shell ;	//定义我方和地方子弹、炮弹
extern DWORD t1, t2,t3,t4, t5, t6,t7,t8;
IMAGE enemybul,blood[2],jineng[15],jinengyanma[15],img_developBul[15], boom2[2], boom2yanma[2],cue[2],bomb[2],hudun,relive, war[15], waryanma[15], enemy[10]; //relive提示复活的图片
IMAGE  boom[11], boomyanma[11], zidan, minebit[2];//爆炸、子弹、飞机掩码
extern IMAGE enemy2[5], enemy2yanma[5];//旋转的敌机
int m = 160, BloodWidth = -10, time[7] , show = 1;//dazao = 0,
int x = 0,jj=0,enemy_x=-1,enemy_y=-1,speedx=2,speedy=2;//enemy_x确定敌机爆炸的位置,speedx确定升级图标的移动速度
int score = 10;//击杀一定数量的敌机,boss出现,
extern int Num[7],count, img_x, img_y;
int fen = score / 2,num_death_enemy;
int ene_2 = 0,my_2;//my_2飞机图片选择,ene_2旋转敌机图片选择
int px[2] ,condition=0;//condition判断无敌状态,px用于地图移动
void image(IMAGE* map1, Role* minePlan)
{
   
	loadimage(&map[0], "feng.jpg", WIDTH, HEIGHT);//封面图片
	loadimage(&mine[0], "mp.JPG", 50, 50);//自身飞机
	loadimage(&minebit[0], "mpbit.jpg", 50, 50);
	loadimage(&minebit[1], "minePlan22.png");//二号飞机
	loadimage(&mine[1], "minePlan2.png");
	loadimage(map1, "map1.jpg", 600, 700);//地图1
	loadimage(&map[1], "map22.jpg", 600, 700);
	loadimage(&map[2], "gameEnd.jpg", 600, 700);
	loadimage(&map[3], "map3.jpg", 600, 1140);
	loadimage(&map[4], "map3.jpg", 600, 1140);
	loadimage(&map[5], "型号.png");
	loadimage(&map[6], "jilu.jpg");
	loadimage(&zidan, "zidan1.png",40,50);//子弹
	loadimage(&enemybul, "enemyBul.png", 20, 50);//敌军子弹
	loadimage(&enemy
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值