C语言基于EasyX图形界面的飞机大战项目

C语言基于EasyX图形界面的飞机大战项目

这是很早之前用C语言写的基于EasyX图形界面的飞机大战项目,当时只是觉得这个项目挺有意思的。但是一直都没能去整理这个项目的代码,今天趁着空闲时间,稍微整理一下这个代码的代码:
/*
	文件名称:planegame
	项目描述:实现飞机大战游戏的效果
*/


#include <stdio.h>
#include <conio.h>			//	检测键盘操作有关的头文件
#include <graphics.h>		//	EasyX第三方图形界面库
#include <stdlib.h>			//	C语言自己的标准库,动态内存分配的头文件。
#include <time.h>			//	种下随机种子的相关头文件
#include <mmsystem.h>		//	添加音乐库
#pragma comment(lib,"winmm.lib")
//#pragma warning(disable:4996);

//宏定义区域
#define WIN_WIDTH	600		//	飞机大战界面的宽度为600
#define	WIN_HEIGHT	800	   //	飞机大战界面的高度为800


//定义结构体类型,注意类型不需要分配空间的。
typedef struct Node			//	结构体类型适用于自己的飞机、敌机和子弹的
{
	int x;					//	飞机(自己和敌人)和子弹的坐标
	int y;

	struct Node* pNext;
}NODE;						//	NODE是结构体类型,是一种特殊的数据类型


typedef NODE* Point_Node;	//	给指针NODE*起一个别名Point_Node


/*
	1、自己的飞机只有一个
	2、敌机有很多个(每个敌机都需要一块内存,很多敌机就需要一个链表来存储啦)
	3、子弹也有很多个(和敌机一样,很多子弹也需要一个链表来存储)
*/
Point_Node pBullet = NULL;		//	定义子弹链表的头指针	-->子弹有很多个,比较复杂!
Point_Node pEnemyPlane = NULL;	//	定义敌机链表的头指针	-->敌机有很多,比较复杂!
Point_Node pMinePlane = NULL;	//	定义自己飞机链表的头指针-->自己的飞机只有一个,简单!


//链表的操作(增删查改)+项目 -->理论结合实际项目

/*
	1、敌机有很多个(敌机的大小为宽50 高30),敌机初始坐标:x随机,y = 0		-->敌机从上往下移动
	2、自己的飞机只有一个(自己飞机宽80 高60),初始坐标是x=260,y=740	-->由用户操作移动	
	3、子弹很多个,先考虑简单的情况,只是由自己的飞机发射出来														-->子弹从下到上移动
*/
//创建链表
void CreateList()	//	从内存中拉一个个内存出来
{
	//	创建敌机的链表
	pEnemyPlane = (Point_Node)malloc(sizeof(NODE));		//	在内存中动态开辟能够存储结构体的空间(以字节为单位)
														//	malloc函数返回值是void*地址,需要强制转换								
	pEnemyPlane->pNext = NULL;							//	防止野指针


	//	创建自己飞机的链表,并初始化自己飞机的xy坐标
	pMinePlane = (Point_Node)malloc(sizeof(NODE));
	//	自己飞机宽80,高60,初始坐标为x=260,y=740
	pMinePlane->x = 260;
	pMinePlane->y = 740;
	pMinePlane->pNext = NULL;
	

	//	创建存储子弹的链表,开始子弹的坐标不用管。
	pBullet = (Point_Node)malloc(sizeof(NODE));
	pBullet->pNext = NULL;

}


//	增加一个节点
void AddNode(int flag)	//	flag==0表示增肌子弹 flag==1表示增加敌机
{						
	//	增加一个敌机,具体可以画图来理解
	/*Point_Node pNew = (Point_Node)malloc(sizeof(NODE));
	pNew->pNext = NULL;
	pEnemyPlane->pNext = pNew;*/

	//不带头节点的头插法(很经典的数据结构算法)
	Point_Node pNew;
	//开辟内存空间
	pNew = (Point_Node)malloc(sizeof(NODE));	//	从内存来看是增加一个节点,实际项目中是增加一个敌机。

	if (flag == 1)			//	增加的是敌机
	{
		/*填充数据*/
		pNew->x = rand() % (WIN_WIDTH - 50);	//	减去敌机的宽度,防止越出窗口边界
		pNew->y = 0;
		/*连接链表,这里就是头插法的关键啦!*/
		pNew->pNext = pEnemyPlane->pNext;
		pEnemyPlane->pNext = pNew;
	}
	else if (flag == 0)		//	子弹
	{
		/*填充数据*/
		//设置子弹的宽度为10,高度为15.
		pNew->x = pMinePlane->x + 35;
		pNew->y = pMinePlane->y - 20;		//	子弹在自己飞机正前方5出现
		/*连接链表*/
		pNew->pNext = pBullet->pNext;
		pBullet->pNext = pNew;
	}

}


//	键盘按键检测函数
void IsPressKey()
{
	if (kbhit())			//	如果有按键按下
	{
		char key;
		key = getch();		//	获取键盘信息(按下的是哪个键)

		switch (key)
		{
		case 72:			//	上方向键
			pMinePlane->y -= 8;
//			printf("上方向键的ASCII是%d", key);
//			Sleep(1500);
			break;
		case 80:			//	下方向键
			pMinePlane->y += 8;
			break;
		case 75:			//	左方向键
			pMinePlane->x -= 6;
			break;
		case 77:			//	右方向键
			pMinePlane->x += 6;
			break;
		default:
			break;
		}
	}
}


//	碰撞检测,子弹发射出去干掉敌机
void Shoot()
{
	//	定义敌机的两个临时指针,为了删除被子弹打中的敌机。(实际上就是释放相应的内存)
	Point_Node pDj = pEnemyPlane->pNext;	//	pDj开始指向敌机链表中的第二个敌机
	Point_Node pDjPre = pEnemyPlane;		//	pDjPre开始指向第一敌机

	//	定义子弹的两个临时指针
	Point_Node pZd = pBullet->pNext;		//	同上面的敌机
	Point_Node pZdPre = pBullet;

	//	遍历所有的敌机和所有子弹,看看两者是否有碰撞?
	while (pDj != NULL)						//	遍历所有的敌机
	{
		pZd = pBullet->pNext;				//	初始化子弹的两个临时指针
		pZdPre = pBullet;

		while (pZd != NULL)					//	遍历所有的子弹
		{
			//	判断敌机和子弹是否碰撞到(需要画出二维图形来理解)
			if (pZd->x >= (pDj->x-10) && pZd->x <= (pDj->x+50) &&
				pZd->y >= (pDj->y-15) && pZd->y <= (pDj->y+30)
				)
			{
				//	碰撞条件满足的话干掉敌机,同时子弹也消失了。
				//	干掉敌机(其实是干掉敌机链表中的第二个节点,第一个节点不动,碰撞后依次干掉后面的敌机)
				pDjPre->pNext = pDj->pNext;
				free(pDj);
				//	干掉子弹(注释同敌机)
				pZdPre->pNext = pZd->pNext;
				free(pZd);

				//mciSendString(TEXT("open ./boom2.mp3 alias music"), 0, 0, 0);
				//    mciSendString(L"play music repeat", 0, 0, 0);	//播放音乐


				//	重新寻找敌机
				pDj = pEnemyPlane->pNext;
				pDjPre = pEnemyPlane;
				break;	//	一个子弹干掉一个敌机以后,跳出循环,等待下一次的碰撞检测。
			}
			else		//	这颗子弹没击中敌机,就换别的子弹来碰撞敌机
			{
				//	寻找下一颗子弹,继续和敌机进行碰撞检测。
				pZd = pZd->pNext;
				pZdPre = pZdPre->pNext;
			}

		}

		//	子弹用尽或者一个敌机被干掉以后,下一个敌机继续来和子弹进行碰撞检测。
		pDj = pDj->pNext;
		pDjPre = pDjPre->pNext;
	}
}



int main()
{
	initgraph(WIN_WIDTH, WIN_HEIGHT,SHOWCONSOLE);	//	创建600*800的窗口同时显示控制台窗口
	//PlaySound(L"TheMass.mp3",NULL,SND_FILENAME|SND_LOOP|SND_ASYNC);
	//窗口加载瞬间就播放音乐



	//mciSendString(TEXT("open ./shoot.mp3 alias music2"), 0, 0, 0);
	//mciSendString(L"play music2", 0, 0, 0);


	IMAGE planeimg;			//	定义保存敌机图片的对象
	IMAGE Mineplaneimg;		//	定义保存自己飞机图片的对象
	Point_Node p;			//	定义指针变量p
	DWORD t1, t2;			//	控制敌机的速度
	DWORD tt1,tt2;			//	控制子弹的速度



	//加载敌机图片,敌机大小为宽50,高30。
	loadimage(&planeimg,L"./plane.jpg",50,30);

	//加载自己飞机图片,自己的飞机大小是宽80,高60。
	loadimage(&Mineplaneimg, L"./MinePlane.jpg", 80, 60);
	
	//种下随机数种子
	srand((unsigned int)time(NULL));//	种下产生随机数的种子
	
	//创建链表的第一个节点
	CreateList();
	
	//在内存中增加4个节点,就是在实际的项目中随机增加4个飞机
/*	for (int i = 0; i < 4; i++)
	{
		AddNode();
	}*/

	t1 = GetTickCount();			//	给敌机一个初始时间
	tt1 = GetTickCount();			//	给子弹一个初始时间

	while (1)
	{
		t2 = GetTickCount();		//	敌机间隔一段时间
		if (t2 - t1 >= 1000)		//	间隔1000ms增加一个敌机
		{
			AddNode(1);				//	1000ms产生一个敌机
			t1 = t2;				//	为了下次增加敌机做时间差的准备
		}


		tt2 = GetTickCount();		//	子弹间隔一段时间
		if (tt2 - tt1 >= 200)		//	间隔300ms增加一个子弹
		{
			AddNode(0);			    //	300ms产生一个子弹
		//	mciSendString(TEXT("open ./shoot.mp3 alias music"), 0, 0, 0);//mci(media control interface) Send(发送) String(字符串)
		//	mciSendString(L"play music repeat", 0, 0, 0);	//播放音乐
			tt1 = tt2;			   //	为下次增加子弹做时间差的准备
		}

		//Sleep(5);
		BeginBatchDraw();		  //这个函数用于开始绘图,执行后,任何绘图操作都将暂时不输出到屏幕上面
								 //直到执行到FlushBatchDraw 或 EndBatchDraw才将之前的绘图输出。
		cleardevice();			//这个函数用于清除屏幕内容。具体的,是用当前背景色清空屏幕,并将当前点移至 (0, 0)。



		//在x=260,y=740位置显示自己的飞机
		putimage(pMinePlane->x,pMinePlane->y,&Mineplaneimg);

		//显示自己的子弹
		p = pBullet->pNext;
		while (p != NULL)
		{
			//在控制台打印数据
			printf("x = %d y = %d\n", p->x, p->y);

			/*在easyx图形界面中使用控制台数据,画一个矩形。*/
			roundrect(p->x, p->y, p->x+10, p->y+15, 5, 5);
			
			p->y--;			//	子弹向上移动
			p = p->pNext;	//	继续访问子弹链表中下一个子弹
		}

		//显示敌机
		p = pEnemyPlane->pNext;
		while (p != NULL)
		{
			//在控制台打印数据
			printf("x = %d y = %d\n", p->x, p->y);

			/*在easyx图形界面中使用控制台数据,画一个矩形。*/
			//roundrect(p->x, p->y, p->x+30, p->y+20, 5, 5);

			/*在easyx图形界面中使用控制台的数据,输出飞机图片*/
			putimage(p->x, p->y, &planeimg);
			
			p->y++;				//	飞机向下移动
			//Sleep(100);		//	加上这句话后飞机只剩下一个了

			p = p->pNext;		//	继续访问飞机链表中的下一个飞机
		}

		Sleep(5);
		Shoot();				//	碰撞检测,子弹是否击中敌机

		EndBatchDraw();			//	显示之前所有的绘图操作

		IsPressKey();			//	键盘按键检测函数

	}

	getchar();					//	等待键盘输入字符,起到暂停窗口作用。

	return 0;
}
 实际运行起来的效果如下:

在这里插入图片描述

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值