追着双标黑子胖揍 發表於 2020-1-17 10:44:32

纯汇编实现打飞机小游戏的示例代码

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"></ul></div><p>汇编暑假作业要求做一个大项目,题目可以自拟。我思来想去,还是觉得做一个小游戏比较有意思。最后选择了做打飞机游戏。</p>
<p>这里采用的是VGA模式320x200 4色。</p>
<p>打飞机游戏的游戏逻辑比较简单。首先,飞机可以移动,也可以发射炮弹;其次,会有敌人不断地从前方飞过来,如果撞上飞机游戏结束;最后,飞机发射的炮弹可以击落敌人。</p>
<p>既然是打飞机,我们就必须首先造一台飞机,代码如下:</p>
<div class="jb51code">
<pre class="brush:plain;">
Comment/***********
       function:draw a horizontal line
       parameters: horizontal position
             vertical position
                              length of the line
                              color
       return:   void
       description:draw some points horizontally.
                              color '0001h' represents drawing a line while
                              color '0000h' which is black means erase the line.
**********/
drawALine PROC NEAR
    PUSH BP
    MOV BP, SP
    PUSH AX
    PUSH CX
    PUSH DX
    PUSH SI
           
    MOV AH, 0Ch
    MOV CX,
    MOV DX,
    MOV SI,
    MOV AL, Byte Ptr
drawALineLoop:
          INT 10h
          INC CX
          DEC SI
          JNZ drawALineLoop
   
    POP SI
    POP DX
    POP CX
    POP AX
    MOV SP, BP
    POP BP
    RET
drawALine ENDP


Comment/***********
       function:draw a plane or a missile
       parameters: horizontal position
             vertical position
             color
             type (plane or missile or enemy)
             map length
       return:   void
       description:call "drawALine" function repeatedly.
**********/
drawCraft PROC NEAR
          
          PUSH BP
          MOV BP, SP
          SUB SP, 8
          
          PUSH AX
          PUSH DX
          PUSH SI
          PUSH DI
          MOV DI, 0
          
          MOV AX,
          MOV ,AX
          MOV SI,
          MOV AX,
          MOV , AX
          MOV AX,
          MOV , AX
          MOV AX,
          MOV , AX
drawCraftLoop:
                PUSH Word Ptr
          MOV DX, Word Ptr
          PUSH DX          
          PUSH Word Ptr
          MOV AX,
          SHR DX, 1
          SUB AX, DX
          PUSH AX
          CALL drawALine
          ADD SP, 8
          ADD Word Ptr , 1
          ADD SI, 2
          INC DI
          CMP DI,
          JB drawCraftLoop
          
          POP DI
          POP SI
          POP DX
          POP AX
          MOV SP, BP
          POP BP
                RET
drawCraft ENDP</pre>
</div>
<p>PLANEMAP DW 1,1,3,3,3,3,3,3,7,14,16,14,6,2,2,6,6 <br />
N1 EQU ($-PLANEMAP)/2</p>
<p>这里之所以做的这么复杂是出于代码重用的考虑,相同的代码只要输入不同的参数,就能制造出不一样的东西。这是抽象的思想。</p>
<p>drawCraft 根据输入的不同的数组,可以绘制出不同的东西,比如飞机,导弹,敌人。而不必画飞机一个函数,画导弹又是另一个函数了。</p>
<p>drawCraft主要是一条线一条线地画,就像3D打印一样。不过这里是2D的版本。</p>
<p>光画了飞机不行,我们还需要让它动起来。动起来的方法很简单,只需要用黑色覆盖原图,然后再在新的位置上建立一个新的即可:</p>
<div class="jb51code">
<pre class="brush:plain;">
Comment/***********
       function:move a plane
       parameters: original horizontal position
             original vertical position
                              direction(left-a up-w right-d bottom-s)
       return:   rectify POS_X, POS_Y
       description:destory the original and then create a new one                              
**********/
movePlane PROC NEAR
               PUSH BP
               MOV BP, SP
               
               PUSH AX
               PUSH BX
               PUSH CX
               
               MOV AX,N1
               PUSH AX
               MOV AX, Offset PLANEMAP
               PUSH AX
               MOV AX, 0000h
               PUSH AX ;black color
               MOV AX,
               PUSH AX
               MOV AX,
               PUSH AX
               CALL drawCraft
               ADD SP, 10
               
               MOV CX, CS:MoveItems
               MOV AH, Byte Ptr
               MOV BX, Offset MoveCase
movePlaneLoop1:               
               CMP AH, Byte Ptr CS:
               JE ToCase
               ADD BX, 4
               LOOP movePlaneLoop1
               
ToCase:       JMP Word Ptr CS:
MoveItems DW 4
MoveCase DW 75,Case1,72,Case2,77,Case3,80,Case4, 0, Default
Default: JMP EndSwitch
Case1:SUB POS_X, 5
               JMP EndSwitch
Case2:       SUB POS_Y, 5
               JMP EndSwitch
Case3:       ADD POS_X, 5
               JMP EndSwitch
Case4:ADD POS_Y, 5
       
EndSwitch:
;draw a new plan in new position
               MOV CX, N1
               PUSH CX
               MOV CX, Offset PLANEMAP
               PUSH CX
               MOV CX, 0001h
               PUSH CX
           PUSH POS_Y
           PUSH POS_X
           CALL drawCraft
               ADD SP, 10
EndMovePlane:

               POP CX
               POP BX
               POP AX
               MOV SP, BP
               POP BP
               RET
movePlane ENDP</pre>
</div>
<p>有了飞机,还要发射炮弹啊。我所期望的飞机,应该是可以多连发的,而且是无限发!那玩起来才爽。于是我设计了这样的数组。</p>
<div class="jb51code">
<pre class="brush:plain;">
MISSILEDW 512 DUP('$$')
MISSILESNUM DW 0 </pre>
</div>
<p>这是什么意思呢?我们一步一步来看。首先这是存储每次发射炮弹,炮弹所在的位置(横坐标和纵坐标)。</p>
<p>每当玩家按下空格键,就向数组里添加位置信息。这是为了方便后面让导弹一起上升。</p>
<p>在没有键盘输入的时候。我们想让导弹自己上升。为了做到这点,我们需要遍历这个MISSILE数组,每找到一个位置信息,就读出来,删除它,然后更新数组位置信息(只需要更新纵坐标),然后再在新的位置上画一个导弹就行了。</p>
<p>以下是具体实现代码</p>
<div class="jb51code">
<pre class="brush:plain;">
Comment/***********
       function:
       parameters: horizontal position
             vertical position
       return:   rectify 'MISSILE' and 'MISSILESNUM'
       description:When press the space key, this program will put
             the position into the 'MISSILE' array.
             Then call 'drawMissile' to display it.        
**********/
fireMissile PROC NEAR       
               PUSH BP
           MOV BP, SP
          
           PUSH CX
           PUSH DX
           PUSH SI
           PUSH DI
          
           MOV CX,
           MOV DX,
           SUB DX, 5
               MOV SI, Offset MISSILE
fireMissileLoop:
               CMP Word Ptr , '$$'
               JZ fireMissileIf
               ADD SI, 4
               JMP fireMissileLoop
fireMissileIf:
   MOV , CX
   MOV , DX
   
   MOV DI, N2
   PUSH DI
   MOV DI, Offset MISSILEMAP
   PUSH DI
   MOV DI, 0003h
   PUSH DI
   PUSH DX
   PUSH CX
   CALL drawCraft
   ADD SP, 10
               
               INC MISSILESNUM
               POP DI
           POP SI
           POP DX
           POP CX
           MOV SP, BP
           POP BP
           RET
fireMissile ENDP


Comment/***********
       function:rise all the existing missiles
       parameters: void
       return:   rectify MISSILE and MISSILESNUM
       description:When there is no input event, this program will
                              rise all the existing missiles which stored in
                              the 'MISSILE' array unless there is no missile.
**********/
riseMissile PROC NEAR
               PUSH BP
               MOV BP, SP
               
               PUSH SI
               PUSH CX
               PUSH DX
                          
               MOV SI, Offset MISSILE
               MOV CX, 256
riseMissileLoop:               
               CMP Word Ptr , '$$'
               JZ riseMissileIf
               
               MOV DX, N2
               PUSH DX
               MOV DX, Offset MISSILEMAP
               PUSH DX
               MOV DX, 0000h
               PUSH DX
               MOV DX, Word Ptr
               PUSH DX
               MOV DX, Word Ptr
               PUSH DX
               CALL drawCraft
               ADD SP, 10
               
               SUB Word Ptr ,2
               JLE riseMissileIf2
               
               MOV DX, N2
               PUSH DX
               MOV DX, Offset MISSILEMAP
               PUSH DX
               MOV DX, 0003h
               PUSH DX
               MOV DX, Word Ptr
               PUSH DX
               MOV DX, Word Ptr
               PUSH DX
               CALL drawCraft
               ADD SP, 10
               
               JMP riseMissileIf
riseMissileIf2:
               MOV Word Ptr , '$$'
               MOV Word Ptr , '$$'
               DEC MISSILESNUM       
riseMissileIf:       
               ADD SI, 4
               LOOP riseMissileLoop
               
               POP DX
               POP CX
               POP SI
               POP AX       
                  
               MOV SP, BP
               POP BP
               RET
riseMissile ENDP</pre>
</div>
<p>以上做的都是具体的例程。做到这里,回过头看一下我们主程序的实现。</p>
<p>整个游戏是依靠主程序调用一个个例程来运作的。</p>
<div class="jb51code">
<pre class="brush:plain;">
Start:
          MOV AX, _DATA
          MOV DS, AX
          CLI
          MOV AX, _STACK
          MOV SS, AX
          MOV SP, Offset TOS
          STI
          
          CALL init
          
          MOV AH, 00h
          MOV AL, 04h
          INT 10h
          MOV CX, N1
          PUSH CX
          MOV SI, Offset PLANEMAP
          PUSH SI
          MOV CX, 0001h
          PUSH CX
          PUSH POS_Y
          PUSH POS_X
          CALL drawCraft
          ADD SP, 10
Again:          
          MOV AH,01h
          INT 16h
Next:
          JZ Process
          MOV AH, 00h
          INT 16h
          CMP AL, 27
          JZ EndMain
          CMP AL, ' '
          JZ Shoot
          PUSH AX
          PUSH POS_Y
          PUSH POS_X
          CALL movePlane
          ADD SP, 6
          JMP Again
Shoot:
          PUSH POS_Y
          PUSH POS_X
          CALL fireMissile
          ADD SP, 4
          JMP Again
Process:
          CALL showScoreByDemical
          CALL checkCollision
          INC TIMER
          MOV DX, DIFFICULTY
          CMP TIMER, DX
          JBE Loc1
          CALL dropEnemy
          MOV DX, MAX
          SUB DX, TIMER
          MOV TIMER, 0
          CMP ENEMYNUM, DX
          JA Loc1
          CALL generateEnemy
Loc1:
          CMP MISSILESNUM, 0
          JZ Again
          CALL riseMissile
          JMP Again</pre>
</div>
<p>解释一下:Again开始是主程序。先判断有没有键盘输入,如果有,判断是不是ESC(退出),然后再判断是不是空格(攻击),如果都不是,就执行 movePlane,在movePlane中实现具体的移动过程。</p>
<p>如果没有键盘输入,就执行Process后面的代码。这段代码稍后解释。</p>
<p>有了飞机,有了导弹,还需要有敌人啊。敌人的制作过程和导弹类似。也建立一个ENEMY数组来存储每一个敌人的位置信息,并在没有键盘输入的时候进行更新。只要注意导弹是上升,敌人是下降。</p>
<p>之后是检测碰撞,我是用二重循环遍历了MISSILE 和 ENEMY两个数组。一开始我写的是严格相等才算碰撞,后来玩的时候发现这条件太苛刻了,于是改成了在一定范围内就行。</p>
<div class="jb51code">
<pre class="brush:plain;">
Comment/***********
       function:check if there is any collision
       parameters: void
       return:   void
       description:check vertical pos and horizontal pos, if both are
             equel, delete one of them.
**********/
checkCollision PROC NEAR       
               PUSH BP
               MOV BP, SP       

               PUSH AX
               PUSH BX
               PUSH CX
               PUSH DX
               PUSH SI
               PUSH DI
               
               MOV SI, Offset ENEMY
               MOV DI, Offset MISSILE
               MOV AX, 0
               MOV BX, 0
checkCollisionLoop1:
               CMP Word Ptr , '$$'
               JZ checkCollisionIf
               
               MOV CX, POS_X
               SUB CX, Word Ptr
               JGE checkCollisionLoc1
               NEG CX
checkCollisionLoc1:
               CMP CX, 8
               JA checkCollisionLoop2
                      
               MOV DX, POS_Y
               SUB DX, Word Ptr
               JGE checkCollisionLoc2
               NEG DX
checkCollisionLoc2:
               CMP DX, 1
               JA checkCollisionLoop2
               
               ;game over
               PUTS GAMEOVER
               CALL delay
               MOV AX, 4C00h
               INT 21h
                  
        checkCollisionLoop2:       
               CMP Word Ptr , '$$'
               JZ checkCollisionIf3
               
               MOV CX, Word Ptr
               SUB CX, Word Ptr
               JGE checkCollisionLoc3
               NEG CX
        checkCollisionLoc3:
               CMP CX, 5
               JA checkCollisionIf3
               
               MOV DX, Word Ptr
               SUB DX, Word Ptr
               JGE checkCollisionLoc4
               NEG DX
        checkCollisionLoc4:
               CMP DX, 5
               JA checkCollisionIf3
               
               JMP deleteEnemy
        checkCollisionIf3:       
               INC BX
               ADD DI, 4
               CMP BX, 256
               JB checkCollisionLoop2
       
checkCollisionIf:
               ADD SI, 4
               MOV BX, 0
               INC AX
               CMP AX, 256
               MOV DI, Offset MISSILE
               JB checkCollisionLoop1
               JMP checkCollisionEnd
               
deleteEnemy:               
               MOV DX, N3
               PUSH DX
               MOV DX, Offset ENEMYMAP
               PUSH DX
               MOV DX, 0000h
               PUSH DX
               MOV DX, Word Ptr
               PUSH DX
               MOV DX, Word Ptr
               PUSH DX
               CALL drawCraft
               ADD SP, 10
               MOV Word Ptr , '$$'
               MOV Word Ptr , '$$'
               DEC ENEMYNUM
               ADD SCORE, 2
       
               JMP checkCollisionIf3
               
checkCollisionEnd:
                  
               POP DI
               POP SI
               POP DX
               POP CX
               POP BX
               POP AX
               MOV SP, BP       
               POP BP       
               RET
checkCollision ENDP</pre>
</div>
<p>到这里,主要的例程都已经结束了。</p>
<p>再回过头看一下主程序的代码。</p>
<div class="jb51code">
<pre class="brush:plain;">
Again:          
          MOV AH,01h
          INT 16h
Next:
          JZ Process
          MOV AH, 00h
          INT 16h
          CMP AL, 27
          JZ EndMain
          CMP AL, ' '
          JZ Shoot
          PUSH AX
          PUSH POS_Y
          PUSH POS_X
          CALL movePlane
          ADD SP, 6
          JMP Again
Shoot:
          PUSH POS_Y
          PUSH POS_X
          CALL fireMissile
          ADD SP, 4
          JMP Again
Process:
          CALL showScoreByDemical
          CALL checkCollision
          INC TIMER
          MOV DX, DIFFICULTY
          CMP TIMER, DX
          JBE Loc1
          CALL dropEnemy
          MOV DX, MAX
          SUB DX, TIMER
          MOV TIMER, 0
          CMP ENEMYNUM, DX
          JA Loc1
          CALL generateEnemy
Loc1:
          CMP MISSILESNUM, 0
          JZ Again
          CALL riseMissile
          JMP Again</pre>
</div>
<p>从Process开始,首先调用的是显示分数的例程(比较容易我没放上来),然后先检测是否碰撞。这里设置了一个DIFFICULTY变量,是适应不同难度(DIFFICULTY)。TIMER是用来定时的。每次都+1,加到DIFFICLUTY的值才执行敌人下降的例程。</p>
<p>难度大的话,敌人移动速度快,只需要把DIFFICULTY的值设小一点,就可以更快的使敌人下降;反之亦然。</p>
<p>附上一些效果图。</p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202001/202001171040571.png" /></p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202001/202001171040572.png" /></p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202001/202001171040573.png" /></p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202001/202001171040574.png" /></p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202001/202001171040575.png" /></p>
<p>附上源码:https://github.com/zhongyuchen/hitplanegame<br />
</p>
<p>以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持琼殿技术社区。</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>Java实现打飞机小游戏(附完整源码)</li><li>JavaScript用200行代码制作打飞机小游戏实例</li><li>纯javascript模仿微信打飞机小游戏</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: 纯汇编实现打飞机小游戏的示例代码