HUNAN UNIVERSITY
创新设计 (大作业)报告
课程名称: 电子系统设计与创新基础训练
实验项目名称: 坦克大战游戏
专 业 班 级: 通信工程1901
姓 名: 吴华
学 号: 201908030121
指 导 教 师: 骆坚
选题时间: 2021 年9月 8日 项目设计时间:2021 年9月 8日 至 2021 年9月 14日 报告完成时间:2021 年9月 15日
目录
1 设计概述 3 1.1设计目的 3 1.2设计任务 3 1.3设计要求 3 2 总体方案设计 4 3 实验过程与测试 7 4 设计总结与心得 9 4.1 设计总结 9 4.1设计心得 10
题目: 坦克大战游戏 ------基于BSP完成 1、项目概述 在我小时候流行一款名叫“小霸王”的小游戏机,里面的有一款游戏叫坦克大战让我印象深刻,一直想重温这个小游戏。这次实验项目采用的STC-B学习板制作,我发现上面的八位数码管似乎可以完成我的这个想法,也就是利用八位数码管作为显示屏完成这个坦克大战小游戏的制作。设计预想是设置进攻方与防守方两辆坦克,而八位数码管可以提供3条弹道作为炮弹发射的轨道,双方坦克都应该能够进行上下位移进行闪避。 另外,由于设置了进攻与防守两方,为了显示区别,也为了充分利用显示空间,我的想法就是设置一方可以进行上下与前后移动,发炮为手动控制,为进攻方;另一方只能进行上下位移,但是可以完成自动发炮,此为防守方。 1.1设计目的 在对小学期学习内容进行实际运用的同时,完成对童年记忆游戏的重温,希望设计出一款可以与当年小游戏机上面的坦克大战类似的游戏,引进音效以及增加移动与闪避功能,并且也可以记录实时得分与暂停游戏。 1.2设计任务 基于课程群中发出的DEMO文件,也就是基于BSP制作,在原有代码框架下,删除与我本次实验项目无关的部分功能和模块,同时根据我的需要,对原有函数进行必要改写,如八位数码管的译码表decode_table[],如用于显示的dealwithDisp()函数等等,我需要调用到包括导航键在内的全部按键,因此需要对可能用到的按键进行功能编写,并且将冲突的部分删除。 1.3设计要求 1)由于炮弹的轨迹必须可见,并且按固定的速度向前发射,所以要对其进行时间上的回调,并且场上可以同时存在3条弹道的炮弹,因此需要对每一类型的炮弹进行分类; 2)同一弹道上的两枚炮弹相遇就会引爆,也要分情况讨论; 3)而不同弹道上面的炮弹互不影响,相遇后仍然可以正常向原有方向前进; 4)防守方坦克Tank1需要完成自动发炮 5)进攻方坦克Tank2发炮后仍然可以前后位移 6)双方坦克均可以对来自上下两个方向到来的敌方炮弹进行“闪避”,需要显示出炮弹与坦克擦肩而过的瞬间状态 7)击中对方坦克可以计分,用LED二极管进行临时计分可以供游戏过程中实时查看,另外还要设置暂停功能,按下某个按键可以暂停游戏,并且可以在数码管上以阿拉伯数字表示双方得分,此外,还要设置一个按键回到游戏 8)游戏音效设计,炮弹与炮弹相遇会引爆,炮弹击中对方坦克也会引爆,此外,先得3分的一方设置为获胜方,游戏结束后播放专属音乐 2、总体方案设计 首先是关于导航按键,它用于控制进攻方坦克Tank2的前后上下移动,其中导航键的enter键进行Tank2的发炮动作;
我这里设置Key3按键对防守方坦克Tank1进行上下的位移,并且在位移动作的同时向前发射出一枚炮弹;
Key1按键我设置为查看当前得分,按下key1可以中断当前游戏,查看得分,key2按键则是返回游戏并对双方状态进行初始化,但并不改变双方得分;
关于数码管的译码表,我根据我的需要进行了添加,加入了我的坦克状态,炮弹状态,炮弹与坦克的叠加态等;
因为我的炮弹都是分类写的,每一枚炮弹之间互不影响,所以理论上场上是可以同时存在6枚炮弹,但是实际上由于数码管长度太短,而每一发炮弹需要占用一个格子,炮弹与炮弹之间也要有间隔,所以在操作时只能同时有4枚炮弹在显示屏上; 整体思路肯定是基于1S的时间回调函数去做,炮弹的自动前进与状态改变,还有弹道的区别都要分情况讨论,同时,在炮弹命中对方坦克后,发射炮弹的一方会得一分,以二进制的方式显示在LED二极管上,前4位为Tank1的得分,后4位为Tank2的得分,按下key1键可以中断游戏并且在数码管上查看实时分数;
关于坦克闪避炮弹的二者“擦肩而过”的状态也要添加,同样基于现有的1S时间回调函数,进行2S的刷新回调用于显示这个叠加态;
音效的加入,我选择了频率1350,时长100ms的短促音效作为炮弹引爆的声音,此外我还调用了上半学期自己编写的一段乐曲《old memory》作为获胜时的背景音乐;
3、 实验过程与测试 1) 炮弹与炮弹相遇引爆 因为我用的是全局的数组,所以我会在炮弹行进过程中进行一次判断,若前进方向的下一个数值与当前数值相同(即炮弹类型相同)则两枚炮弹均被引爆置零,显示为空 2)不同弹道炮弹相遇 在炮弹行进过程中进行判断,若前进方向的下一个数值与当前数值不同(即炮弹类型不同或前方净空) 则令两个数值进行交换,这就完成了炮弹的前进工作 3)同一坦克发射多枚炮弹 按照我的设计思路,一条弹道上面不允许同时存在两枚同一坦克发射的炮弹,这个设计是因为显示空间实在有限,如果在当前炮弹还未引爆前再次发射同一类型炮弹,那么前一枚炮弹将会停留在原地。当然,游戏过程中可以利用这一特性进行“防御”,这一停留在场上的炮弹会变成“屏障”,无论哪一方的炮弹击中都会引爆。 虽然不能在同一弹道发射多枚炮弹,但是可以“打一炮换一个阵地”,连续打出3枚炮弹,对方将会难于应付,很难全部挡下或闪避。 4)闪避 坦克本身是占满数码管的纵向两格的,如一个横向的大写字母T,但可以通过上下位移将一部分车身“躲起来”,这样就意味着可以闪避上下两个方向飞来的敌方炮弹(也就是说中间飞来的炮弹只能通过发射炮弹进行拦截)。 在数码管的译码表我添加了4个闪避状态,并且在判断到闪避现象发生的情况下送出一个判断值,用于对坦克与炮弹在一个数码管内“擦肩而过”的暂态的显示,这时候需要用上一个“2S的时间回调函数”, 这里就是利用原有1S时间回调函数对一个变量进行+1操作,判断能否被2整除即可。 5)计分 设置两个全局变量lc与rc,分别记录双方得分,由于用到LED二极管显示,所以分前四位与后四位, Tank1每次击中lc自加16,Tank2每次击中lc自加1,将lc与rc相加 表示为LedPrint(lc+rc)即可同时显示双方得分。 按下key1查看分数时,调用一个函数将lc与rc代表的实际得分显示在数码管上即可。 6)音效 炮击音效为频率1350,时长100ms的短促声音,直接使用SetBeep(1350,10); 一方获胜后,播放背景音乐《old memory》,这是上半学期自己编写的一段乐曲,改写入原DEMO的song.c文件中,因此我可以直接调用。 7)关于Tank2的“闪回”的加入 在实际游戏过程中,我发现按键的响应常常不够及时,常常Tank2还没来得及后退就被击中了,于是我决定把导航右键设置为“闪回”键,可以理解为一个极限闪避,按下一次即可直达数码管最右侧也就是d7位置,最大限度保证进攻方Tank2的安全,增加了游戏的可玩性。 4、 设计总结与心得 4.1设计总结 我个人很乐观地认为我已经完成了实验预期目标,这个坦克大战小游戏是完全可玩的,炮弹的相遇引爆,炮弹的击中效果已经坦克对炮弹的闪避,这些是原本游戏的规则,其实也就是最基础的规则,在此之外,我设置的进攻方与防守方概念是因为受限于数码管长度而做的变通,让一方不能左右移动,可以减少空间浪费,让进攻方Tank2拥有更多的空间发挥,为此我给防守方Tank1增加“自动发炮”的技能,算是让游戏变得相对公平。 但因为Tank1的“自动发炮”可以做到极限防御,并且体验后发现导航键不够灵敏,导致在游戏过程中进攻方Tank2常常因为后撤(也就是连续按导航Right键)不及时导致被击中,为此我冒出给Tank2增加“闪回”键的想法,只需要按一次Right键就能回到初始最右点,极大地提高了Tank2的生存能力。 暂停功能的设置其实一开始并没有,我使用Key1按键是为了查看准确的得分(LED虽然也在实时显示但毕竟是二进制不好看),但其实看得分的时候游戏就是中断状态,我一想干脆就做成key2恢复初始状态算了,这样当场面上出现大量敌方炮弹自己不能拦截或闪躲的情况下,可以通过按key1与key2进行一次初始化,这大概算一个作弊功能,但我认为为了游戏的进行(也为了同学的心情)应该加入的功能,这样游戏可以玩得更久也更快乐。我本来设置先得15分一方胜利,因为4位二进制的LED最多能显示15,但实际上很难打到15分,大概就是因为我加入了这些奇奇怪怪的功能吧,为此我最后把游戏规则改为先得3分胜利。 4.2设计心得 1) 我是基于给的DEMO进行改写,进行了大量删除工作,因为我的炮弹就是基于1S时间回调函数才能显示炮弹轨迹,但一开始不熟悉回调的含义,我把发射炮弹这个事件写成函数,再在my1S_callback()的里面调用,导致了各种奇奇怪怪的乱码,其实就是炮弹自己已经在走了,通过请教同学以及查看源文件代码,我发现了这个问题,于是把所有关于炮弹的情况都写进my1S_callback()里面,我在外面改变某一全局变量的值,然后引起my1S_callback() 内关于这种情况的反映,这就解决了困扰多天的乱码bug问题。同学间的讨论也帮助我解决了很多问题。 2) 学会变通也是这次实验给我的一个收获: 数码管长度有限,那就让Tank1不能左右移动; Tank1不能左右移动似乎不公平,那就给它加上一个“自动发炮”技能; 连续按导航Right键不能快速反应,那就新增“闪回”功能; 多枚炮弹打来拦不住又不想输,那就给一个“作弊”的机会吧。 3)充分利用材料,我在这个游戏里面把能用上的模块都用上了,数码管、LED、3个key以及5个导航按键,如果数码管长度够以及还有空余按键,或许我想加上2枚炮弹的连发,或者双弹道同时发射炮弹这种技能,实际上我的这个小游戏已经比小时候玩的“坦克大战”功能丰富了。 main.c部分
#include "main.H"
#include "song.c"
#include "music.h"
struct_ADC ADCresult;
unsigned char Music_tone,Music_PM;
unsigned char rxd[8];
unsigned char rxdhead[2]={0xAA,0x55};
unsigned char Tank1=0;
unsigned char Tank2=3;
int q,ll;int w=1;
unsigned char plm=0;
unsigned char pl[6]={0,0,0,0,0,0};
unsigned char key[6]={0,0,0,0,0,0};
unsigned char paodan[6]={9,9,9,9,9,9};
unsigned char zu[6]={0,0,0,0,0,0};
unsigned char flag[6]={1,1,1,1,1,1};
unsigned char t[8]={0,16,16,16,16,16,16,3};
unsigned char lc=0,rc=0;
#include "function.c"
void myUart1Rxd_callback()
{ if ( GetUart2TxStatus() == enumUart2TxFree )
{ (*(rxd+6)) += 1;
Uart2Print(&rxd, sizeof(rxd));
}
}
void myUart2Rxd_callback()
{ if ( GetIrStatus() == enumIrFree )
{ (*(rxd+6)) += 2;
IrPrint(&rxd, sizeof(rxd));
}
}
void my1mS_callback()
{
}
void my10mS_callback()
{ dealwithDisp();
}
void my100mS_callback()
{
}
void my1S_callback()
{
LedPrint(lc+rc);
if(flag[0]==0){
zu[0]=paodan[0];paodan[0]++;
if(t[zu[0]+1]==Tank2){Tank2=14;t[zu[0]+1]=14;t[zu[0]]=16;flag[0]=1;lc=lc+16;SetBeep(1350,10);}
else if(t[zu[0]+1]==t[zu[0]]) {t[zu[0]]=16;t[zu[0]+1]=16; flag[0]=1;}
else if(t[zu[0]+1]==16){pl[0]=t[zu[0]+1];t[zu[0]+1]=t[zu[0]];t[zu[0]]=pl[0]; flag[0]=0;}
}
if(flag[1]==0){
zu[1]=paodan[1];paodan[1]++;
if(t[zu[1]+1]==Tank2&&Tank2==5){Tank2=25;t[zu[1]+1]=25;key[1]=zu[1]+1;t[zu[1]]=16;flag[1]=1;}
else if(t[zu[1]+1]==Tank2){Tank2=14;t[zu[1]+1]=14;t[zu[1]]=16;flag[1]=1;lc=lc+16;SetBeep(1350,10);}
else if(t[zu[1]+1]==t[zu[1]]) {t[zu[1]]=16;t[zu[1]+1]=16; flag[1]=1;}
else if(zu[1]+1>=8){t[zu[1]]=16;}
else if(t[zu[1]+1]==16){pl[1]=t[zu[1]+1];t[zu[1]+1]=t[zu[1]];t[zu[1]]=pl[1]; flag[1]=0;}
}
if(flag[2]==0){
zu[2]=paodan[2];paodan[2]++;
if(t[zu[2]+1]==Tank2&&Tank2==4){Tank2=26;t[zu[2]+1]=26;key[2]=zu[2]+1;t[zu[2]]=16;flag[2]=1;}
else if(t[zu[2]+1]==Tank2){Tank2=14;t[zu[2]+1]=14;t[zu[2]]=16;flag[2]=1;lc=lc+16;SetBeep(1350,10);}
else if(t[zu[2]+1]==t[zu[2]]) {t[zu[2]]=16;t[zu[2]+1]=16; flag[2]=1;}
else if(zu[2]+1>=8){t[zu[2]]=16;}
else if(t[zu[2]+1]==16){pl[2]=t[zu[2]+1];t[zu[2]+1]=t[zu[2]];t[zu[2]]=pl[2]; flag[2]=0;}
}
if(flag[3]==0){
zu[3]=paodan[3];paodan[3]--;
if(t[zu[3]-1]==Tank1&&Tank1==1){Tank1=24;t[0]=24;t[1]=16;key[3]=1;flag[3]=1;}
else if(t[zu[3]-1]==Tank1){Tank1=14;t[zu[3]-1]=14;t[zu[3]]=16;flag[3]=1;rc=rc+1;SetBeep(1350,10);}
else if(t[zu[3]-1]==t[zu[3]]){t[zu[3]-1]=16;t[zu[3]]=16;flag[3]=1;SetBeep(1350,10);}
else if(t[zu[3]-1]!=t[zu[3]]){pl[3]=t[zu[3]-1];t[zu[3]-1]=t[zu[3]];t[zu[3]]=pl[3];flag[3]=0;}
}
if(flag[4]==0){
zu[4]=paodan[4];paodan[4]--;
if(t[zu[4]-1]==Tank1&&Tank1==2){Tank1=23;t[zu[4]-1]=23;t[zu[4]]=16;key[4]=1;flag[4]=1;}
else if(t[zu[4]-1]==Tank1){Tank1=14;t[zu[4]-1]=14;t[zu[4]]=16;flag[4]=1;rc=rc+1;SetBeep(1350,10);}
else if(t[zu[4]-1]==t[zu[4]]){t[zu[4]-1]=16;t[zu[4]]=16;flag[4]=1;SetBeep(1350,10);}
else if(t[zu[4]-1]!=t[zu[4]]){pl[4]=t[zu[4]-1];t[zu[4]-1]=t[zu[4]];t[zu[4]]=pl[4];flag[4]=0;}
}
if(flag[5]==0){
zu[5]=paodan[5];paodan[5]--;
if(t[zu[5]-1]==Tank1){Tank1=14;t[0]=14;t[zu[5]]=16;flag[5]=1;rc=rc+1; SetBeep(1350,10);}
else if(t[zu[5]-1]==t[zu[5]]){t[zu[5]-1]=16;t[zu[5]]=16;flag[5]=1;SetBeep(1350,10);}
else if(t[zu[5]-1]!=t[zu[5]]){pl[5]=t[zu[5]-1];t[zu[5]-1]=t[zu[5]];t[zu[5]]=pl[5];flag[5]=0;}
}
plm++;
if(plm%2==0)
{ if(key[4]!=0){Tank1=2;t[0]=2;key[4]=0;}
if(key[3]!=0){Tank1=1;t[0]=1;key[3]=0;}
if(key[2]!=0){Tank2=4;t[key[2]]=4;paodan[2]=key[2]+1;t[key[2]+1]=17;flag[2]=0;key[2]=0;}
if(key[1]!=0){Tank2=5;t[key[1]]=5;paodan[1]=key[1]+1;t[key[1]+1]=19;flag[1]=0;key[1]=0;}}
}
void myADC_callback()
{
}
void myKN_callback()
{ dealwithmyKN();
}
void myhall_callback()
{ if(GetHallAct() == enumHallGetClose) SetBeep(1350,100);
}
void mykey_callback()
{ dealwithmykey();
}
void mySV_callback()
{ if(GetVibAct())
if (GetPlayerMode() == enumModePause) SetPlayerMode(enumModePlay);
else SetPlayerMode(enumModePause);
}
void main() {
Key_Init();
HallInit();
VibInit();
DisplayerInit();
BeepInit();
MusicPlayerInit();
AdcInit(ADCexpEXT);
IrInit(NEC_R05d);
Uart1Init(1200);
Uart2Init(2400,Uart2Usedfor485);
SetEventCallBack(enumEventKey, mykey_callback);
SetEventCallBack(enumEventSys1mS, my1mS_callback);
SetEventCallBack(enumEventSys10mS, my10mS_callback);
SetEventCallBack(enumEventSys100mS, my100mS_callback);
SetEventCallBack(enumEventSys1S, my1S_callback);
SetEventCallBack(enumEventHall, myhall_callback);
SetEventCallBack(enumEventVib, mySV_callback);
SetEventCallBack(enumEventNav, myKN_callback);
SetEventCallBack(enumEventUart1Rxd, myUart1Rxd_callback);
SetEventCallBack(enumEventUart2Rxd, myUart2Rxd_callback);
SetEventCallBack(enumEventXADC,myADC_callback);
SetDisplayerArea(0,7);
SetUart1Rxd(&rxd, sizeof(rxd), rxdhead, sizeof(rxdhead));
SetUart2Rxd(&rxd, sizeof(rxd), rxdhead, sizeof(rxdhead));
SetIrRxd(&rxd);
LedPrint(0);
MySTC_Init();
while(1)
{ MySTC_OS();
if(lc==48||rc==3){SetMusic(Music_PM, Music_tone, &song, sizeof(song), enumMscNull);
SetPlayerMode(enumModePlay);}
if(lc==48||rc==3){lost();}
}
}
function.c部分
void dealwithDisp(){ Seg7Print(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7]);}
void lost(){t[0]=(lc/16)/10+6;t[1]=(lc/16)%10+6;
t[2]=18;t[3]=18;t[4]=18;t[5]=18;t[6]=rc/10+6;t[7]=rc%10+6;}
void dealwithmyKN()
{
if(GetAdcNavAct(enumAdcNavKey3) == enumKeyPress)
{
if((Tank1++)>=2)Tank1=0;t[0]=Tank1;
if(t[0]==0){paodan[0]=1;t[1]=18;flag[0]=0;
}
if(t[0]==1){paodan[1]=1;t[1]=19;flag[1]=0;
}
if(t[0]==2){paodan[2]=1;t[1]=17;flag[2]=0;
}
}
if(GetAdcNavAct(enumAdcNavKeyUp)== enumKeyPress) {
for(q=0;q<8;q++){ if(Tank2==t[q]){
if((Tank2++) >=5) Tank2 =3;
t[q]=Tank2;dealwithDisp();}}}
if(GetAdcNavAct(enumAdcNavKeyDown)== enumKeyPress) {
for(q=0;q<8;q++){ if(Tank2==t[q]){
if((Tank2--) <=3) Tank2 =5;
t[q]=Tank2;dealwithDisp();}}}
if(GetAdcNavAct(enumAdcNavKeyRight) == enumKeyPress){
for(q=0;q<8;q++){ ll=q; if(Tank2==t[q]&&(q>=2&&q<=7)){
if((ll++) >=7) ll =7;
t[ll]=Tank2;if(ll!=q)t[q]=16;dealwithDisp();}}}
if(GetAdcNavAct(enumAdcNavKeyLeft) == enumKeyPress){
q=0;for(;q<8;q++){ ll=q; if(Tank2==t[q]&&(q>=2&&q<=7)){
if((ll--)<=2) ll =2;
t[ll]=Tank2;if(ll!=q)t[q]=16;dealwithDisp();}}}
if(GetAdcNavAct(enumAdcNavKeyCenter)== enumKeyPress)
{ w=1;
if(Tank2==3)
for(;w<8;w++)
if(t[w]==Tank2){t[w-1]=18;paodan[5]=w-1;flag[5]=0;}
if(Tank2==4)
for(;w<8;w++)
if(t[w]==Tank2){t[w-1]=19;paodan[4]=w-1;flag[4]=0;}
if(Tank2==5)
for(;w<8;w++)
if(t[w]==Tank2){t[w-1]=17;paodan[3]=w-1;flag[3]=0;}
}
}
void dealwithmykey()
{
if (GetKeyAct(enumKey1)== enumKeyPress)
{
lost();
}
if (GetKeyAct(enumKey2)== enumKeyPress)
{
Tank1=0;Tank2=3; plm=0;
pl[0]=0;pl[1]=0;pl[2]=0;pl[3]=0;pl[4]=0;pl[5]=0;
key[0]=0;key[1]=0;key[2]=0;key[3]=0;key[4]=0;key[5]=0;
paodan[0]=9;paodan[1]=9;paodan[2]=9;
paodan[3]=9;paodan[4]=9;paodan[5]=9;
zu[0]=0;zu[1]=0;zu[2]=0;zu[3]=0;zu[4]=0;zu[5]=0;
flag[0]=1;flag[1]=1;flag[2]=1;flag[3]=1;flag[4]=1;flag[5]=1;
t[0]=0;t[1]=16;t[2]=16;t[3]=16;t[4]=16;t[5]=16;t[6]=16;t[7]=3;
paodan[0]=1;t[1]=18;flag[0]=0;
}
}
添加链接描述
|