上学期汇编语言的期末考核可选的一项是用纯汇编语言编写一个小游戏,喜欢动手的我当然首选这个了。小游戏当中用来练手的,一般有贪吃蛇、俄罗斯方块、打飞机之类的咯,于是我选择了其中一个——打飞机!咳咳,选择它可并不是因为个人某种特殊喜好,我想大多数男同胞都会倾向于玩这款游戏@。@
接下来我就跟大家分享一下如何一步步的完成这项工作,当然,我并不打算把源代码都贴上来,我主要是为大家提供一个思路,必要时提供部分代码~
首先说明下,本例子的运行环境是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
什么?不会开飞机?没关系,接下来我就教你怎么开飞机!不过,没有驾驶证的小朋友,不要随便试哦~
首先,当然是坐到驾驶座上咯~然后呢,绑好安全带……
哈哈,接下来要启动引擎啦~想让飞机移动,先想想移动是什么概念,比如你现在向前走一步,是不是在原来位置的你不见了,而在当前位置出现了一个一模一样的你~
同样的,要让飞机移动,我们要做的事情是:把原来位置的飞机擦除,然后在新位置画上一架一模一样的飞机!由于我们的游戏背景是黑色的,所以实际上擦除飞机,等于在该位置画一架黑色的飞机将其覆盖掉即可:
;画水平直线有了上面的程序之后,你只需要在测试程序中加入控制语句,当键盘输入←时,调用play_plane1函数擦除飞机,然后飞机的横坐标减1,再调用play_plane画上新的飞机,就能看到飞机移动的效果啦~
;入口参数 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
怎么样,这飞机开起来是不是很爽?但是有些朋友不乐意了,咱们可是想要打造一架飞机中的战斗机,你这飞机算什么?
这还不简单,给我们的飞机装上个大炮不就得了~子弹的移动跟飞机的移动是同样的道理,擦除,新位置画,再擦除,再画……
;//////////////发射子弹子程序
;入口参数 玩家飞机发射口的坐标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坐标
decdx
;擦除炮弹轨迹,移动炮弹
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
jnzgoout
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!