此作业的要求参见[https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11577]
作业要求
- 课程网址 [https://www.icourse163.org/course/HHU-1206797807]
- 项目名称及分值
游戏名称 | 满分分值 | 功能点提示 |
---|---|---|
飞机大战 | 50 | 移动飞机、发射子弹、敌机移动、消灭敌机、被敌机撞击、存档读档 |
- 作业提交要求
除代码及git以外,要求 WBS、PSP,要求使用博客报告完成的功能和截图,讲解
关键技术和代码片断。其中WBS要求包括不限于每个子任务的工时估算时间和实
际耗时,精确到分钟。子任务可以包括分析、设计、代码、测试、调试、文档,
鼓励精确到二级子任务如功能点等。
项目git地址
https://github.com/ZigHello/learngit/tree/master/%E9%A3%9E%E6%9C%BA%E5%A4%A7%E6%88%98
项目PSP
类型 | 任务 | 开始时间 | 结束时间 | 中断时间(分钟) | delta时间(分钟) |
---|---|---|---|---|---|
分析 | 技术原型、函数设计 | 12.07 8:07 | 12.07 9:23 | 0 | 76 |
编程 | 实现英雄机能按键移动 | 12.07 9:51 | 12.07 10:16 | 0 | 25 |
编程 | 实现子弹的移动 | 12.07 10:20 | 12.07 11:03 | 0 | 43 |
编程 | 实现敌机的移动 | 12.07 11:03 | 11.29 11:26 | 0 | 23 |
编程 | 实现子弹击中敌机 | 12.07 11:30 | 11.29 12:18 | 0 | 48 |
编程 | 实现得分的变化 | 12.08 14:00 | 12.08 14:14 | 0 | 14 |
编程 | 实现多发子弹 | 12.08 14:14 | 12.08 15:38 | 0 | 84 |
编程 | 实现多个敌机 | 12.08 15:40 | 12.08 16:54 | 0 | 74 |
编程 | 英雄机与敌机碰撞 | 12.08 18:14 | 12.08 19:23 | 0 | 69 |
博客 | 技术文档说明 | 12.08 20:30 | 12.08 22:11 | 0 | 101 |
总耗时 | 0 | 559min | |||
项目WBS | |||||
任务类型 | 估计时间 | 实际用时 | |||
---- | ---- | ---- | |||
技术原型、函数设计 | 45min | 76min | |||
消息循环 | 25min | 15min | |||
获取按键消息 | 30min | 25min | |||
对象移动 | 90min | 56min | |||
随机数生成 | 10min | 20min | |||
碰撞检测 | 90min | 117min | |||
边界控制 | 30min | 67min | |||
关键技术 |
- 功能:移动飞机、发射子弹、敌机移动、消灭敌机、被敌机撞击
- 对象分析:
- 我机:绘制->按键控制移动->被敌机击中,游戏结束
- 子弹:绘制->按键控制子弹发射->子弹上移
- 敌机:绘制->敌机下移->子弹击中,敌机消失
- 技术原型:
- 界面绘制
- 获取按键消息
- 对象移动
- 随机数生成
- 碰撞检测
- 边界控制
- 函数设计:
绘图函数: show()
功能:游戏界面定义为一个二维数组,定义数组值为0时,绘制空格;数组值为1时,绘制我机;数组为2时,绘制子弹;数组为3时,绘制敌机。
数据初始化函数:dataInit()
功能:游戏开始,初始化我机、敌机位置。
获取用户的按键消息函数:getUserInput()
功能:获取用户的按键消息,改变我机的坐标位置及控制子弹是否发射。
子弹上移函数:updateBullet()
功能:子弹上移,改变子弹坐标。子弹超出边界,坐标置零。
敌机下移函数:updateEnemy()
功能:敌机下移,改变敌机坐标。敌机超出边界,分数--。
碰撞函数:hitEnemy()
功能:如果子弹击中敌机,分数++。原始敌机消失,子弹消失,同时生成新的敌机。
如果敌机与我机相撞,游戏结束。
游戏退出函数:gameOver()
功能:退出游戏。
采用二维数组的方式绘制界面,规定0画空格,1画我机,2画子弹,3画敌机
void show() //界面显示
{
gotoxy(0, 0); //重绘
int i, j;
for (i = 0; i < High; i++)
{
for (j = 0; j < Width; j++)
{
if (flag == 1)
break;
else if (game[i][j] == 1) //绘制飞机
printf("*");
else if (game[i][j] == 2) //绘制子弹
printf("|"); //输出子弹
else if (game[i][j] == 3) //绘制敌机
printf("@");
else if ((j == Width - 1) || (i == High - 1) || (j == 0) || (i == 0)) //绘制边界
printf("-");
else
printf(" "); //输出空格
}
printf("
");
}
printf("得分:%d",score);
};
通过kbhit()函数获取按键消息,从而控制英雄级的移动及子弹的发射状况。
void getUserInput() // 与用户输入有关的更新
{
char input;
if(_kbhit()) //当按键时
{
input = _getch();
if ((input == 'a') && position_y > 1)
{
game[position_x][position_y] = 0;
position_y--;
game[position_x][position_y] = 1;
}
if ((input == 'd') && position_y < Width - 2)
{
game[position_x][position_y] = 0;
position_y++;
game[position_x][position_y] = 1;
}
if ((input == 'w') && position_x > 1)
{
game[position_x][position_y] = 0;
position_x--;
game[position_x][position_y] = 1;
}
if ((input == 's') && position_x < High - 2)
{
game[position_x][position_y] = 0;
position_x++;
game[position_x][position_y] = 1;
}
if (input == ' ')
{
bullet_x = position_x - 1;
game[bullet_x][x] = 2;
}
}
};
更新敌机及子弹的位置,需要先将子弹原始位置的数组值置为0,再改变子弹纵坐标位置后,数组值置为2。敌机位置的改变原理同子弹一致。
void updateBullet() //更新子弹位置
{
int i, j;
for (i = 0; i < High - 1; i++)
{
for (j = 0; j < Width - 1; j++)
{
if (game[i][j] == 2)
{
game[i][j] = 0;
if (i > 0)
{
i--;
game[i][j] = 2;
}
}
}
}
};
子弹与敌机的碰撞检测做的较简单,循环遍历整个数组,如果当前位置的数组值为2,则表示此处为子弹,再判断该位置的坐标与敌机坐标是否相同,相同则表示击中敌机,分数加1。与此同时子弹与敌机均消失,然后生成新的敌机。下面给出的代码是多台敌机的碰撞检测情况。
同理,如果当前位置的数组值为1,表示英雄机在此处,判断敌机坐标是否与英雄机坐标相同,相同则游戏结束。
void hitEnemy()
{
int i, j, k;
for (i = 0; i < High - 1; i++)
{
for (j = 0; j < Width - 1; j++)
{
if (game[i][j] == 2) //子弹击中敌机
{
for (k = 0; k < EnemyNum; k++)
{
if ((i == enemy_x[k]) && (j == enemy_y[k]))
{
score++; //击中加分
if (score % 5 == 0) //子弹变厉害
BulletWidth++;
game[enemy_x[k]][enemy_y[k]] = 0;
enemy_x[k] = rand()%2; //产生新的敌机
enemy_y[k] = 2 + rand() % Width - 2;
game[enemy_x[k]][enemy_y[k]] = 3;
game[i][j] = 0; //子弹消失
}
}
}
if (game[i][j] == 1) //我机与敌机相撞
{
for (k = 0; k < EnemyNum; k++)
{
if ((i == enemy_x[k]) && (j == enemy_y[k]))
{
printf("游戏结束!!!
");
flag = 1;
}
}
}
}
}
}
采用了while循环来重绘页面,为了控制页面的重绘光标的闪动,引入了HideCursor()函数来隐藏光标。
//隐藏光标函数
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1,0 };//第二个值为0表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
最终效果