纯汇编实现打飞机小游戏的示例代码
汇编暑假作业要求做一个大项目,题目可以自拟。我思来想去,还是觉得做一个小游戏比较有意思。最后选择了做打飞机游戏。
这里采用的是VGA模式320x2004色。
打飞机游戏的游戏逻辑比较简单。首先,飞机可以移动,也可以发射炮弹;其次,会有敌人不断地从前方飞过来,如果撞上飞机游戏结束;最后,飞机发射的炮弹可以击落敌人。
既然是打飞机,我们就必须首先造一台飞机,代码如下:
Comment/*********** function:drawahorizontalline parameters:horizontalposition verticalposition lengthoftheline color return:void description:drawsomepointshorizontally. color'0001h'representsdrawingalinewhile color'0000h'whichisblackmeanserasetheline. **********/ drawALinePROCNEAR PUSHBP MOVBP,SP PUSHAX PUSHCX PUSHDX PUSHSI MOVAH,0Ch MOVCX,[BP+4] MOVDX,[BP+6] MOVSI,[BP+8] MOVAL,BytePtr[BP+10] drawALineLoop: INT10h INCCX DECSI JNZdrawALineLoop POPSI POPDX POPCX POPAX MOVSP,BP POPBP RET drawALineENDP Comment/*********** function:drawaplaneoramissile parameters:horizontalposition verticalposition color type(planeormissileorenemy) maplength return:void description:call"drawALine"functionrepeatedly. **********/ drawCraftPROCNEAR PUSHBP MOVBP,SP SUBSP,8 PUSHAX PUSHDX PUSHSI PUSHDI MOVDI,0 MOVAX,[BP+12] MOV[BP-8],AX MOVSI,[BP+10] MOVAX,[BP+8] MOV[BP-6],AX MOVAX,[BP+6] MOV[BP-4],AX MOVAX,[BP+4] MOV[BP-2],AX drawCraftLoop: PUSHWordPtr[BP-6] MOVDX,WordPtr[SI] PUSHDX PUSHWordPtr[BP-4] MOVAX,[BP-2] SHRDX,1 SUBAX,DX PUSHAX CALLdrawALine ADDSP,8 ADDWordPtr[BP-4],1 ADDSI,2 INCDI CMPDI,[BP-8] JBdrawCraftLoop POPDI POPSI POPDX POPAX MOVSP,BP POPBP RET drawCraftENDP
PLANEMAPDW1,1,3,3,3,3,3,3,7,14,16,14,6,2,2,6,6
N1EQU($-PLANEMAP)/2
这里之所以做的这么复杂是出于代码重用的考虑,相同的代码只要输入不同的参数,就能制造出不一样的东西。这是抽象的思想。
drawCraft根据输入的不同的数组,可以绘制出不同的东西,比如飞机,导弹,敌人。而不必画飞机一个函数,画导弹又是另一个函数了。
drawCraft主要是一条线一条线地画,就像3D打印一样。不过这里是2D的版本。
光画了飞机不行,我们还需要让它动起来。动起来的方法很简单,只需要用黑色覆盖原图,然后再在新的位置上建立一个新的即可:
Comment/*********** function:moveaplane parameters:originalhorizontalposition originalverticalposition direction(left-aup-wright-dbottom-s) return:rectifyPOS_X,POS_Y description:destorytheoriginalandthencreateanewone **********/ movePlanePROCNEAR PUSHBP MOVBP,SP PUSHAX PUSHBX PUSHCX MOVAX,N1 PUSHAX MOVAX,OffsetPLANEMAP PUSHAX MOVAX,0000h PUSHAX;blackcolor MOVAX,[BP+6] PUSHAX MOVAX,[BP+4] PUSHAX CALLdrawCraft ADDSP,10 MOVCX,CS:MoveItems MOVAH,BytePtr[BP+9] MOVBX,OffsetMoveCase movePlaneLoop1: CMPAH,BytePtrCS:[BX] JEToCase ADDBX,4 LOOPmovePlaneLoop1 ToCase: JMPWordPtrCS:[BX+2] MoveItemsDW4 MoveCaseDW75,Case1,72,Case2,77,Case3,80,Case4,0,Default Default:JMPEndSwitch Case1:SUBPOS_X,5 JMPEndSwitch Case2: SUBPOS_Y,5 JMPEndSwitch Case3: ADDPOS_X,5 JMPEndSwitch Case4:ADDPOS_Y,5 EndSwitch: ;drawanewplaninnewposition MOVCX,N1 PUSHCX MOVCX,OffsetPLANEMAP PUSHCX MOVCX,0001h PUSHCX PUSHPOS_Y PUSHPOS_X CALLdrawCraft ADDSP,10 EndMovePlane: POPCX POPBX POPAX MOVSP,BP POPBP RET movePlaneENDP
有了飞机,还要发射炮弹啊。我所期望的飞机,应该是可以多连发的,而且是无限发!那玩起来才爽。于是我设计了这样的数组。
MISSILEDW512DUP('$$') MISSILESNUMDW0
这是什么意思呢?我们一步一步来看。首先这是存储每次发射炮弹,炮弹所在的位置(横坐标和纵坐标)。
每当玩家按下空格键,就向数组里添加位置信息。这是为了方便后面让导弹一起上升。
在没有键盘输入的时候。我们想让导弹自己上升。为了做到这点,我们需要遍历这个MISSILE数组,每找到一个位置信息,就读出来,删除它,然后更新数组位置信息(只需要更新纵坐标),然后再在新的位置上画一个导弹就行了。
以下是具体实现代码
Comment/*********** function: parameters:horizontalposition verticalposition return:rectify'MISSILE'and'MISSILESNUM' description:Whenpressthespacekey,thisprogramwillput thepositionintothe'MISSILE'array. Thencall'drawMissile'todisplayit. **********/ fireMissilePROCNEAR PUSHBP MOVBP,SP PUSHCX PUSHDX PUSHSI PUSHDI MOVCX,[BP+4] MOVDX,[BP+6] SUBDX,5 MOVSI,OffsetMISSILE fireMissileLoop: CMPWordPtr[SI],'$$' JZfireMissileIf ADDSI,4 JMPfireMissileLoop fireMissileIf: MOV[SI],CX MOV[SI+2],DX MOVDI,N2 PUSHDI MOVDI,OffsetMISSILEMAP PUSHDI MOVDI,0003h PUSHDI PUSHDX PUSHCX CALLdrawCraft ADDSP,10 INCMISSILESNUM POPDI POPSI POPDX POPCX MOVSP,BP POPBP RET fireMissileENDP Comment/*********** function:risealltheexistingmissiles parameters:void return:rectifyMISSILEandMISSILESNUM description:Whenthereisnoinputevent,thisprogramwill risealltheexistingmissileswhichstoredin the'MISSILE'arrayunlessthereisnomissile. **********/ riseMissilePROCNEAR PUSHBP MOVBP,SP PUSHSI PUSHCX PUSHDX MOVSI,OffsetMISSILE MOVCX,256 riseMissileLoop: CMPWordPtr[SI],'$$' JZriseMissileIf MOVDX,N2 PUSHDX MOVDX,OffsetMISSILEMAP PUSHDX MOVDX,0000h PUSHDX MOVDX,WordPtr[SI+2] PUSHDX MOVDX,WordPtr[SI] PUSHDX CALLdrawCraft ADDSP,10 SUBWordPtr[SI+2],2 JLEriseMissileIf2 MOVDX,N2 PUSHDX MOVDX,OffsetMISSILEMAP PUSHDX MOVDX,0003h PUSHDX MOVDX,WordPtr[SI+2] PUSHDX MOVDX,WordPtr[SI] PUSHDX CALLdrawCraft ADDSP,10 JMPriseMissileIf riseMissileIf2: MOVWordPtr[SI],'$$' MOVWordPtr[SI+2],'$$' DECMISSILESNUM riseMissileIf: ADDSI,4 LOOPriseMissileLoop POPDX POPCX POPSI POPAX MOVSP,BP POPBP RET riseMissileENDP
以上做的都是具体的例程。做到这里,回过头看一下我们主程序的实现。
整个游戏是依靠主程序调用一个个例程来运作的。
Start: MOVAX,_DATA MOVDS,AX CLI MOVAX,_STACK MOVSS,AX MOVSP,OffsetTOS STI CALLinit MOVAH,00h MOVAL,04h INT10h MOVCX,N1 PUSHCX MOVSI,OffsetPLANEMAP PUSHSI MOVCX,0001h PUSHCX PUSHPOS_Y PUSHPOS_X CALLdrawCraft ADDSP,10 Again: MOVAH,01h INT16h Next: JZProcess MOVAH,00h INT16h CMPAL,27 JZEndMain CMPAL,'' JZShoot PUSHAX PUSHPOS_Y PUSHPOS_X CALLmovePlane ADDSP,6 JMPAgain Shoot: PUSHPOS_Y PUSHPOS_X CALLfireMissile ADDSP,4 JMPAgain Process: CALLshowScoreByDemical CALLcheckCollision INCTIMER MOVDX,DIFFICULTY CMPTIMER,DX JBELoc1 CALLdropEnemy MOVDX,MAX SUBDX,TIMER MOVTIMER,0 CMPENEMYNUM,DX JALoc1 CALLgenerateEnemy Loc1: CMPMISSILESNUM,0 JZAgain CALLriseMissile JMPAgain
解释一下:Again开始是主程序。先判断有没有键盘输入,如果有,判断是不是ESC(退出),然后再判断是不是空格(攻击),如果都不是,就执行movePlane,在movePlane中实现具体的移动过程。
如果没有键盘输入,就执行Process后面的代码。这段代码稍后解释。
有了飞机,有了导弹,还需要有敌人啊。敌人的制作过程和导弹类似。也建立一个ENEMY数组来存储每一个敌人的位置信息,并在没有键盘输入的时候进行更新。只要注意导弹是上升,敌人是下降。
之后是检测碰撞,我是用二重循环遍历了MISSILE和ENEMY两个数组。一开始我写的是严格相等才算碰撞,后来玩的时候发现这条件太苛刻了,于是改成了在一定范围内就行。
Comment/*********** function:checkifthereisanycollision parameters:void return:void description:checkverticalposandhorizontalpos,ifbothare equel,deleteoneofthem. **********/ checkCollisionPROCNEAR PUSHBP MOVBP,SP PUSHAX PUSHBX PUSHCX PUSHDX PUSHSI PUSHDI MOVSI,OffsetENEMY MOVDI,OffsetMISSILE MOVAX,0 MOVBX,0 checkCollisionLoop1: CMPWordPtr[SI],'$$' JZcheckCollisionIf MOVCX,POS_X SUBCX,WordPtr[SI] JGEcheckCollisionLoc1 NEGCX checkCollisionLoc1: CMPCX,8 JAcheckCollisionLoop2 MOVDX,POS_Y SUBDX,WordPtr[SI+2] JGEcheckCollisionLoc2 NEGDX checkCollisionLoc2: CMPDX,1 JAcheckCollisionLoop2 ;gameover PUTSGAMEOVER CALLdelay MOVAX,4C00h INT21h checkCollisionLoop2: CMPWordPtr[DI],'$$' JZcheckCollisionIf3 MOVCX,WordPtr[SI] SUBCX,WordPtr[DI] JGEcheckCollisionLoc3 NEGCX checkCollisionLoc3: CMPCX,5 JAcheckCollisionIf3 MOVDX,WordPtr[SI+2] SUBDX,WordPtr[DI+2] JGEcheckCollisionLoc4 NEGDX checkCollisionLoc4: CMPDX,5 JAcheckCollisionIf3 JMPdeleteEnemy checkCollisionIf3: INCBX ADDDI,4 CMPBX,256 JBcheckCollisionLoop2 checkCollisionIf: ADDSI,4 MOVBX,0 INCAX CMPAX,256 MOVDI,OffsetMISSILE JBcheckCollisionLoop1 JMPcheckCollisionEnd deleteEnemy: MOVDX,N3 PUSHDX MOVDX,OffsetENEMYMAP PUSHDX MOVDX,0000h PUSHDX MOVDX,WordPtr[SI+2] PUSHDX MOVDX,WordPtr[SI] PUSHDX CALLdrawCraft ADDSP,10 MOVWordPtr[SI],'$$' MOVWordPtr[SI+2],'$$' DECENEMYNUM ADDSCORE,2 JMPcheckCollisionIf3 checkCollisionEnd: POPDI POPSI POPDX POPCX POPBX POPAX MOVSP,BP POPBP RET checkCollisionENDP
到这里,主要的例程都已经结束了。
再回过头看一下主程序的代码。
Again: MOVAH,01h INT16h Next: JZProcess MOVAH,00h INT16h CMPAL,27 JZEndMain CMPAL,'' JZShoot PUSHAX PUSHPOS_Y PUSHPOS_X CALLmovePlane ADDSP,6 JMPAgain Shoot: PUSHPOS_Y PUSHPOS_X CALLfireMissile ADDSP,4 JMPAgain Process: CALLshowScoreByDemical CALLcheckCollision INCTIMER MOVDX,DIFFICULTY CMPTIMER,DX JBELoc1 CALLdropEnemy MOVDX,MAX SUBDX,TIMER MOVTIMER,0 CMPENEMYNUM,DX JALoc1 CALLgenerateEnemy Loc1: CMPMISSILESNUM,0 JZAgain CALLriseMissile JMPAgain
从Process开始,首先调用的是显示分数的例程(比较容易我没放上来),然后先检测是否碰撞。这里设置了一个DIFFICULTY变量,是适应不同难度(DIFFICULTY)。TIMER是用来定时的。每次都+1,加到DIFFICLUTY的值才执行敌人下降的例程。
难度大的话,敌人移动速度快,只需要把DIFFICULTY的值设小一点,就可以更快的使敌人下降;反之亦然。
附上一些效果图。
附上源码:https://github.com/zhongyuchen/hitplanegame
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。