純匯編實現(xiàn)打飛機小游戲的示例代碼
匯編暑假作業(yè)要求做一個大項目,題目可以自擬。我思來想去,還是覺得做一個小游戲比較有意思。最后選擇了做打飛機游戲。
這里采用的是VGA模式320x200 4色。
打飛機游戲的游戲邏輯比較簡單。首先,飛機可以移動,也可以發(fā)射炮彈;其次,會有敵人不斷地從前方飛過來,如果撞上飛機游戲結(jié)束;最后,飛機發(fā)射的炮彈可以擊落敵人。
既然是打飛機,我們就必須首先造一臺飛機,代碼如下:
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, [BP+4]
MOV DX, [BP+6]
MOV SI, [BP+8]
MOV AL, Byte Ptr [BP+10]
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, [BP+12]
MOV [BP-8],AX
MOV SI, [BP+10]
MOV AX, [BP+8]
MOV [BP-6], AX
MOV AX, [BP+6]
MOV [BP-4], AX
MOV AX, [BP+4]
MOV [BP-2], AX
drawCraftLoop:
PUSH Word Ptr [BP-6]
MOV DX, Word Ptr [SI]
PUSH DX
PUSH Word Ptr [BP-4]
MOV AX, [BP-2]
SHR DX, 1
SUB AX, DX
PUSH AX
CALL drawALine
ADD SP, 8
ADD Word Ptr [BP-4], 1
ADD SI, 2
INC DI
CMP DI, [BP-8]
JB drawCraftLoop
POP DI
POP SI
POP DX
POP AX
MOV SP, BP
POP BP
RET
drawCraft ENDP
PLANEMAP DW 1,1,3,3,3,3,3,3,7,14,16,14,6,2,2,6,6
N1 EQU ($-PLANEMAP)/2
這里之所以做的這么復雜是出于代碼重用的考慮,相同的代碼只要輸入不同的參數(shù),就能制造出不一樣的東西。這是抽象的思想。
drawCraft 根據(jù)輸入的不同的數(shù)組,可以繪制出不同的東西,比如飛機,導彈,敵人。而不必畫飛機一個函數(shù),畫導彈又是另一個函數(shù)了。
drawCraft主要是一條線一條線地畫,就像3D打印一樣。不過這里是2D的版本。
光畫了飛機不行,我們還需要讓它動起來。動起來的方法很簡單,只需要用黑色覆蓋原圖,然后再在新的位置上建立一個新的即可:
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, [BP+6] PUSH AX MOV AX, [BP+4] PUSH AX CALL drawCraft ADD SP, 10 MOV CX, CS:MoveItems MOV AH, Byte Ptr [BP+9] MOV BX, Offset MoveCase movePlaneLoop1: CMP AH, Byte Ptr CS:[BX] JE ToCase ADD BX, 4 LOOP movePlaneLoop1 ToCase: JMP Word Ptr CS: [BX+2] 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
有了飛機,還要發(fā)射炮彈啊。我所期望的飛機,應該是可以多連發(fā)的,而且是無限發(fā)!那玩起來才爽。于是我設計了這樣的數(shù)組。
MISSILE DW 512 DUP('$$')
MISSILESNUM DW 0
這是什么意思呢?我們一步一步來看。首先這是存儲每次發(fā)射炮彈,炮彈所在的位置(橫坐標和縱坐標)。
每當玩家按下空格鍵,就向數(shù)組里添加位置信息。這是為了方便后面讓導彈一起上升。
在沒有鍵盤輸入的時候。我們想讓導彈自己上升。為了做到這點,我們需要遍歷這個MISSILE數(shù)組,每找到一個位置信息,就讀出來,刪除它,然后更新數(shù)組位置信息(只需要更新縱坐標),然后再在新的位置上畫一個導彈就行了。
以下是具體實現(xiàn)代碼
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, [BP+4]
MOV DX, [BP+6]
SUB DX, 5
MOV SI, Offset MISSILE
fireMissileLoop:
CMP Word Ptr [SI], '$$'
JZ fireMissileIf
ADD SI, 4
JMP fireMissileLoop
fireMissileIf:
MOV [SI], CX
MOV [SI+2], 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 [SI], '$$'
JZ riseMissileIf
MOV DX, N2
PUSH DX
MOV DX, Offset MISSILEMAP
PUSH DX
MOV DX, 0000h
PUSH DX
MOV DX, Word Ptr [SI+2]
PUSH DX
MOV DX, Word Ptr [SI]
PUSH DX
CALL drawCraft
ADD SP, 10
SUB Word Ptr [SI+2],2
JLE riseMissileIf2
MOV DX, N2
PUSH DX
MOV DX, Offset MISSILEMAP
PUSH DX
MOV DX, 0003h
PUSH DX
MOV DX, Word Ptr [SI+2]
PUSH DX
MOV DX, Word Ptr [SI]
PUSH DX
CALL drawCraft
ADD SP, 10
JMP riseMissileIf
riseMissileIf2:
MOV Word Ptr [SI], '$$'
MOV Word Ptr [SI+2], '$$'
DEC MISSILESNUM
riseMissileIf:
ADD SI, 4
LOOP riseMissileLoop
POP DX
POP CX
POP SI
POP AX
MOV SP, BP
POP BP
RET
riseMissile ENDP
以上做的都是具體的例程。做到這里,回過頭看一下我們主程序的實現(xiàn)。
整個游戲是依靠主程序調(diào)用一個個例程來運作的。
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
解釋一下:Again開始是主程序。先判斷有沒有鍵盤輸入,如果有,判斷是不是ESC(退出),然后再判斷是不是空格(攻擊),如果都不是,就執(zhí)行 movePlane,在movePlane中實現(xiàn)具體的移動過程。
如果沒有鍵盤輸入,就執(zhí)行Process后面的代碼。這段代碼稍后解釋。
有了飛機,有了導彈,還需要有敵人啊。敵人的制作過程和導彈類似。也建立一個ENEMY數(shù)組來存儲每一個敵人的位置信息,并在沒有鍵盤輸入的時候進行更新。只要注意導彈是上升,敵人是下降。
之后是檢測碰撞,我是用二重循環(huán)遍歷了MISSILE 和 ENEMY兩個數(shù)組。一開始我寫的是嚴格相等才算碰撞,后來玩的時候發(fā)現(xiàn)這條件太苛刻了,于是改成了在一定范圍內(nèi)就行。
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 [SI], '$$' JZ checkCollisionIf MOV CX, POS_X SUB CX, Word Ptr[SI] JGE checkCollisionLoc1 NEG CX checkCollisionLoc1: CMP CX, 8 JA checkCollisionLoop2 MOV DX, POS_Y SUB DX, Word Ptr[SI+2] 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 [DI], '$$' JZ checkCollisionIf3 MOV CX, Word Ptr[SI] SUB CX, Word Ptr[DI] JGE checkCollisionLoc3 NEG CX checkCollisionLoc3: CMP CX, 5 JA checkCollisionIf3 MOV DX, Word Ptr[SI+2] SUB DX, Word Ptr[DI+2] 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 [SI+2] PUSH DX MOV DX, Word Ptr [SI] PUSH DX CALL drawCraft ADD SP, 10 MOV Word Ptr [SI], '$$' MOV Word Ptr [SI+2], '$$' 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
到這里,主要的例程都已經(jīng)結(jié)束了。
再回過頭看一下主程序的代碼。
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
從Process開始,首先調(diào)用的是顯示分數(shù)的例程(比較容易我沒放上來),然后先檢測是否碰撞。這里設置了一個DIFFICULTY變量,是適應不同難度(DIFFICULTY)。TIMER是用來定時的。每次都+1,加到DIFFICLUTY的值才執(zhí)行敵人下降的例程。
難度大的話,敵人移動速度快,只需要把DIFFICULTY的值設小一點,就可以更快的使敵人下降;反之亦然。
附上一些效果圖。





附上源碼:https://github.com/zhongyuchen/hitplanegame
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
匯編語言中的函數(shù)調(diào)用參數(shù)傳遞及全局與局部變量與“基址”
這篇文章主要介紹了匯編眼中的函數(shù)調(diào)用參數(shù)傳遞以及全局與局部變量與“基址”,本文通過圖文并茂的形式給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02

