300 行代码的《雷霆战机》游戏,找乐趣的同时学会单链表的使用!

低配版本的雷霆战机,敌机无法发射子弹

在这个游戏中我们能学到什么知识呢?

  1. 背景的循环原理
  2. 不一样的增加链表结点的方法
  3. 链表结点的删除

首先我们要准备一点素材,没有素材的小伙伴,可以私聊我 ~
在这里插入图片描述

效果如下所示:

雷霆游戏C语言实现


代码如下所示:

#include <graphics.h>	//图形界面库
#include <conio.h>		//标准输入输出头文件
#include <mmsystem.h>	//音乐头文件
#include <math.h>		//数学头文件
#include <stdio.h>		
#include <time.h>		//时间头文件
#pragma comment(lib,"winmm") //音乐库文件

#define WIDTH 480	//宏定义 控制界面大小 
#define HEIGHT 640

IMAGE img_background, img_myplane, img_bullet, img_enemyplane;	//定义图像对象
char ch;							//接收键盘上按下的字符
int bj_y;							//用于移动背景图片
DWORD t1, tt1;						//移动背景
DWORD t2, tt2, t3, tt3;	//生成子弹和敌机
DWORD t4, tt4;						//打印子弹和敌机

//结点		敌机 子弹
typedef struct Node
{
	int x;    //坐标
	int y;
	struct Node* pNext;
}NODE;

NODE mine;						//我的飞机
NODE* pBullet = NULL;			//我的子弹
NODE* pEnemy = NULL;			//敌机

void Welcome();					//欢迎界面
void InitGame();				//初始化游戏
void RunGame();					//开始游戏
void MovePlane();				//移动飞机
void CreateList();				//创建链表
void CreateBulletEnemy(int);	//创建子弹和敌机	增加结点
void JudgeGame();				//判断游戏 


int main()
{
	Welcome();
	InitGame();

	BeginBatchDraw();	//开始批量绘图
	while (1)
	{
		RunGame();

		FlushBatchDraw();	//结束批量绘图
	}

	return 0;
}

void Welcome()
{
	SetConsoleTitle("Thunder__Fighter(低级版)    作者:花里胡哨D 梦");     //设置窗口名称
	printf("欢迎来到飞机大战游戏界面!\n\n");
	printf("控制:↑ ↓ → ←\n\n");
	printf("游戏低级的原因:游戏未设置敌方子弹	→.→\n\n");
	printf("\t\t注意不要碰到敌机,否则游戏将会结束!!!\n\n\n\n");
	printf("按任意键继续...");
	getch();
}

void InitGame()
{
	initgraph(WIDTH, HEIGHT);	//游戏界面大小

	srand((unsigned)time(0));

	loadimage(&img_background, "background.bmp", WIDTH, HEIGHT);	//加载图片
	loadimage(&img_myplane, "myplane.bmp");
	loadimage(&img_bullet, "bullet.bmp");
	loadimage(&img_enemyplane, "enemyplane.bmp");

	mciSendString("open bj.mp3", 0, 0, 0);		//背景音乐
	mciSendString("play bj.mp3 repeat", 0, 0, 0);

	mine.x = WIDTH / 2 - 37, mine.y = HEIGHT - 200;	//飞机的初始位置
	mine.pNext = NULL;

	t1 = GetTickCount();	//用于获取系统时间
	t2 = GetTickCount();
	t3 = GetTickCount();
	t4 = GetTickCount();

	CreateList();	//创建链表
}

void RunGame()
{
	tt1 = GetTickCount();	//背景
	tt2 = GetTickCount();	//我方子弹
	tt3 = GetTickCount();	//敌机
	tt4 = GetTickCount();	//打印子弹和敌机

	if (tt1 - t1 >= 10)		//动态背景
	{
		if (bj_y >= HEIGHT)
			bj_y = 0;

		putimage(0, -HEIGHT + bj_y, &img_background);	//用两张图片形成循环
		putimage(0, bj_y, &img_background);

		bj_y += 1;
		t1 = tt1;
	}

	if (tt2 - t2 > 100)		//100个毫秒 生成子弹
	{
		CreateBulletEnemy(0);
		t2 = tt2;
	}
	if (tt3 - t3 > 1000)	//1000个毫秒 生成敌机
	{
		CreateBulletEnemy(1);
		t3 = tt3;
	}

	NODE* p;	//用于打印子弹、飞机
	if (tt4 - t4 > 10)
	{
		p = pBullet->pNext;
		while (p != NULL)	//打印子弹
		{
			putimage(p->x, p->y, 60, 25, &img_bullet, 60, 0, SRCAND);
			putimage(p->x, p->y, 60, 25, &img_bullet, 0, 0, SRCPAINT);

			p->y -= 5;
			p = p->pNext;
		}
		p = pEnemy->pNext;
		while (p != NULL)	//打印敌机
		{
			putimage(p->x, p->y, 50, 34, &img_enemyplane, 0, 0, SRCAND);
			putimage(p->x, p->y, 50, 34, &img_enemyplane, 50, 0, SRCPAINT);

			p->y += 2;
			p = p->pNext;
		}
		t4 = tt4;
	}

	if (kbhit())	//判断是否从键盘上按下字符
		MovePlane();

	JudgeGame();

	putimage(mine.x, mine.y, 75, 45, &img_myplane, 0, 15, SRCAND);	//自己的飞机
	putimage(mine.x, mine.y, 75, 45, &img_myplane, 75, 15, SRCPAINT);
}

void MovePlane()
{
	ch = getch();

	switch (ch)		//改变飞机的位置
	{
	case 72:
		if (mine.y > 0)
			mine.y -= 15;
		break;
	case 75:
		if (mine.x > 0)
			mine.x -= 15;
		break;
	case 77:
		if (mine.x < WIDTH - 75)
			mine.x += 15;
		break;
	case 80:
		if (mine.y < HEIGHT - 75)
			mine.y += 15;
		break;
	}
}

void CreateList()
{
	pBullet = (NODE*)malloc(sizeof(NODE));	//为结构体变量开辟一段空间
	pBullet->pNext = NULL;

	pEnemy = (NODE*)malloc(sizeof(NODE));
	pEnemy->pNext = NULL;
}

void CreateBulletEnemy(int flag)	//游戏逻辑部分
{
	NODE* pNew = (NODE*)malloc(sizeof(NODE));

	if (flag == 0)  //子弹
	{
		pNew->x = mine.x + 7;//   子弹宽度6 高度8
		pNew->y = mine.y - 25;

		pNew->pNext = pBullet->pNext;
		pBullet->pNext = pNew;
	}
	else if (flag == 1) //敌机
	{
		pNew->x = rand() % WIDTH;
		pNew->y = 0;

		pNew->pNext = pEnemy->pNext;
		pEnemy->pNext = pNew;
	}
}

void JudgeGame()
{
	//1  子弹和敌机 到达边界要释放
	NODE* pLastBullet = pBullet;			 //上一个的结点
	NODE* pPreBullet = pBullet->pNext;	 //当前的结点
	NODE* pLastEnemy = pEnemy;
	NODE* pPreEnemy = pEnemy->pNext;

	while (pPreBullet != NULL)
	{
		if (pPreBullet->y < 0)		//找到链表的最后一个
		{
			pLastBullet->pNext = NULL;
			free(pPreBullet);
			break;
		}
		pLastBullet = pLastBullet->pNext;
		pPreBullet = pPreBullet->pNext;
	}

	while (pPreEnemy != NULL)
	{
		if (pPreEnemy->y > HEIGHT)		//找到链表的最后一个
		{
			pLastEnemy->pNext = NULL;
			free(pPreEnemy);
			break;
		}
		pLastEnemy = pLastEnemy->pNext;
		pPreEnemy = pPreEnemy->pNext;
	}

	//2  子弹碰到敌机 子弹和敌机被释放

	pLastBullet = pBullet;			//每一颗子弹都要遍历一次敌机
	pPreBullet = pBullet->pNext;
	while (pPreBullet != NULL)
	{
		pLastEnemy = pEnemy;
		pPreEnemy = pEnemy->pNext;
		while (pPreEnemy != NULL)
		{
			if (abs(pPreBullet->x - pPreEnemy->x) < 50 && pPreBullet->y - pPreEnemy->y < 35 && pPreBullet->y - pPreEnemy->y > 0)
			{
				pLastBullet->pNext = pPreBullet->pNext;
				free(pPreBullet);

				pLastEnemy->pNext = pPreEnemy->pNext;
				free(pPreEnemy);

				pLastBullet = pBullet;		//子弹回到第一个!!!	原因:第一个已经被释放掉了 不能跳着来
				pPreBullet = pBullet->pNext;
				break;
			}
			else
			{
				pLastEnemy = pLastEnemy->pNext;
				pPreEnemy = pPreEnemy->pNext;
			}
		}
		pLastBullet = pLastBullet->pNext;
		pPreBullet = pPreBullet->pNext;
	}

	//3	 判断游戏是否结束

	pPreEnemy = pEnemy->pNext;
	while (pPreEnemy != NULL)
	{
		if (abs((mine.x + 37) - (pPreEnemy->x + 25)) < 62 && abs(mine.y - pPreEnemy->y) < 20)
		{
			MessageBox(NULL, "      游戏结束", "PlaneGame", MB_OK);
			exit(0);
		}
		pPreEnemy = pPreEnemy->pNext;
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_42100963/article/details/107446474
300