【組合語言】純組合語言編寫打飛機小遊戲
上學期組合語言的期末考核可選的一項是用純組合語言編寫一個小遊戲,喜歡動手的我當然首選這個了。小遊戲當中用來練手的,一般有貪吃蛇、俄羅斯方塊、打飛機之類的咯,於是我選擇了其中一個——打飛機!咳咳,選擇它可並不是因為個人某種特殊喜好,我想大多數男同胞都會傾向於玩這款遊戲@。@
接下來我就跟大家分享一下如何一步步的完成這項工作,當然,我並不打算把原始碼都貼上來,我主要是為大家提供一個思路,必要時提供部分程式碼~
首先說明下,本例子的執行環境是windows 32位系統,編譯工具是masm6.15。
先上個效果圖鎮樓!
寫這個遊戲,我大概用了半天的時間查詢、學習相關資料,一天的時間進行編碼和完善,半天的時間進行除錯。
俗話說,萬事開頭難,想要開始寫,卻真的不知道從何下手!既然是打飛機遊戲,首先要有飛機吧!所以,我們的第一步就是畫出一架飛機!
為獲得比較好的畫質,本次主遊戲頁面顯示採用了320*200彩色圖形顯示模式,通過畫素點來繪畫飛機。
mov ah,00H ;設定顯示方式為320*200彩色圖形方式
mov al,04H
int 10H
接下來,我們就畫出一架飛機來,由於我用的事圖形模式,飛機實際上是由一個點一個點畫出來的,為簡化程式,我們先寫一個畫水平線的函式:
;畫水平直線 ;入口引數 CX相當於X0 DX相當於Y0,Y1 si影象長度 BL畫素 sp_line proc pusH ax pusH bx MOV BL,2 ;飛機的顏色 MOV AH,0cH MOV AL,BL lop: INT 10H inc CX dec si jnz lop pop bx pop ax ret sp_line endp
由點及線之後,便可以由線及面了,通過長短不同的線條的疊加,即可畫出一架飛機來:
至此,飛機就畫出來啦!那下一步要做什麼呢?有了一架飛機,最好的體驗當然是坐到駕駛座上把它開走咯~;////////////////////////////////////// ;//畫玩家飛機子程式 傳入引數bx設定飛機的水平位置 BP設定飛機的垂直位置 BX,BP記錄飛機的位置 play_plane proc push cx push dx push es push si push di push ax jmp sk play_plane_1: dw 6,1,1,5,2,3,5,3,3,5,4,3,4,5,5,3,6,7,1,7,11,1,8,11,4,9,5,5,10,3,4,11,5,3,12,7,4,13,2,7,13,2 ;X0,Y,長度 sk: mov cx,ax mov ax,cs mov es,ax mov di,0 lop2: mov cx,word ptr es:[play_plane_1+di] ;x0 add cx,bx mov dx,word ptr es:[play_plane_1+di+2] ;y add dx,bp mov si,word ptr es:[play_plane_1+di+4] ;長度 call sp_line add di,6 cmp di,84 jne lop2 ;plane_pos用於記錄飛機的位置,此處更新飛機位置 mov ds:[plane_pos],bx mov ds:[plane_pos+2],bp pop ax pop di pop si pop es pop dx pop cx ret play_plane endp
什麼?不會開飛機?沒關係,接下來我就教你怎麼開飛機!不過,沒有駕駛證的小朋友,不要隨便試哦~
首先,當然是坐到駕駛座上咯~然後呢,綁好安全帶……
哈哈,接下來要啟動引擎啦~想讓飛機移動,先想想移動是什麼概念,比如你現在向前走一步,是不是在原來位置的你不見了,而在當前位置出現了一個一模一樣的你~
同樣的,要讓飛機移動,我們要做的事情是:把原來位置的飛機擦除,然後在新位置畫上一架一模一樣的飛機!由於我們的遊戲背景是黑色的,所以實際上擦除飛機,等於在該位置畫一架黑色的飛機將其覆蓋掉即可:
;畫水平直線
;入口引數 CX相當於X0 DX相當於Y0,Y1 si影象長度 BL畫素
sp_line1 proc
pusH ax
pusH bx
pusH bp
pusH di
MOV bp,CX
MOV di,11
MOV BL,0 ;飛機的顏色 用來擦除原來的飛機
MOV AH,0cH
MOV AL,BL
lop1: INT 10H
inc CX
dec di
jnz lop1
MOV CX,bp
pop di
pop bp
pop bx
pop ax
ret
sp_line1 endp
play_plane1 proc ;擦除飛機軌跡子程式 傳入引數CX,DX
push si
push di
inc cx
mov si,13
mov di,0
lop5: inc di
inc dx
call sp_line1
cmp di,14
jne lop5
pop di
pop si
ret
play_plane1 endp
有了上面的程式之後,你只需要在測試程式中加入控制語句,當鍵盤輸入←時,呼叫play_plane1函式擦除飛機,然後飛機的橫座標減1,再呼叫play_plane畫上新的飛機,就能看到飛機移動的效果啦~
怎麼樣,這飛機開起來是不是很爽?但是有些朋友不樂意了,咱們可是想要打造一架飛機中的戰鬥機,你這飛機算什麼?
這還不簡單,給我們的飛機裝上個大炮不就得了~子彈的移動跟飛機的移動是同樣的道理,擦除,新位置畫,再擦除,再畫……
;//////////////發射子彈子程式
;入口引數 玩家飛機發射口的座標bx+5,bp
shoot_plane proc
push ax
push bx
push cx
push dx
push si
push bp
mov cx,bx
add cx,5 ;x座標BX+5
mov dx,bp ;y座標
dec dx
;擦除炮彈軌跡,移動炮彈
a0: MOV BX,2 ;寬度
INC DX
a1: MOV AH,0CH ;在繪圖模式顯示一點
MOV AL,0 ;顏色(黑色),用於擦除上一個子彈
INT 10H
INC CX
DEC BX
JNZ a1 ;擦除炮彈寬度
SUB CX,2
MOV BX,2
DEC DX
a2: MOV AH,0CH ;在繪圖模式顯示一點
MOV AL,11 ;顏色(白色),用於畫新子彈
INT 10H
INC CX
DEC BX
JNZ a2 ;畫出炮彈寬度
SUB CX,2
CALL delay<span style="white-space:pre"> </span>;時延,可用來調整子彈的移動速度
DEC DX
CMP DX,6 ;迴圈畫炮彈,到頂端才停止
JA a0
notdes:
;最後一次擦除
mov bp,sp
mov cx,word ptr ss:[bp+8]
add cx,5
mov dx,7
MOV AH,0CH ;在繪圖模式顯示一點
MOV AL,0 ;顏色
INT 10H
inc cx
MOV AH,0CH ;在繪圖模式顯示一點
MOV AL,0 ;顏色
INT 10H
pop bp
pop si
pop dx
pop cx
pop bx
pop ax
ret
shoot_plane endp
這下子好咯,我們的飛機簡直就是戰鬥機了!可是有再大的能耐,沒有敵人也只能孤獨求敗~
這個時候,總得製造寫麻煩出來才好玩。現在很多東西都是一通百通了,敵人的繪畫與移動跟飛機如出一轍,此部分我不再贅述。
tips:我寫這個遊戲的時候,所有敵人都是統一移動的,這樣子比較方便,當然如果你想讓遊戲更高階一點,可以嘗試讓敵人非同步移動。
現在問題來了:一方面,敵人會以一定的速度往下移動,而在此同時,我們需要讀取鍵盤的狀態來移動我們的飛機,也就是說,我們要做到敵人、我們的飛機同時都能夠移動。一開始,我的方案是進行輪詢,敵人移動--監測鍵盤--敵人移動--監測鍵盤,但是這樣子做的效率是十分低下的,飛機移動的響應實在不敢恭維。
這個時候,系統的時鐘中斷就派上用場了。我們可以這樣設計:主程式中不斷查詢有無鍵盤輸入,然後當時鍾中斷來臨的時候,進入敵人的移動程式,使得敵人移動。如此一來,鍵盤的控制得以實時響應,然後每隔一段時間的中斷使得敵人移動——這一段程式的執行時間是如此的短,以致於我們幾乎感覺不到,所以看上去我們鍵盤的控制和敵人的移動是同時進行的——這也是我們在單核計算機中得以同時聊著QQ、聽著音樂、瀏覽網頁的原理。
mov al,34h ; 設控制字值
out 43h,al ; 寫控制字到控制字暫存器
mov ax,0ffffh ; 中斷時間設定
out 40h,al ; 寫計數器 0 的低位元組
mov al,ah ; AL=AH
out 40h,al ; 寫計數器 0 的高位元組
xor ax,ax ; AX = 0
mov ds,ax ; DS = 0
mov word ptr ds:[20h],offset Timer ; 設定時鐘中斷向量的偏移地址
mov ax,cs
mov word ptr ds:[22h],ax ; 設定時鐘中斷向量的段地址=CS
lop3:
call play_plane1 ;擦除飛機軌跡
call play_plane ;畫飛機
mov cx,bx
mov dx,bp
again:
mov ah,01 ;檢測是否有按鍵,沒有的話迴圈檢測
int 16h
jz again ;沒有按鍵,顯示移動,再次檢測
;從鍵盤讀入字元
mov ah,0H
int 16H
;判斷字元
cmp ah,72
je up
cmp ah,80
je down
cmp ah,75
je left
cmp ah,77
je right
cmp ah,57 ;空格
je shoot
cmp ah,16 ;Q退出
je quite
jmp lop3
up: sub bp,3
jmp lop3
down: add bp,3
jmp lop3
left: sub bx,3
jmp lop3
right: add bx,3
jmp lop3
shoot:
call shoot_plane
jmp lop3
;退出程式
quite:
mov ah,4ch
int 21h
Timer:
push ax
mov al,byte ptr ds:[timecontrol] ;timecontrol為設定的敵人移動速度
cmp byte ptr ds:[delay_timer],al ;delay_timer等於timecontrol時才移動敵人,否則本次中斷不做任何事
pop ax
jnz goout
mov byte ptr ds:[delay_timer],0
call move_smile
call play_smile ;畫笑臉
goout:
inc byte ptr [delay_timer]
push ax
mov al,20h ; AL = EOI
out 20h,al ; 傳送EOI到主8529A
out 0A0h,al ; 傳送EOI到從8529A
pop ax
iret ; 從中斷返回
好啦~程式的主要部分我都在上面提及了,其他部分,比如如何檢測炮彈是否打到了敵人等等,都是體力活,就不在這裡介紹了,大家可以自行新增到程式碼當中,以完善功能。另外,大家可以根據自己的需要和興趣新增一些相關功能,比如我的程式可以設定敵人的數量、移動速度,可以計算得分等等。
最後,給大家展示下我的遊戲成品:
Over,thanks!