8086纯汇编实现推箱子

设计内容

游戏中玩家可以用键盘的上,下,左,右键控制小人的移动位置,游戏规则也十分简单,玩家在一个有限空间内,将木箱放在指定的位置。箱子只能推不能拉,而且一次智能推动一个箱子,胜利的条件是把所有箱子都推到目的地。

需求分析

经典的推箱子是一个来自日本的古老游戏,目的是在训练人的逻辑思考能力。在一个狭小的仓库中,要求把木箱从开始位置推放到指定的位置。在仓库有障碍物,稍不小心就会出现箱子无法移动或者通道被堵住的情况,而且箱子只能推,不能拉,所以需要巧妙的利用有限的空间和通道,合理安排移动的次序和位置,才能顺利的完成任务。本游戏一共7关,由易到难,拥有初始化、按键处理、重置、播放音乐、选关及退出功能。
1.初始化包括屏幕初始化和每一关卡的初始化,屏幕被初始化宽640×400×56色的图形模式。
2.按键处理包括移动小人和移动箱子,通过移动W、S、A、D键来控制小人的移动,从而推动箱子,以把箱子推到指定的目的地为过关。
3.每一关都可以重置,按0键可以重置当前关。
4.在每一关都可跳关或选择上一关卡,按1键进行跳关,按2键退回上一关卡。
5.播放音乐可通过按3键进行处理,默认开启状态,按一下关闭,再按开启。
6.按Esc键可以在任何时候退出游戏。
备注:按CTRL+F12键加速游戏,建议调节CPU速度到100000以上,按CTRL+F11键减速游戏,按ALT+ENTER键进入全屏模式,以此获得更好的游戏体验。

概要设计

  • 方案设计
    在这里插入图片描述

  • 模块功能说明
    本程序采用模块化设计,包括6个模块,分别是初始化模块、绘图模块、人物移动模块、输入控制模块、音乐播放模块和文件操作模块。各个模块的功能描述如下:

    1) 初始化模块:本模块的主要任务是将游戏中的墙、目标位置、菜单信息、箱子、人物输出到游戏窗口上。此模块通过map中的偏移地址,然后根据坐标绘制各个信息,同时其还会显示菜单信息。
    2) 绘图模块:此模块用于绘制路、墙、人物、目的地等图形。
    3) 人物移动模块:本模块的主要任务是控制人物的正确移动。接收到人物移动的输入指令后,判断人物是否能移动,可以移动更新地图中各个位置的信息。此模块分为两个部分,先判断前方是否是箱子,此功能实现在子程序GO中,若是箱子,判断箱子能否移动,此功能实现在子程序AHEAD中,若箱子能移动,更新地图信息,否则不执行操作。
    4) 输入控制模块:本模块的主要任务是接收玩家输入的按键执行对应的操作。游戏中需要通过接收玩家输入的按键,控制游戏重新开始,游戏音效的开启与关闭,关卡选择,游戏退出以及控制人物移动等重要游戏功能。游戏中本功能的主要实现在子程序GET_CHAR,在获取到用户输入的按键后与游戏中的操作按键进行逐一比对。比对游戏中已经设定的按键即可确定用户输入的按键,进而跳转到按键对应的功能实现程序段中,继续执行相应操作,从而保证人物的正常移动、游戏跳转以及游戏功能的实现。
    5) 音乐播放模块:此模块用于通过蜂鸣器播放音乐。
    6) 文件操作模块:此模块用于将地图信息从本地文件中读取出来,以达到游戏的可拓展性。

详细设计

  • 流程图
    总流程图
    音乐播放模块
    按键处理模块
    功能按键处理模块
    人物移动模块
    箱子移动模块
    图形界面绘制及判断胜负模块
  • 主要子程序调用
    在这里插入图片描述

运行截图

在这里插入图片描述
游戏初始界面,这是第一关,左上角8×8个小方格,白色是路,灰色是墙,褐色是箱子,绿色叉号是目标位置,蓝色是角色,右边是菜单,分别是按ESC退出,按↑向上移动,按↓向下移动,按←向左移动,按→向右移动,按0键重新开始这关,按1跳到下一关卡,按2跳到上一关卡,按3进行背景音乐的开和关,level后面的是当前关卡/最高关卡,Score后面是当前分数。最下面显示的是为了游戏的更好体验,请将DOS BOX 的CPU速度调制100000 cycles以上。

绿色箱子表示箱子推到目的地,由于按了↑,而上方是墙,不能移动,游戏会提示你的前方有障碍物,不能直行。
在这里插入图片描述

当把箱子推到目的地后会有提示信息:恭喜!你将进入下一关!
在这里插入图片描述

按任意键进入下一关,右边level后的当前关卡信息也随之变为第二关。

在这里插入图片描述

连按两下1键,会跳两关,变为第4关。

在这里插入图片描述
按2键会回到上一关,即第3关。

在这里插入图片描述

按↑、↓、←、→可以移动人物位置。

在这里插入图片描述
按0键会回到当前关卡初始界面,按3键可进行音乐的开关。

调试情况

遇到的问题:

  • JMP跳转超出范围
    刚开始写程序时,有时代码写的过长使用JMP语句时,就会出现ADDRESS OUT OF RANGE XX的错误,之后使用了间接跳转的方法解决了这个问题。刚开始为了传参数方便大多都用的宏指令,但发现宏指令嵌套使用很容易出现JMP跳转范围不够的错误,即使使用间接跳转的方法也常出现范围不够的错误,最终将宏指令全部改为子程序解决了这个问题。
  • 读取文件不能随机读取
    在从本地文件读取地图数据时,发现只能从文件开始读取,但我们想要的数据需要随机读取,因此我们想到可以先将前面的数据读取出来,直到读到我们想要的数据起始地址,这样就达到了随机读取的目的。为此我们专门分配了1个字节的内存,每次把不需要的数据读取到这里,直到读到我们想要的数据。
  • 清屏太慢
    可能是CPU速度不够的原因,图形界面绘图时速度很慢,也没有专门的清屏中断,若用一种颜色画一个640×400的矩形进行清屏操作,又太费时间,之后设置显示窗口的方法达到了清屏的目的。
  • 背景音乐播放
    由于播放音乐时需要用到延时函数,若直接将一首歌曲的播放携程子程序放入主程序的循环中,音乐函数的延时会将程序挂起来,导致无法正常运行。因此我们先判断是否有按键按下,当无按键按下时播放一个频率,然后继续查询,按键按下时去执行按键操作,从而解决了这个问题。

源代码

  • 地图文件(名为pushbox.txt,与可执行文件需同目录)
4440011100000131000001011111112023113024111111121000001310000011100322011110000100110001430100013321100102201001000010010000100111111046300111100001331000110311001002310110200111001220110040001111111115310011111100100001111222011402330110233311111100100001111000000000864111111101332331013313310102220101002001010222010100140101111111035600000000001111001113010010300111100022011103024101111111000000004130111110001040111110120011053030110022011111013100010001000111110
  • 源代码
;名称:推箱子
;完成日期:2019-12-22
;需在dosbox下运行,另有名为pushbox.txt的地图文件与其同目录,地图文件如上

ROAD	EQU	48	
WALL	EQU	49
BOX	    EQU	50
AIM	    EQU	51
ROLE	EQU	52
BoxInAim	EQU	53
RoleInAim	EQU	54
WALL_LENGTH	EQU	30
WALL_DEPTH	EQU	8
;功能键定义
ESCAPE	EQU	27
UP	EQU	72
DOWN	EQU	80
LEFT	EQU	75
RIGHT	EQU	77
ReGame	EQU	48
LevelUp	EQU	49
LevelDown	EQU	50
MusicOn	EQU	51
DATA	SEGMENT
	MUS_FREG 	DW 330,294,262,294,3 dup (330)     ;频率表
            			DW 3 dup (294),330,392,392
               			DW 330,294,262,294,4 dup (330)
               			DW 294,294,330,294,262,-1
    	MUS_TIME	DW 6 dup (25),50                   ;节拍表
               			DW 2 dup (25,25,50)
              			DW 12 dup (25),100
	MusicFlag		DB	1
	FILE_NAME	DB	'pushbox.txt' , 0
	HANDLE	DW	?	;保存文件号
	TEMP_DB	DB	?	;用来临时存放不需要读的字节
	LevelFlag		DB	10 dup(0)	
	LEVEL_MAX	DB	7
	LEVEL	DB	1
	BOX_MAX	 DB	4
	ROLE_POS	 DB	4,4	
	DATA_ARRAY 	DB      48,48,49,49,49,48,48,48
			DB      48,48,49,51,49,48,48,48
			DB      48,48,49,48,49,49,49,49
			DB      49,49,49,50,48,50,51,49
			DB      49,51,48,50,52,49,49,49
			DB      49,49,49,49,50,49,48,48
			DB      48,48,48,49,51,49,48,48
			DB      48,48,48,49,49,49,48,48
DATA	ENDS

EXTRA1	SEGMENT
	VICTORY		DB	"Congratulations! You will enter the next level!"	
	SCORE		DB	"score:"
	SCORE_NUM	DB	"00","10","20","30","40","50","60","70","80","90"
	OBSCATLE	DB	"There are obstacles in front of you. You can't go straight!"
	MENU_EXIT	DB	"exit:esc",7 dup(' ')
	MENU_UP		DB	"up:",24,11 dup(' ')
	MENU_DOWN	DB	"down:",25,9 dup(' ')
	MENU_LEFT	DB	"left:",26,9 dup(' ')
	MENU_RIGHT	DB	"right:",27,8 dup(' ')
	MENU_REGAME	DB	"regame:0",7 dup(' ')
	MENU_LEVEL_UP	DB	"level up:1",5 dup(' ')
	MENU_LEVEL_DOWN	DB	"level down:2",3 dup(' ') 
	MENU_MUSIC	DB	"music(on/off):3"
	MENU_LEVEL	DB	"level:",' ','/',7 dup(' ')
	MENU_WARNING	DB	"In order to ensure a better game experience, please ensure that the CPU speed is at least 100000 cycles!"
	
EXTRA1	ENDS
CODE	SEGMENT
	ASSUME CS:CODE,DS:DATA,ES:EXTRA1
START:
	MOV	AX,	DATA
	MOV	DS,	AX
	MOV	AX,	EXTRA1
	MOV	ES,	AX
	
	CALL	GAME_INIT	;游戏初始化	
	CALL	GAME_START	
EXIT:	MOV	AH,	4CH
	INT	21H
	
;对本地文件进行写操作
;入口参数:DI:文件绝对路径的偏移量,SI:需传送数据的地址偏移量,CX:传送字节数
WRITE_FILE	PROC
	PUSH	AX			;保护现场
	PUSH	BX
	PUSH	CX
	PUSH	DX
	;打开文件
	MOV	DX,	DI		;文件绝对路径的偏移量送往DX
	MOV	AH,	3DH		;3DH功能调用
	MOV	AL,	2		;以读/写方式打开
	INT	21H

	MOV	HANDLE,	AX		;将文件句柄保存至HANDLE
	;写文件
	MOV	BX,	AX		;将文件句柄送至BX
	MOV	DX,	SI		;需传送数据的地址偏移量送往DX
	MOV	AH,	40H		;40H功能调用
	INT	21H
	
	;关闭文件
	MOV	BX,	HANDLE		;取出句柄
	MOV	AH,	3EH		;3EH功能调用
	INT	21H
	POP	DX			;恢复现场
	POP	CX
	POP	BX
	POP	AX
	RET
WRITE_FILE	ENDP
;对本地文件进行读操作
;入口参数:DI:文件绝对路径的偏移量,SI:需读取数据的地址偏移量,AX:从第AX个字节开始读,CX:传送字节数
READ_FILE	PROC
	PUSH	AX			;保护现场
	PUSH	BX
	PUSH	CX
	PUSH	DX

	PUSH	CX			;保存读的字节数
	MOV	CX,	AX		;将不需读的字节个数送至CX
	;打开文件
	MOV	DX,	DI		;文件绝对路径的偏移量送往DX
	MOV	AH,	3DH		;3DH功能调用
	MOV	AL,	2		;以读/写方式打开
	INT	21H

	MOV	HANDLE,	AX		;将文件句柄保存至HANDLE
	;写文件
	MOV	BX,	AX		;将文件句柄送至BX
	CMP	CX,	0
	JE	NO_TEMP
READ_TEMP_DB:				;每次读取1个字节,读取跳过字节的次数
	PUSH	CX
	MOV	CX,	1
	LEA	DX,	TEMP_DB		;将不需读取的字节读至TEMP_DB
	MOV	AH,	3FH		;3FH功能调用
	INT	21H
	POP	CX
	LOOP	READ_TEMP_DB
	

NO_TEMP:
	POP	CX			;取出需要读取的字节数
	MOV	DX,	SI		;需读取数据的地址偏移量送往DX
	MOV	AH,	3FH		;3FH功能调用
	INT	21H
	
	;关闭文件
	MOV	BX,	HANDLE		;取出句柄
	MOV	AH,	3EH		;3EH功能调用
	INT	21H
	POP	DX			;恢复现场
	POP	CX
	POP	BX
	POP	AX
	RET
READ_FILE	ENDP

;功能:计数器产生方波送至扬声器发声
;入口参数:DI:频率表首地址,BX:节拍表首地址
BEEP	PROC
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	DI
	MOV	AL,	10110110B
	OUT	43H,	AL	;计数器2用于产生方波送至扬声器发声
	MOV	DX,	12H
	MOV	AX,	348CH
	DIV	DI		;计数初值为12348C/频率
	OUT	42H,AL		;送低8位
	MOV	AL,AH
	OUT	42H,AL		;送高8位

	IN	AL,61H		;读取8255B端口
	MOV	AH,AL		;存在AH
	OR	AL,03H
	OUT	61H,AL		;输出至8255的B端口,使扬声器发声

G7:
	MOV	CX,	1FFFH;	3314	
	CALL	DELAY	
	DEC BX			;BL的值为控制长短声,BL=6(长),BL=1(短)
	JNZ	G7

	MOV	AL,AH		;恢复8255B端口值,停止发声
	OUT	61H,AL
	POP	DI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET	
BEEP	ENDP

;功能:延时函数
;入口参数:CX
DELAY PROC
	PUSH	AX
	PUSH	CX
DELAY1:
	IN	AL,	61H
	AND	AL,	10H
	CMP	AL,	AH
	JE	DELAY1
	MOV	AH,	AL
	LOOP DELAY1
	POP	CX
	POP	AX
	RET
DELAY endp
	
;功能:打印字符串
;入口参数:AX:字符串地址,CX:显示字符串长度,(DH、DL)=坐标(行、列)
PRINT_STR	PROC
	PUSH	AX
	PUSH	BX
	PUSH	DX
	PUSH	BP
	MOV	BP,	AX	;需显示的字符串首地址
	MOV	AX,	1301H	;AH:功能调用,AL:显示方式
	MOV	BH,	00H	;显示的页面
	MOV	BL,	0CH	;显示的前景色及背景色
	INT 10H
	POP	BP
	POP	DX
	POP	BX
	POP	AX	
	RET
PRINT_STR	ENDP

;功能:显示障碍物信息
;入口参数:无
DRAW_OBSCATLE	PROC
	PUSH	AX
	PUSH	CX
	PUSH	DX
	MOV	CX,	59	;要显示的字符串长度
	
	MOV	DX,	1100H	;(DH、DL)=坐标(行、列)
	LEA	AX,	OBSCATLE ;要显示的字符串首地址
	CALL	PRINT_STR	;调用字符串显示程序
	POP	DX
	POP	CX
	POP	AX
	RET
DRAW_OBSCATLE	ENDP
	
;功能:显示菜单界面
DRAW_MENU	PROC
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	MOV	CX,	15	;要显示的字符串长度
	
	MOV	DX,	022AH	;(DH、DL)=坐标(行、列)
	LEA	AX,	MENU_EXIT;要显示的字符串首地址	
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_UP	;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_DOWN ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_LEFT ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_RIGHT ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_REGAME ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_LEVEL_UP ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_LEVEL_DOWN ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	LEA	AX,	MENU_MUSIC ;要显示的字符串首地址	
	INC	DH		;行地址加1
	CALL	PRINT_STR	;调用字符串显示程序
	MOV	AL,	BYTE PTR LEVEL[0]	;获取当前关卡等级
	OR	AL,	30H			;将其转化成对应的ASCII码
	MOV	BYTE PTR MENU_LEVEL[6],	AL	;将其存入待显示字符串对应的位置中	
	MOV	AL,	BYTE PTR LEVEL_MAX[0]	;获取当前最大关卡等级
	OR	AL,	30H			;将其转化成对应的ASCII码
	MOV	BYTE PTR MENU_LEVEL[8],	AL	;将其存入待显示字符串对应的位置中
	LEA	AX,	MENU_LEVEL		;要显示的字符串首地址
	INC	DH				;行地址加1
	CALL	PRINT_STR			;调用字符串显示程序
	
	MOV	CX,	6			;待显示字符串长度
	MOV	DX,	0C2AH			;(DH、DL)=坐标(行、列)
	LEA	AX,	SCORE			;要显示的字符串首地址
	CALL	PRINT_STR			;调用字符串显示程序

	MOV	CX,	7			;获取当前关卡标志,为1BX加2,BX为分数偏移地址
	MOV	SI,	0
	MOV	BX,	0
CONTINUE_LEVEL:
	CMP	LevelFlag[SI],	1
	JNE	CONTINUE_LEVEL_ERGODIC
	INC	SI
	ADD	BX,	2
CONTINUE_LEVEL_ERGODIC:
	LOOP	CONTINUE_LEVEL

	MOV	DX,	0C30H			;(DH、DL)=坐标(行、列)
	MOV	CX,	2			;待显示字符串长度
	LEA	AX,	SCORE_NUM[BX]		;要显示的字符串首地址
	CALL	PRINT_STR			;调用字符串显示程序



	MOV	DX,	1500H			;(DH、DL)=坐标(行、列)
	MOV	CX,	104			;待显示字符串长度
	LEA	AX,	MENU_WARNING		;要显示的字符串首地址
	CALL	PRINT_STR			;调用字符串显示程序

	

	POP	SI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
DRAW_MENU	ENDP

;功能:显示胜利界面
;入口参数:无
DRAW_VICTORY	PROC
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	LEA	AX,	VICTORY			;要显示的字符串首地址
	MOV	CX,	47			;待显示字符串长度
	MOV	DX,	1100H			;(DH、DL)=坐标(行、列)
	CALL	PRINT_STR			;调用字符串显示程序
	MOV	AH,08H				;等待按键按下,不回显
	INT 21H
	MOV	BL,	BYTE PTR LEVEL[0]	;获取当前关卡等级
	DEC	BL
	MOV	BYTE PTR LevelFlag[BX],	1	;当前关卡标志置为1
	MOV	BL,	BYTE PTR LEVEL[0]	;获取当前关卡等级
	CMP	BL,	BYTE PTR LEVEL_MAX[0]	;与最大关卡等级比较
	JE	RE				;等于则跳转
	INC	BL				;否则关卡等级加1
	MOV	BYTE PTR LEVEL[0],	BL	;更新关卡等级信息
	XOR	BX,	BX
	JMP	V_EXIT				;跳转去更新地图信息与绘制界面
RE:	MOV	BYTE PTR LEVEL[0],1		;当前关卡是最大等级,将其更新为第一关
V_EXIT:	CALL	DATA_INIT			;更新地图信息
	CALL	GRAPH_INIT			;绘制游戏界面
	MOV	AX,	0C00H			;清空键盘缓冲区
	INT	21h
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
DRAW_VICTORY	ENDP



;功能:从文件中读取地图数据
;入口参数:无
DATA_INIT	PROC
	PUSH	AX
	PUSH	CX
	PUSH	DI
	PUSH	SI
	
	LEA	DI,	FILE_NAME		;获取文件名称
	LEA	SI,	BOX_MAX			;获存储地址
	MOV	AL,	67			;获取数据长度
	MOV	AH,	BYTE PTR LEVEL[0]	;获取当前关卡等级
	DEC	AH				;获取的内容地址为(AH-1)*67
	MUL	AH	
	MOV	CX,	67
	CALL	READ_FILE			;将地图信息读入
	LEA	SI,	BOX_MAX			;获取存储关卡箱子数量、角色坐标的地址,将ASCII码转化为数字
	MOV	CX,	3
DATA_INIT1:
	SUB	BYTE PTR [SI],	48
	INC	SI
	LOOP	DATA_INIT1	
	
	POP	SI
	POP	DI
	POP	CX
	POP	AX
	RET
DATA_INIT	ENDP

;功能:游戏数据、图形界面初始化
;入口参数:无
GAME_INIT	PROC
	CALL	DATA_INIT
	CALL	GRAPH_INIT
	CALL	DRAW_MENU
	RET
GAME_INIT	ENDP



;功能:清屏,将显示模式重新设置来清屏
;入口参数:无
GAME_CLEAR	PROC
	PUSH	AX		;保护现场
	PUSH	BX
	MOV	AX,	4F02H	;640*400 256色的图形模式
	MOV	BX,	100H	;用来设定显示模式的服务程序	
	INT	10H
	POP	BX		;恢复现场
	POP	AX
	RET
GAME_CLEAR	ENDP


;功能:画点
;入口参数:AX:x坐标,BX:y坐标,DL颜色
POINT	PROC
	PUSH	AX		;保护现场
	PUSH	CX
	PUSH	DX
	MOV	CX,	AX	;X坐标
	MOV	AL,	DL	;颜色
	MOV	DX,	BX	;Y坐标
	MOV	AH,	0CH	;写入像点
	INT	10H		
	
	POP	DX		;调用中断
	POP	CX
	POP	AX
	RET		
POINT	ENDP

;功能:画横线
;入口参数:AX:x坐标,BX:y坐标,DL:颜色,DI:长度
HORIZONTAL_LINE	PROC
	PUSH	AX		;保护现场
	PUSH	CX
	MOV	CX,	DI
HORIZONTAL:	CALL	POINT		;调用画点子程序
	
	INC	AX		;x坐标+1
	LOOP	HORIZONTAL
			
	POP	CX		;恢复现场
	POP	AX
	RET
HORIZONTAL_LINE	ENDP

;功能:画竖线
;入口参数:AX:x坐标,BX:y坐标,DL:颜色:,DI:长度
VERTICA_LINE	PROC
	PUSH	BX		;保护现场
	PUSH	CX
	MOV	CX,	DI
VERTICA:	CALL	POINT		;调用画点子程序
	
	INC	BX		;y坐标+1
	LOOP	VERTICA
	POP	CX		;恢复现场
	POP	BX
	RET
VERTICA_LINE	ENDP

;功能:画正斜线
;入口参数:AX:x坐标,BX:y坐标,DL:颜色,DI:横向长度
OBLIQUE_LINE	PROC
	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX
	MOV	CX,	DI
OBLIQUE:	CALL	POINT		;调用画点子程序
	INC	AX		;x坐标+1
	INC	BX		;y坐标+1
	LOOP	OBLIQUE
	
	POP	CX		;恢复现场
	POP	BX
	POP	AX
	RET
OBLIQUE_LINE	ENDP



;功能:画反斜线
;入口参数:AX:x坐标,BX:y坐标,DL:颜色:,DI:横向长度
REV_OBLIQUE_LINE	PROC
	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX
	MOV	CX,	DI
REV_OBLIQUE:	CALL	POINT		;调用画点子程序
	DEC	AX		;x坐标-1
	INC	BX		;y坐标+1
	LOOP	REV_OBLIQUE
	POP	CX		;恢复现场
	POP	BX
	POP	AX
	RET
REV_OBLIQUE_LINE	ENDP

;功能:画实心矩形
;入口参数:AX:横坐标,BX:纵坐标,DL:颜色,DI:宽度,SI:高度
SOLID_RECTANGLE	PROC
	PUSH	AX		;保护现场
	PUSH	CX
	MOV	CX,	SI
RECTANGLE: 
	CALL	VERTICA_LINE
	INC	AX
	LOOP RECTANGLE
	POP	CX		;恢复现场
	POP	AX
	RET
SOLID_RECTANGLE	ENDP

;功能:画墙
;入口参数:AX:横坐标,BX:纵坐标
DRAW_WALL	PROC
	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	DI
	PUSH	SI
				;画中心部分
	MOV	DL,	011110100B
	MOV	DI,	WALL_LENGTH
	MOV	SI,	WALL_LENGTH
	CALL	SOLID_RECTANGLE
	
	;画箱子四边,利用色差达到立体效果
	PUSH	AX		;保护箱子坐标
	PUSH	BX
	
	MOV	CX,	WALL_DEPTH
	MOV	DI,	WALL_LENGTH
	MOV	DL,	01011011B
WALL1:	CALL	HORIZONTAL_LINE
	INC	AX
	INC	BX
	SUB	DI,	2	
	LOOP	WALL1
	
	POP	BX		;恢复箱子坐标
	POP	AX	
	
	PUSH	AX		;保护箱子坐标
	PUSH	BX
	
	MOV	CX,	WALL_DEPTH
	MOV	DI,	WALL_LENGTH
	MOV	DL,	01011011B
WALL2:	CALL	VERTICA_LINE
	INC	AX
	INC	BX
	SUB	DI,	2	
	LOOP	WALL2
		
	POP	BX		;恢复箱子坐标
	POP	AX
	
	PUSH	AX		;保护箱子坐标
	PUSH	BX
	
	ADD	AX,	WALL_LENGTH
	MOV	CX,	WALL_DEPTH
	MOV	DI,	WALL_LENGTH
	MOV	DL,	11110111B
WALL3:	CALL	VERTICA_LINE
	DEC	AX
	INC	BX
	SUB	DI,	2	
	LOOP	WALL3
	
	POP	BX		;恢复箱子坐标
	POP	AX
	
	ADD	BX,	WALL_LENGTH
	MOV	CX,	WALL_DEPTH
	MOV	DI,	WALL_LENGTH
	MOV	DL,	11110111B
	
WALL4:	CALL	HORIZONTAL_LINE
	INC	AX
	DEC	BX
	SUB	DI,	2	
	LOOP	WALL4
	
	POP	SI		;恢复现场
	POP	DI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
DRAW_WALL	ENDP

;功能:画叉号
;入口参数:AX:横坐标,BX:纵坐标,DL:颜色,DI:横向长度
DRAW_CROSS	PROC
	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX

	PUSH	AX		;保护坐标
	PUSH	BX
	MOV	CX,	5
	ADD	AX,	5	
CROSS1: 
	CALL	OBLIQUE_LINE
	DEC	AX
	INC	BX	
	LOOP	CROSS1
	
	POP	BX		;恢复坐标
	POP	AX
		
	MOV	CX,	5
	ADD	AX,	25
CROSS2: 
	CALL	REV_OBLIQUE_LINE
	INC	AX
	INC	BX	
	LOOP	CROSS2
	
	POP	CX		;恢复现场
	POP	BX
	POP	AX
	RET
DRAW_CROSS	ENDP

;功能:画目标位置
;入口参数:AX:横坐标,BX:纵坐标,DL:颜色,DI:横向长度
DRAW_AIM	PROC
	PUSH	DX		;保护现场
	PUSH	DI
	
	MOV	DI,	25
	MOV	DL,	1010B	

	CALL	DRAW_CROSS
	
	POP	DI		;恢复现场
	POP	DX
	RET
DRAW_AIM	ENDP

;功能:画箱子
;入口参数:AX:横坐标,BX:纵坐标
DRAW_BOX	PROC
	PUSH	DX		;保护现场
	PUSH	DI
	PUSH	SI
	
	MOV	DL,	00000100B
	MOV	DI,	30
	MOV	SI,	30
	CALL	SOLID_RECTANGLE
	
	MOV	DI,	25
	MOV	DL,	00000101B
	
	CALL	DRAW_CROSS
		
	POP	SI		;恢复现场
	POP	DI
	POP	DX
	RET
DRAW_BOX	ENDP

;功能:画角色
;入口参数:AX:横坐标,BX:纵坐标
DRAW_ROLE	PROC
	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	DX
	PUSH	DI
	PUSH	SI
	
	MOV	DL,	1001B
	MOV	DI,	4
	MOV	SI,	4
	ADD	AX,	13
	ADD	BX,	2
	CALL	SOLID_RECTANGLE
	
	MOV	DI,	2
	ADD	AX,	1
	ADD	BX,	4
	CALL	VERTICA_LINE

	ADD	AX,	1
	CALL	VERTICA_LINE
	
	MOV	DI,	10
	MOV	SI,	6
	SUB	AX,	3
	ADD	BX,	2
	CALL	SOLID_RECTANGLE
	
	MOV	DI,	5
	ADD	BX,	2
	CALL	REV_OBLIQUE_LINE
	
	ADD	AX,	6
	CALL	OBLIQUE_LINE
	
	MOV	DI,	8
	SUB	AX,	1
	ADD	BX,	8
	CALL	VERTICA_LINE
	
	SUB	AX,	4
	CALL	VERTICA_LINE
		
	POP	SI		;恢复现场
	POP	DI
	POP	DX
	POP	BX
	POP	AX
	RET
DRAW_ROLE	ENDP

;功能:画目标位置的人
;入口参数:AX:横坐标,BX:纵坐标
DRAW_RoleInAim	PROC
	
	CALL	DRAW_AIM
	CALL	DRAW_ROLE

	RET
DRAW_RoleInAim	ENDP

;功能:画绿箱子
;入口参数:AX:横坐标,BX:纵坐标
DRAW_BoxInAim	PROC
	PUSH	DX		;保护现场
	PUSH	DI
	PUSH	SI
	
	MOV	DL,	1010B
	MOV	DI,	30
	MOV	SI,	30
	CALL	SOLID_RECTANGLE
	
	MOV	DL,	1000B
	MOV	DI,	25
	CALL	DRAW_CROSS
		
	POP	SI		;恢复现场
	POP	DI
	POP	DX
	RET
DRAW_BoxInAim	ENDP

;功能:画路
;入口参数:AX:横坐标,BX:纵坐标
DRAW_ROAD	PROC
	PUSH	DX		;保护现场
	PUSH	DI
	PUSH	SI
	
	MOV	DL,	0FH
	MOV	DI,	30
	MOV	SI,	30
	
	CALL 	SOLID_RECTANGLE
	
	POP	SI		;恢复现场
	POP	DI
	POP	DX
	RET
DRAW_ROAD ENDP

;功能:界面初始化
;入口参数:无
GRAPH_INIT	PROC

	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	
	XOR	DX,	DX	;DX初始化为0,用来计算推到目的地的箱子	

	CALL	GAME_CLEAR
	MOV	CX,	8	;外层循环次数
	LEA	SI,	DATA_ARRAY	
	MOV	BX,	0	;X坐标
OUT_LOOP:
	PUSH	CX
	MOV	CX,	8	;内层循环次数
	MOV	AX,	0	;Y坐标
IN_LOOP:	
	
COMPARE:	
	CMP	BYTE PTR [SI],	ROAD	;当前位置是路
	JNZ	A1			;否则跳转
	CALL	DRAW_ROAD		;调用画路子程序
B1:	
	JMP	CONTINUE_IN_LOOP	;继续下一循环
A1:	
	CMP	BYTE PTR [SI],	WALL	;当前位置是墙
	JNZ	A2			;否则跳转
	CALL	DRAW_WALL
	JMP	B1			;继续下一循环
A2:	
	CMP	BYTE PTR [SI],	BOX	;当前位置是箱子
	JNZ	A3			;否则跳转
	CALL	DRAW_BOX
	JMP	B1			;继续下一循环
A3:	
	CMP	BYTE PTR [SI],	AIM	;当前位置是目标位置
	JNZ	A4			;否则跳转
	CALL	DRAW_AIM
	JMP	B1			;继续下一循环
A4:	
	CMP	BYTE PTR [SI],	ROLE	;当前位置是角色
	JNZ	A5			;否则跳转
	CALL	DRAW_ROLE
	JMP	B1			;继续下一循环
A5:	
	CMP	BYTE PTR [SI],	BoxInAim ;当前位置是在目的地的箱子
	JNZ	A6			;否则跳转
	CALL	DRAW_BoxInAim
	INC	DX			;如果箱子推到目的地,DX++
	JMP	B1			;继续下一循环
A6:	
	CMP	BYTE PTR [SI],	RoleInAim ;当前位置是在目的地的角色
	JNZ	B1			;继续下一循环
	CALL	DRAW_RoleInAim
	
CONTINUE_IN_LOOP:
	
	INC	SI			;指向地图数据的指针后移
	ADD	AX,	30		;x坐标+30
	LOOP	IN_LOOP
	POP	CX
	ADD	BX,	30		;y坐标+30
	LOOP	OUT_LOOP
	
	CMP	DL,	BOX_MAX[0]	;比较推到目的地的箱子数量与箱子总数
	JNE	PEND			;不等则跳转

	CALL	DRAW_VICTORY		;相等则执行胜利子程序

PEND:	POP	SI		;恢复现场		
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
GRAPH_INIT	ENDP


;功能:计算一维数组下标
;入口参数:BH:数组二维下标,BL:数组一维下标
;出口参数:SI:一维数组下标
CALC_POS	PROC
	PUSH	AX	;保护现场
	PUSH	BX
	MOV	AL,	8	;SI=BH*8+BL
	MUL	BH
	AND	BX,	00FFH
	MOV	SI,	BX
	ADD	SI,	AX
	POP	BX	;恢复现场
	POP	AX
	RET
CALC_POS	ENDP

;功能:计算一维数组下标(移动的下一个方向)
;入口参数:BH:数组二维下标,BL:数组一维下标,DH:四个数表示上、下、左、右四个方向
;出口参数:BH:前进后的数组二维下标,BL:前进后的数组一维下标
PRE_POS	PROC
	PUSH	AX	;保护现场
	CMP	DH,	UP
	JNE	DOWN_2
	DEC	BH
	JMP	CALC_END
DOWN_2:
	CMP	DH,	DOWN
	JNE	LEFT_2
	INC	BH
	JMP	CALC_END
LEFT_2:
	CMP	DH,	LEFT
	JNE	RIGHT_2
	DEC	BL
	JMP	CALC_END
RIGHT_2:
	CMP	DH,	RIGHT
	JNE	CALC_END
	INC	BL
	
CALC_END:
	POP	AX	;恢复现场
	RET
PRE_POS	ENDP

;功能:按键对角色进行操作
;入口参数:BH:数组二维下标,BL:数组一维下标
GET_CHAR	PROC

	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	
	MOV	BL,	ROLE_POS[1]	;取角色下标
	MOV	BH,	ROLE_POS[0]
	CALL	CALC_POS

	XOR	DX,	DX	;DX清零
			
	
	MOV	AH,	08H	;读键盘输入到AL中无回显
	INT	21H		
	
	CMP	AL,	70	;判断是方向键还是功能键,方向键ASII码值大于70
	JB	ESCAPE_1	;小于跳转到功能键比较
	
	CMP	AL,	UP	;上键
	JNE	DOWN_1		;否则跳转
	MOV	DH,	UP	;将上键ASII码值移入DH
	JMP	GO_AHEAD	;跳转判断可否前行
DOWN_1:	
	CMP	AL,	DOWN	;下键
	JNE	LEFT_1		;否则跳转
	MOV	DH,	DOWN	;将下键ASII码值移入DH
	JMP	GO_AHEAD	;跳转判断可否前行
LEFT_1:	
	CMP	AL,	LEFT	;左键
	JNE	RIGHT_1		;否则跳转
	MOV	DH,	LEFT	;将左键ASII码值移入DH
	JMP	GO_AHEAD	;跳转判断可否前行
RIGHT_1:
	CMP	AL,	RIGHT	;右键
	JNE	TEMP_END	;否则跳转
	MOV	DH,	RIGHT	;将右键ASII码值移入DH
	JMP	GO_AHEAD	;跳转判断可否前行

ESCAPE_1:
	CMP	AL,	ESCAPE	;ESC键
	JNE	REGAME_1
	CALL	GAME_CLEAR	;清屏
	JMP	EXIT		;退出游戏
REGAME_1:
	CMP	AL,	ReGame	;0键
	JNE	LEVEL_UP_1
	CALL	GAME_INIT	;调用初始化程序
	JMP	TEMP_END	;跳转到功能按键结束处理
LEVEL_UP_1:
	CMP	AL,	LevelUp			;1键
	JNE	LEVEL_DOWN_1
	MOV	BL,	BYTE PTR LEVEL[0]	;取当前关卡等级
	CMP	BL,	BYTE PTR LEVEL_MAX[0]	;与最大关卡等级比较
	JE	RE1				;相等则跳转到RE1,将其置为1
	INC	BL				;否则关卡等级+1
	MOV	BYTE PTR LEVEL[0],	BL
	JMP	LEVEL_END			;跳转到功能按键结束处理
RE1:	MOV	BYTE PTR LEVEL[0],1		;将当前关卡等级置为1
	JMP	LEVEL_END			;跳转到功能按键结束处理
TEMP_END:					;中间跳转,防止跳转范围不够
		JMP	END1
LEVEL_DOWN_1:
	CMP	AL,	LevelDown		;2键
	JNE	MUSIC_ON_1		
	MOV	BL,	BYTE PTR LEVEL[0]	;取当前关卡等级
	CMP	BL,	1			;与最初关卡等级比较
	JE	RE2				;相等则跳转,将其置为最大
	DEC	BL				;否则关卡等级减1
	MOV	BYTE PTR LEVEL[0],	BL
	JMP	LEVEL_END			;跳转到功能按键结束处理
RE2:	MOV	CL,	BYTE PTR LEVEL_MAX[0]	;将当前关卡等级置为最大
	MOV	BYTE PTR LEVEL[0],	CL
	JMP	LEVEL_END			;跳转到功能按键结束处理
MUSIC_ON_1:				
	CMP	AL,	MusicOn			;3键	
	JNE	END1				;否则结束
	XOR	BYTE PTR MusicFlag[0],	01H	;对当前音乐状态取非
	JMP	END1				;结束
	;功能按键处理
LEVEL_END:	CALL	DATA_INIT		;游戏地图数据初始化	
		CALL	GRAPH_INIT		;游戏界面初始化
		JMP	END1			;结束
	;障碍处理,下面程序的跳转	
END0:	CALL	DRAW_OBSCATLE			;
	JMP	END1

GO_AHEAD:
	CALL	GO				;判断前方是否可行
	CMP	DL,	1			
	JNE	END0				;不可行跳转障碍处理
	
	CMP	BYTE PTR DATA_ARRAY[SI],	ROLE	;否则判断当前位置是否是角色
	JNE	GO_AHEAD1				;不是将当前位置置为目的地
	MOV	BYTE PTR DATA_ARRAY[SI],	ROAD	;否则置为路
	JMP	S_POS					;跳转去保存当前人物坐标
GO_AHEAD1:
	MOV	BYTE PTR DATA_ARRAY[SI],	AIM	;将当前位置置为目的地

S_POS:	CALL	PRE_POS					;计算人物移动后的位置
	MOV	ROLE_POS[0],	BH	;保存角色下标
	MOV	ROLE_POS[1],	BL
	
	
	MOV 	AX,	0C00H	;清空键盘缓冲区
	INT	21H
	CALL	GRAPH_INIT

END1:	POP	SI		;恢复现场
	POP	DX	
	POP	CX	
	POP	BX
	POP	AX
	RET
GET_CHAR	ENDP


;功能:前面人前面是否可走
;入口参数:BH:数组二维下标,BL:数组一维下标
;出口参数:DL:为0时表示不可以走,为1时表示可以走
GO	PROC

	PUSH	AX		;保护现场
	PUSH	BX
	PUSH	CX
	PUSH	SI
	
	XOR	DL,	DL	;DL清零
	CALL	PRE_POS		;将BX置为移动后的坐标
	CALL	CALC_POS	;计算移动后在地图数组中的偏移量
	
	CMP	BYTE PTR DATA_ARRAY[SI],	ROAD	;当前位置是路
	JNE	WALL_1					;否则跳转
	MOV	DL,	1				;可以移动
	MOV	BYTE PTR DATA_ARRAY[SI],	ROLE	;当前位置置为角色
	JMP	END2					;跳转结束
WALL_1:		
	CMP	BYTE PTR DATA_ARRAY[SI],	WALL	;当前位置是墙
	JNE	BOX_1					;否则跳转
	JMP	END2					;跳转结束,默认不可以移动
BOX_1:
	CMP	BYTE PTR DATA_ARRAY[SI],	BOX	;当前位置是箱子
	JNE	AIM_1					;否则跳转
	CALL	AHEAD					;判断箱子能否移动
	CMP	DL,	1
	JNE	END2					;不能移动,跳转结束
	MOV	BYTE PTR DATA_ARRAY[SI],	ROLE	;可以移动将当前位置置为角色
	JMP	END2					;跳转结束
AIM_1:	
	CMP	BYTE PTR DATA_ARRAY[SI],	AIM	;当前位置是目的地
	JNE	BoxInAim_1				;否则跳转
	MOV	DL,	1				;可以移动
	MOV	BYTE PTR DATA_ARRAY[SI],	RoleInAim ;将当前位置置为角色在目的地
	JMP	END2					;跳转结束
BoxInAim_1:
	CMP	BYTE PTR DATA_ARRAY[SI],	BoxInAim ;当前位置是箱子在目的地
	JNE	END2					;否则跳转
	CALL	AHEAD					;判断箱子能否移动
	CMP	DL,	1		
	JNE	END2					;不能移动,跳转结束
	MOV	BYTE PTR DATA_ARRAY[SI],	RoleInAim ;可以移动将当前位置置为角色在目的地

END2:	POP	SI		;恢复现场		
	POP	CX
	POP	BX
	POP	AX
	RET
GO	ENDP

;功能:判断箱子前方是否能走
;入口参数:BH:数组二维下标,BL:数组一维下标
;出口参数:DL:为0时表示不可以走,为1时表示可以走
AHEAD	PROC
	PUSH	BX			;保护现场
	PUSH	SI
	XOR	DL,	DL	;DL清零
	CALL	PRE_POS		;将BX置为移动后的坐标
	CALL	CALC_POS	;计算移动后在地图数组中的偏移量
	
	CMP	BYTE PTR DATA_ARRAY[SI],	ROAD	;当前为位置是路
	JNE	WALL_2					;否则跳转
	MOV	DL,	1				;可以移动
	MOV	BYTE PTR DATA_ARRAY[SI],	BOX	;将当前位置置为箱子
	JMP	END3					;跳转结束
WALL_2:	
	CMP	BYTE PTR DATA_ARRAY[SI],	WALL	;当前位置是墙
	JNE	BOX_2					;否则跳转
	JMP	END3					;跳转结束
BOX_2:
	CMP	BYTE PTR DATA_ARRAY[SI],	BOX	;当前位置是箱子
	JNE	AIM_2					;否则则跳转
	JMP	END3					;跳转结束
AIM_2:	
	CMP	BYTE PTR DATA_ARRAY[SI],	AIM	;当前位置是目标位置
	JNE	BoxInAim_2				;否则跳转
	MOV	DL,	1
	MOV	BYTE PTR DATA_ARRAY[SI],	BoxInAim ;将当前位置置为箱子在目的地
	JMP	END3					;跳转结束
BoxInAim_2:
	CMP	BYTE PTR DATA_ARRAY[SI],	BoxInAim ;当前位置是箱子在目的地
	JNE	END3					;跳转结束

END3:	POP	SI			;恢复现场
	POP	BX
	RET
AHEAD	ENDP

;功能:开始游戏
;入口参数:无	
GAME_START	PROC
	PUSH	AX
	PUSH	BX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	BP
	
MUS_INIT:			;音乐频率表、节拍表首地址初始化
	XOR	AX,	AX
	XOR	SI,	SI
	XOR	BP,	BP
LOOP1:		
	MOV	AH,	0BH	;是否有按键按下
	INT	21H
	CMP	AL,	00H	;无按键则跳转播放音乐
	JE	MUSIC1	
	
	CALL	GET_CHAR	;按键处理	
	CALL	DRAW_MENU	;绘制菜单
MUSIC1:
	MOV	DL,	BYTE PTR MusicFlag[0]	;判断音乐播放标志是否为0
	CMP	DL,	0
	JE	MUS_INIT			;是则跳转音乐初始化
	MOV DI, mus_freg[SI]			;否则获取频率和节拍信息
	CMP DI, 0FFFFH				;判断是否播放到音乐末尾
	JE	MUS_INIT			;是则跳转音乐初始化
	MOV BX, mus_time[BP]
	CALL BEEP				;调用蜂鸣器子程序播放音乐
	ADD	SI,	2			;指针移向下一个字
	ADD	BP,	2			;指针移向下一个字
	JMP	LOOP1				;继续循环
	
	POP	BP
	POP	DI
	POP	SI
	POP	DX
	POP	BX
	POP	AX	
	RET
GAME_START	ENDP


CODE	ENDS

	END	START
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这里提供一个 x86 汇编语言实现推箱子游戏的简单代码示例,仅供参考: ```assembly .model small .stack 100h .data map db 10,10,0,0,0,0,0,0,0,0 db 10,10,0,1,1,1,1,1,1,0 db 10,10,0,1,3,0,0,0,1,0 db 10,10,0,1,2,2,2,0,1,0 db 10,10,0,1,0,0,0,0,1,0 db 10,10,0,1,0,0,0,0,1,0 db 10,10,0,1,0,0,0,0,1,0 db 10,10,0,1,0,0,0,0,1,0 db 10,10,0,1,0,0,0,0,1,0 db 10,10,0,1,1,1,1,1,1,0 playerX dw 3 playerY dw 2 .code main proc mov ax, @data mov ds, ax mov ah, 0 int 33h ; 打开鼠标 call drawMap ; 绘制地图 call drawPlayer ; 绘制玩家 gameLoop: mov ah, 0 int 33h ; 获取鼠标状态 cmp bx, 1 ; 判断左键是否按下 jne gameLoop mov dx, cx ; 获取鼠标 x 坐标 mov dy, dx ; 获取鼠标 y 坐标 sub dx, 10 ; 转换为地图坐标 sub dy, 10 ; 转换为地图坐标 call movePlayer, dx, dy ; 移动玩家 call drawMap ; 重新绘制地图 call drawPlayer ; 重新绘制玩家 jmp gameLoop mov ah, 0 int 33h ; 关闭鼠标 mov ax, 4c00h int 21h main endp ; 绘制地图 drawMap proc mov si, offset map mov dl, 0 mov dh, 0 mov ah, 9 mov bl, 1 ; 灰色 mov bh, 0 drawMapLoop: mov al, [si] cmp al, 0 ; 空地 je drawMapEmpty cmp al, 1 ; 墙 je drawMapWall cmp al, 2 ; 箱子 je drawMapBox cmp al, 3 ; 目标点 je drawMapTarget cmp al, 4 ; 人物 je drawMapEmpty ; 人物由 drawPlayer 绘制 inc dh jmp drawMapNext drawMapEmpty: mov ah, 9 mov cx, 1 int 10h inc dh jmp drawMapNext drawMapWall: mov ah, 9 mov cx, 219 ; 方块字符 int 10h inc dh jmp drawMapNext drawMapBox: mov ah, 9 mov cx, 254 ; 实心方块字符 int 10h inc dh jmp drawMapNext drawMapTarget: mov ah, 9 mov cx, 249 ; 圆形字符 int 10h inc dh jmp drawMapNext drawMapNext: inc si cmp dh, 10 jne drawMapLoop mov ah, 9 mov cx, 13 int 10h ; 换行 mov dh, 0 ret drawMap endp ; 绘制玩家 drawPlayer proc mov ah, 9 mov bl, 4 ; 红色 mov dh, [playerY] mov dl, [playerX] add dh, 1 add dl, 1 mov si, offset map mov cx, 10 mul dh dec dh add si, ax add si, dl cmp byte ptr [si], 2 ; 判断玩家是否在箱子上 je drawPlayerBox mov cx, 1 mov al, 1 int 10h ; 绘制玩家 ret drawPlayerBox: mov cx, 254 ; 实心方块字符 mov al, 1 int 10h ; 绘制箱子 mov cx, 4 ; 红色 mov al, 1 int 10h ; 绘制玩家 ret drawPlayer endp ; 移动玩家 movePlayer proc x:word, y:word mov si, offset map mov cx, 10 mul y dec y add si, ax add si, x cmp byte ptr [si], 0 ; 判断目标位置是否为空地 je movePlayerEmpty cmp byte ptr [si], 3 ; 判断目标位置是否为目标点 je movePlayerTarget cmp byte ptr [si], 2 ; 判断目标位置是否为箱子 je movePlayerBox ret movePlayerEmpty: mov byte ptr [si], 4 ; 将人物移动到目标位置 mov si, offset map mov cx, 10 mul [playerY] dec [playerY] add si, ax add si, [playerX] mov byte ptr [si], 0 ; 将原位置变为空地 mov [playerX], x ; 更新人物坐标 mov [playerY], y ret movePlayerBox: mov si, offset map mov cx, 10 mul y dec y add si, ax add si, x cmp byte ptr [si], 0 ; 判断箱子目标位置是否为空地 je movePlayerBoxEmpty cmp byte ptr [si], 3 ; 判断箱子目标位置是否为目标点 je movePlayerBoxTarget ret movePlayerBoxEmpty: mov byte ptr [si], 2 ; 将箱子移动到目标位置 mov si, offset map mov cx, 10 mul [playerY] dec [playerY] add si, ax add si, [playerX] mov byte ptr [si], 4 ; 将人物移动到原箱子位置 mov si, offset map mov cx, 10 mul y dec y add si, ax add si, x mov byte ptr [si], 0 ; 将原人物位置变为空地 mov [playerX], x ; 更新人物坐标 mov [playerY], y ret movePlayerBoxTarget: mov byte ptr [si], 2 ; 将箱子移动到目标位置 mov si, offset map mov cx, 10 mul [playerY] dec [playerY] add si, ax add si, [playerX] mov byte ptr [si], 4 ; 将人物移动到原箱子位置 mov si, offset map mov cx, 10 mul y dec y add si, ax add si, x mov byte ptr [si], 3 ; 将原目标点变为箱子 mov [playerX], x ; 更新人物坐标 mov [playerY], y ret movePlayerTarget: mov byte ptr [si], 4 ; 将人物移动到目标点 mov si, offset map mov cx, 10 mul [playerY] dec [playerY] add si, ax add si, [playerX] mov byte ptr [si], 0 ; 将原位置变为空地 mov [playerX], x ; 更新人物坐标 mov [playerY], y ret movePlayer endp end main ``` 这只是一个简单的实现示例,实际上还需要按照具体的需求进行功能扩展和优化,例如添加计分、关卡、音效等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值