大家好,我是作者:隐匿269,今天我带来的是用链表制作游戏,飞机大战(俗称:打飞机小游戏)。我会运用到图形库,没有图形库的伙伴不用着急,把打印图片的步骤省略,代码也能运行。
一、准备(大纲)
编程时,首先要做的就是整理好思路。我们要思考飞机大战的游戏规则,以及飞机的移动,敌机、子弹的创建和销毁等。总的可以分为一下几个环节。
* 1、初始化地图窗口,加载图片,飞机链表结构体的实现,变量的定义
* 2、生成我放飞机,并移动
* 3、发射子弹、移动、销毁
4、敌方飞机生成、移动、销毁
5、子弹消灭敌机
6、敌机和我放飞机碰撞
7、表示游戏结束
8、计分
9、背景音乐
二、代码
//头文件名称"Airplane.h"
#pragma once
#include <stdio.h>
#include <stdlib.h>
//头文件重复函数
//链表结构的声明和定义
//链表节点结构体的声明
typedef struct Node
{
int x;//坐标
int y;//坐标
int speed;//移动速度
struct Node*next;//指针域,用于连接下一个节点
}Node;
//链表结构体的声明
typedef struct Linklist
{
Node*head;//头部节点指针
Node*end;//尾部节点指针
}LL;
//链表节点的创建函数
Node *linklist_init(int x, int y, int speed)
{
Node*temp = (Node*)malloc(sizeof(Node));
if (temp == NULL)
{
return 0;
}
temp->speed = speed;
temp->x = x;
temp->y = y;
temp->next = NULL;
return temp;
}
//链表节点的插入
void linklist_insert(LL*list, int x, int y, int speed)
{
if (NULL == list)
{
return;
}
if (list->head == NULL)
{
list->head = list->end = linklist_init(x, y, speed);
}
else
{
list->end->next = linklist_init(x, y, speed);
list->end = list->end->next;
}
}
//链表节点的删除函数
void Node_delete(LL*list, Node*p)
{
if (list == NULL || list->head == NULL)
{
return;
}
if (p == list->head)
{
list->head = p->next;
free(p);
return;
}
Node*temp = list->head;
for (; temp; temp = temp->next)
{
if (temp->next = p)
{
if (p == list->end)
{
list->end = temp;
}
temp->next = p->next;
free(p);
p = NULL;
return;
}
}
}
//链表的释放,销毁
void linklist_dekete(LL*list)
{
if (list->head == NULL || list == NULL)
{
return;
}
for (Node*temp1 = list->head; temp1 != NULL;)
{
Node*temp2 = temp1;
temp1 = temp1->next;
free(temp2);
}
list->head = list->end = NULL;
}
//头文件名称"AirWar.h"
#pragma once
#include"Airplane.h"
//图形库头文件
#include"graphics.h"
#include "stdlib.h"
#include<stdio.h>
#include<conio.h>//_getch()、getch()、_kbhit()头文件
#include<time.h>//时间函数头文件
#include<math.h>//数学函数头文件
#include<tchar.h>//宽字节字符头文件
#define BGWIDETH 400 //窗口、背景宽度
#define BGHIGHT 600 //窗口、背景的高度
#define myPlaneBGWIDETH 50 //我方飞机宽度
#define myPlaneBGHIGHT 75 //我方飞机高度
#define enemyPlaneBGWIDETH 50 //敌方飞机宽度
#define enemyPlaneBGHIGHT 40 //敌方飞机高度
#define BulletBGWIDETH 10 //子弹宽度
#define BulletBGHIGHT 16 //子弹高度
#pragma comment(lib,"winmm.lib")//包含音乐静态库winmm.lib
IMAGE img[4]; //图片数组
Node myPlane; //我方飞机
LL myBullet; //我方子弹链表
LL enemyPlane; //敌方飞机链表
int isWin; //游戏开始、结束初始化为0
int score; //游戏开始得分初始化为0
//加载图片
void init()
{
//定义随机种子
srand((unsigned)time(NULL));
loadimage(&img[0], "res\\背景.jpg", BGWIDETH, BGHIGHT);
loadimage(&img[1], "res\\敌机.jpg", enemyPlaneBGWIDETH, enemyPlaneBGHIGHT);
loadimage(&img[2], "res\\飞机.jpg", myPlaneBGWIDETH, myPlaneBGHIGHT);
loadimage(&img[3], "res\\子弹.jpg", BulletBGWIDETH, BulletBGHIGHT);
}
//初始化我放飞机的坐标和速度
void myPlane_init()
{
isWin = 0;
score = 0;
mciSendString("open res\\JustBlue.mp3", 0, 0, 0);//mciSendString函数用于设置音乐相关选项
//open加载音乐(打开音乐)
//打开音乐时需要注意:音乐文件的名称不能含有空客' ',否则无法识别音乐
//play播报音乐
mciSendString("play res\\JustBlue.mp3", 0, 0, 0);
myPlane.x = BGWIDETH/2- enemyPlaneBGWIDETH/2;
myPlane.y = BGHIGHT- myPlaneBGHIGHT -25;
myPlane.speed = 5;
myPlane.next = NULL;
//初始化子弹的坐标和速度
}
//我方子弹生成函数
void creatmyBullet()
{
linklist_insert(&myBullet, myPlane.x + (myPlaneBGWIDETH / 2) - (BulletBGWIDETH / 2), myPlane.y + (myPlaneBGHIGHT/2)-BulletBGHIGHT, 10);
}
//敌机的生成函数(随机)
void createEnemyPlane()
{
if ((rand() % 200) == 199)
linklist_insert(&enemyPlane, rand() % (BGWIDETH- enemyPlaneBGWIDETH), 0 - enemyPlaneBGHIGHT, (rand() % 2)+1);
}
//子弹和敌机的移动
void move()
{
//子弹移动
for (Node*temp = myBullet.head; temp; temp = temp->next)
{//循环遍历整个子弹链表
temp->y -= temp->speed;
}
//敌机移动
for (Node*temp = enemyPlane.head; temp; temp = temp->next)
{//循环遍历整个敌机链表
temp->y += temp->speed;
}
}
//玩游戏函数,我方飞机的移动
void play()
{
if (_kbhit())
{
switch (getch())
{
//向上移动
case 'w':
case 'W':
if(myPlane.y>=0)
myPlane.y -= myPlane.speed;
break;
//向左移动
case 'a':
case 'A':
if (myPlane.x>=0)
myPlane.x -= myPlane.speed;
break;
//向右移动
case 'd':
case 'D':
if (myPlane.x<=BGWIDETH- myPlaneBGWIDETH)
myPlane.x += myPlane.speed;
break;
//向下移动
case 'x':
case 'X':
if (myPlane.y <= BGHIGHT- myPlaneBGHIGHT)
myPlane.y += myPlane.speed;
break;
//手动发射子弹
case 'b':
case 'B':
creatmyBullet();
break;
}
}
}
//绘制地图
void DrawMap()
{
BeginBatchDraw();//开始批量绘图
putimage(0, 0, &img[0]);//绘制背景
//绘制敌机
for (Node*temp = enemyPlane.head; temp; temp = temp->next)
{//循环遍历整个飞机链表
putimage(temp->x, temp->y, &img[1]);
}
//绘制飞机
putimage(myPlane.x, myPlane.y, &img[2]);
//绘制子弹
for (Node*temp = myBullet.head; temp; temp = temp->next)
{//循环遍历整个子弹链表
putimage(temp->x,temp->y, &img[3]);
}
TCHAR str[50];//需要包含头文件tchar.h,能够很好的保存中文,宽字节字符
//定义一个宽字节字符TCHAR数组str,用于存储即将显示的分数字符串,
sprintf(str, "当前的分:%d", score);
//输入函数,将其中 "当前的分:"这个字符串放到宽字节字符TCHAR数组str中,可以使用
//格式占位符(如:%d、%c%f等)向字符串中插入变量的值
setbkmode(TRANSPARENT);
outtextxy(0, 0, str);
EndBatchDraw();//开始批量绘图
}
//子弹碰撞检测函数
int Blt_isColllision(Node*p,Node*q)
{
//用中心点判断
//求子弹中心点坐标
int px = p->x + BulletBGWIDETH / 2;//x坐标
int py = p->y + BulletBGHIGHT / 2;//y坐标
//求敌机中心点坐标
int qx = q->x + enemyPlaneBGWIDETH / 2;//x坐标
int qy = q->y + enemyPlaneBGHIGHT / 2;//y坐标
if (abs(px - qx) >=( (BulletBGWIDETH + enemyPlaneBGWIDETH) / 2)
|| abs(py - qy) >= ((BulletBGHIGHT + enemyPlaneBGHIGHT) / 2))
{
return 0;
}
else
{
return 1;
}
}
//敌机、飞机碰撞检测函数
int Air_isColllision(Node*p, Node*q)
{
//求飞机中心点坐标
int px = p->x + myPlaneBGWIDETH / 2;//x坐标
int py = p->y + myPlaneBGHIGHT / 2;//y坐标
//求敌机中心点坐标
int qx = q->x + enemyPlaneBGWIDETH / 2;//x坐标
int qy = q->y + enemyPlaneBGHIGHT / 2;//y坐标
if (abs(px - qx) >= ((myPlaneBGWIDETH + enemyPlaneBGWIDETH) / 2)
|| abs(py - qy) >= ((myPlaneBGHIGHT + enemyPlaneBGHIGHT) / 2))
{
return 0;
}
else
{
return 1;
}
}
//子弹销毁和敌机的销毁
void Delete()
{
for (Node*temp = myBullet.head; temp; temp = temp->next)
{
if (temp->y <0- BulletBGHIGHT)
{//如果子弹的y坐标小于0,则判断子弹移动到屏幕外面,销毁子弹
Node_delete(&myBullet, temp);//销毁子弹链表
return;//一次只释放一个节点
}
}
for (Node*temp2 = enemyPlane.head; temp2; temp2 = temp2->next)
{
if (temp2->y > BGHIGHT)
{//如果子弹的y坐标大于屏幕高度,则判断子弹移动到屏幕外面,销毁子弹
Node_delete(&enemyPlane, temp2);//销毁子弹链表
return;//一次只释放一个节点
}
for (Node*temp1 = myBullet.head; temp1; temp1 = temp1->next)
{
if (Blt_isColllision(temp1, temp2)/*子弹消灭敌机*/)
{
Node_delete(&myBullet, temp1);//销毁子弹
Node_delete(&enemyPlane, temp2);//销毁敌机
score++;
return;
}
}
if (Air_isColllision(&myPlane, temp2))
{
//如果我方飞机碰到了敌机
//游戏失败
isWin = 1;//游戏失败
return;
}
}
}
void start()
{
//打开图形库窗口
initgraph(BGWIDETH, BGHIGHT);
//加载图片
init();
//初始化飞机数据
myPlane_init();
while (1)
{
//绘制地图
DrawMap();
//移动
play();
createEnemyPlane();//随机生成敌机
//子弹和敌机的移动
move();
//子弹和敌机的销毁
Delete();
if (isWin)
{
linklist_dekete(&enemyPlane);//游戏结束,释放整个敌机列表
linklist_dekete(&myBullet);//游戏结束,释放整个子弹列表
TCHAR str[50];//需要包含头文件tchar.h,能够很好的保存中文,宽字节字符
//定义一个宽字节字符TCHAR数组str,用于存储即将显示的分数字符串,
sprintf(str, "当前的分:%d\n是否重新开始游戏?", score);
int temp=MessageBox(GetForegroundWindow(), str, "游戏失败", MB_YESNO);
//临时变量temp接受弹窗按键后的返回值
if (6 == temp)//'6'表示弹窗里按下“是”这个按钮
{
init();//重新初始化游戏
myPlane_init();
}
else if(7 == temp)//'7'表示弹窗里按下“否”这个按钮
{
break;
}
}
Sleep(10);//延迟一毫秒
}
closegraph();
}
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include"AirWar.h"
/*
飞机(我方,敌方),地图,子弹
* 1、初始化地图窗口,加载图片,飞机链表结构体的实现,变量的定义
* 2、生成我放飞机,并移动
* 3、发射子弹、移动、销毁
4、敌方飞机生成、移动、销毁
5、子弹消灭敌机
6、敌机和我放飞机碰撞
7、表示游戏结束
8、计分
9、背景音乐
*/
int main()
{
start();//开始函数
system("pause");
return 0;
}