如何在emu8086贪吃蛇游戏中检测指定内存位置实现蛇身增长?
问题描述
我用emu8086编写了一个贪吃蛇汇编游戏,代码如下:
org 100h jmp start s_size equ 7 snake dw s_size dup(0) tail dw ? left equ 4bh right equ 4dh up equ 48h down equ 50h cur_dir db right wait_time dw 0 msg1 db " Snake game", 0dh,0ah,0ah db " Yilani on oklari ile kontrol et.", 0dh,0ah,0ah db " Duvarlara degersen yanarsin!!!", 0dh,0ah, 0ah db " Esc programi sonlandirir.", 0dh,0ah,0ah,0ah db " Baslamak icin bir tusa bas.$" msg3 db "", 0dh,0ah, 0ah,0ah, 0ah,0ah,0ah, 0ah,0ah,0ah, 0ah,0ah,0ah, 0ah,0ah, 0ah, 0ah, 0ah, db " !!GAME OVER!!", 0dh,0ah,0ah, 0ah, 0ah, 0ah,0ah, 0ah,0ah, db " $" start: ; print welcome message: mov dx, offset msg1 mov ah, 9 int 21h ; wait for any key: mov ah, 00h int 16h mov ax, 0xB800 ; Video memory segment mov es, ax ; Set ES segment to video memory es: mov [09b4h],'?' es: mov [0848h],'0' es: mov [06b0h],'0' es: mov [01E8h],'0' ; hide text cursor: mov ah, 1 mov ch, 2bh mov cl, 0bh int 10h game_loop: ; === select first video page mov al, 0 mov ah, 05h int 10h mov dx, snake[0] ; set cursor at dl,dh mov ah, 02h int 10h ; print 'o' at the location: mov al, 'o' mov ah, 09h mov bl, 0eh mov cx, 1 int 10h ; === keep the tail: mov ax, snake[s_size * 2 - 2] mov tail, ax call move_snake ; === hide old tail: mov dx, tail ; set cursor at dl,dh mov ah, 02h int 10h ; print '.' at the old location: mov al, ' ' mov ah, 09h mov bl, 0eh mov cx, 1 int 10h check_for_key: ; === check for player commands: mov ah, 01h int 16h jz no_key mov ah, 00h int 16h cmp al, 1bh ; esc - key? je stop_game mov cur_dir, ah no_key: ; === wait a few moments here: mov ah, 00h int 1ah cmp dx, wait_time jb check_for_key add dx, 4 mov wait_time, dx ; === eternal game loop: jmp game_loop stop_game: ; show cursor back: mov ah, 1 mov ch, 0bh mov cl, 0bh int 10h ret move_snake proc near ; set es to bios info segment: mov ax, 40h mov es, ax ; point di to tail mov di, s_size * 2 - 2 ; move all body parts mov cx, s_size-1 move_array: mov ax, snake[di-2] mov snake[di], ax sub di, 2 loop move_array cmp cur_dir, left je move_left cmp cur_dir, right je move_right cmp cur_dir, up je move_up cmp cur_dir, down je move_down jmp stop_move move_left: mov al, b.snake[0] dec al mov b.snake[0], al cmp al, -1 jne stop_move mov al, es:[4ah] ; col number. dec al call game_over jmp stop_move move_right: mov al, b.snake[0] inc al mov b.snake[0], al cmp al, es:[09b4h] cmp al, es:[4ah] ; col number. jb stop_move call game_over jmp stop_move move_up: mov al, b.snake[1] dec al mov b.snake[1], al cmp al, -1 jne stop_move mov al, es:[84h] ; row number -1. call game_over jmp stop_move move_down: mov al, b.snake[1] inc al mov b.snake[1], al cmp al, es:[84h] ; row number -1. jbe stop_move call game_over jmp stop_move stop_move: ret grow_snake: ret game_over: mov dx, offset msg3 mov ah, 9 int 21h ; wait for any key: mov ah, 00h int 16h move_snake endp
我想让蛇头碰到视频内存中预先放置食物的09b4h、0848h、06b0h、01E8h位置时实现蛇身增长,之前尝试在stop_game里调用grow_snake、递增s_size都没成功,请问该如何检测蛇是否到达指定内存位置,并实现蛇身增长?
解决方案
一、核心问题分析
你之前的失败原因有两个:
s_size是用equ定义的常量,无法在运行时修改;- 没有把蛇头的坐标转换成视频内存偏移地址,无法和食物地址做有效对比。
二、步骤1:修改数据段定义
把常量s_size改成变量,同时给蛇数组预留足够空间,避免增长时溢出:
org 100h jmp start s_size dw 7 ; 从equ常量改为dw变量 snake dw 100 dup(0) ; 预留100个位置,足够蛇身增长 tail dw ? ; 其余常量、变量定义不变
三、步骤2:实现蛇头与食物的碰撞检测
视频内存中每个字符占2字节(1字节字符+1字节属性),需要把蛇头的DH(行)、DL(列)坐标转换成对应偏移地址,再和食物地址对比:
; 新增检测食物的子程序,调用前DX需存入蛇头坐标(DH=行,DL=列) check_food proc near mov ax, dx mov cl, 80 mul cl ; AX = DH * 80(一行80列) add al, dl adc ah, 0 ; AX = DH*80 + DL(坐标转成线性位置) shl ax, 1 ; AX = (DH*80+DL)*2(转成视频内存偏移) ; 依次对比四个食物地址 cmp ax, 09b4h je eat_food cmp ax, 0848h je eat_food cmp ax, 06b0h je eat_food cmp ax, 01E8h je eat_food jmp no_food eat_food: ; 清除已吃掉的食物(视频内存设为空格) mov bx, ax mov ax, 0B800h mov es, ax mov byte ptr es:[bx], ' ' ; 蛇身增长:s_size加1 inc word ptr s_size mov tail, 0 ; 标记不需要隐藏旧尾巴 jmp end_check no_food: ; 记录尾巴位置,后续需要隐藏 mov ax, snake[word ptr s_size*2 - 2] mov tail, ax end_check: ret check_food endp
四、步骤3:修改游戏主循环流程
在移动蛇之后调用碰撞检测,根据检测结果决定是否隐藏旧尾巴:
game_loop: ; ... 原有设置光标、打印蛇头的代码不变 call move_snake call check_food ; 新增:检测是否吃到食物 ; 只有没吃到食物时才隐藏旧尾巴 cmp tail, 0 je skip_hide_tail ; === hide old tail: mov dx, tail mov ah, 02h int 10h mov al, ' ' mov ah, 09h mov bl, 0eh mov cx, 1 int 10h skip_hide_tail: ; ... 原有按键检测、等待代码不变
五、步骤4:调整move_snake子程序的循环逻辑
因为s_size现在是变量,循环次数需要动态获取:
move_snake proc near mov ax, 40h mov es, ax ; 循环次数 = s_size - 1 mov cx, word ptr s_size dec cx mov di, word ptr s_size * 2 - 2 move_array: mov ax, snake[di-2] mov snake[di], ax sub di, 2 loop move_array ; ... 原有方向移动代码不变 move_snake endp
内容的提问来源于stack exchange,提问作者MuratSklzn
相关产品推荐
相关产品推荐

