我尝试实现的算法是https://gbdev.io/gb-asm-tutorial/part3/collision.html
这是获取苹果和蛇的中心的代码
shr bx,1
mov [CenterSquare_X], bx
mov bx, [Y]
shr bx,1
mov [CenterSquare_Y], bx
mov bx, [Xapple]
shr bx,1
mov [CenterApple_X], bx
mov bx, [Yapple]
shr bx,1
mov [CenterApple_Y], bx
call check_collision
这是碰撞测试之前的代码:
mov ax, [CenterSquare_X]
sub ax, [CenterApple_X]
jns DeltaX
neg ax
DeltaX:
cmp ax, [SnakeBody]
jnb NoCollision
mov ax, [CenterSquare_Y]
sub ax, [CenterApple_Y]
jns DeltaY ; Jump short if not sign (SF=0)
neg ax
DeltaY:
cmp ax, [SnakeBody]
jnb NoCollision ; Jump short if not below
Collision:
CALL rnd_y
call rnd_x
Inc [Player_Score]
cmp [Player_Score], 59
jne score_Show
inc [level]
mov [Player_Score], 49
mov cx, [level]
ADD [SnakeBody], cx
ADD [Speed], cx
score_Show:
call set_cursor
call Score
NoCollision:
RET
ENDP check_collision ; hitbox code
出于某种原因,此代码仅在苹果的 x 可被 5 整除并且苹果的 y 可被 5 整除时才有效,尽管该算法应该适用于不在这些轴上的情况。
通过调试器查看后,我找不到原因,碰撞正在工作,rnds正在工作,中心正在工作。
我的完整代码:
MODEL small
STACK 100h
DATASEG
; --------------------------
Y dw 100 ; of charchter
X dw 100 ; of charchter
TIME_AUX DB 0 ; for waiting in between frames
PressFlag db 0 ; press flag
SnakeBody dw 5 ; body of charcter
Xapple dw 80 ; y of apple
yapple dw 80 ; x of apple
Random_x dw 80 ; my rnds
Random_y dw 80 ; my rnds
Speed dw 5 ; speed of snake
Player_Score DB 48 ; ascii value of 0
level dw 1
SEED dw 11
FlagForRnd dw 1 ; if its RND_y we can use any y value times 5 if its x it can be any value from 150,1
CenterSquare_Y dw 0
CenterSquare_X dw 0
CenterApple_Y dw 0
CenterApple_X dw 0
Diffrence_Y dw 0
Diffrence_X dw 0
; --------------------------
CODESEG
proc set_cursor
MOV AH, 02h
MOV BH, 0
MOV DH, 1 ; Adjust row for the score display
MOV DL, 1 ; Adjust column for the score display
INT 10h
RET
ENDP set_cursor
PROC Score
mov ah, 09h ; Display character function
mov bh, 0 ; Page number
mov cx, 1 ; Number of times to display character
mov al, [byte ptr Random_x] ; Character to display
ADD AL, '0' ; Convert to ASCII
mov bl, [byte ptr level] ; Attribute (text color)
int 10h
MOV AH, 02h
MOV BH, 0
MOV DH, 1 ; Adjust row for the score display
MOV DL, 1 ; Adjust column for the score display
INT 10h
MOV AH, 02h
MOV BH, 0
MOV DH, 1 ; Adjust row for the score displayx
MOV DL, 20 ; Adjust column for the score display y
int 10h
ENDP Score
PROC moves
MOV AH, 01h ; Function 01h - Check for Key Press
INT 16h
JZ exit_moves ; Jump if ZF is set (no key pressed)
MOV AH, 00h ; Function 00h - Read Key Stroke
INT 16h
; Check if AH contains 'W' (ASCII value 87)
CMP AL, 'w'
je w_pressed
; Check if AH contains 'D' (ASCII value 68)
CMP AL, 'd'
je d_pressed
; Check if AH contains 'A' (ASCII value 65)
CMP AL, 'a'
je a_pressed
; Check if AH contains 'S' 'S'
CMP AL, 's'
JMP s_pressed
exit_moves:
RET
ENDP moves
PROC w_pressed
mov [PressFlag], 1
mov cx , [Speed]
SUB [Y], cx
CHECK_TIME1: ;time checking loop
MOV AH,2Ch ;get the system time
INT 21h ;is the current time equal to the previous one(TIME_AUX)?
CMP DL,[TIME_AUX] ;is the current time equal to the previous one(TIME_AUX)?
JE CHECK_TIME1
MOV [TIME_AUX],DL
CALL draw
call moves
CMP [PressFlag],1
je w_pressed
RET
ENDP w_pressed
PROC d_pressed
mov [PressFlag], 2
mov cx , [Speed]
ADD [X], cx
CHECK_TIME2: ;time checking loop
MOV AH,2Ch ;get the system time
INT 21h ;is the current time equal to the previous one(TIME_AUX)?
CMP DL,[TIME_AUX] ;is the current time equal to the previous one(TIME_AUX)?
JE CHECK_TIME2
MOV [TIME_AUX],DL
CALL draw
call moves
cmp [PressFlag],2
je d_pressed
ret
ENDP d_pressed
PROC a_pressed
mov [PressFlag], 3
mov cx , [Speed]
SUB [X], cx
CHECK_TIME3: ;time checking loop
MOV AH,2Ch ;get the system time
INT 21h ;is the current time equal to the previous one(TIME_AUX)?
CMP DL,[TIME_AUX] ;is the current time equal to the previous one(TIME_AUX)?
JE CHECK_TIME3
MOV [TIME_AUX],DL
CALL draw
call moves
CMP [PressFlag],3
je a_pressed
ret
ENDP a_pressed
PROC s_pressed
mov [PressFlag], 4
mov cx , [Speed]
ADD [Y], cx
CHECK_TIME4: ;time checking loop
MOV AH,2Ch ;get the system time
INT 21h ;is the current time equal to the previous one(TIME_AUX)?
CMP DL,[TIME_AUX] ;is the current time equal to the previous one(TIME_AUX)?
JE CHECK_TIME4
MOV [TIME_AUX],DL
CALL draw
call moves
CMP [PressFlag],4
je s_pressed
RET
ENDP s_pressed
PROC draw
MOV AX, 0C07h ; Function 0Ch, Set Pixel Color
MOV BH, 0 ; Page number (usually 0 in mode 13h)
; Draw the gray border
MOV CX, 40 ; X-coordinate of the left border
MOV DX, 49 ; Y-coordinate of the top border
MOV SI, 0
MOV DI, 0
MOV AL, 8 ; Color for gray pixels
draw_row_loop_border:
draw_pixel_loop_border:
INC CX
INC DI
INT 10h
CMP DI, 240 ; Width of the border
JNE draw_pixel_loop_border
SUB CX, 240 ; Reset X-coordinate to start
MOV DI, 0
INC DX
INC SI
CMP SI, 150 ; Height of the border
JNE draw_row_loop_border
MOV AX, 0C07h ; Function 0Ch, Set Pixel Color
MOV BH, 0 ; Page number (usually 0 in mode 13h)
; Draw the apple
MOV CX, [Xapple] ; X-coordinate of the apple
MOV DX, [yapple] ; Y-coordinate of the apple
MOV SI, 0
MOV DI, 0
MOV AL, 4 ; Color (choose a color different from the snake)
draw_row_loop_apple:
draw_pixel_loop_apple:
INC CX
INC DI
INT 10h
CMP DI, [SnakeBody] ; size of the apple must be the same size of the snake for collison to work
JNE draw_pixel_loop_apple
SUB CX, [SnakeBody] ; without this, the apple is not drawing up; it's drawing to the side
MOV DI, 0
INC DX
INC SI
CMP SI, [SnakeBody]; size of the apple must be the same size of the snake for collison to work
JNE draw_row_loop_apple
; Draw the snake
MOV CX, [X] ; X-coordinate of the snake head
MOV DX, [Y] ; Y-coordinate of the snake head
MOV SI, 0
MOV DI, 0
MOV AL, 2 ; Color green (you can choose a different color)
draw_row_loop:
draw_pixel_loop:
INC CX
INC DI
INT 10h
CMP DI, [SnakeBody]
JNE draw_pixel_loop
SUB CX, [SnakeBody]
MOV DI, 0
INC DX
INC SI
CMP SI, [SnakeBody]
JNE draw_row_loop
mov bx, [X]
shr bx,1
mov [CenterSquare_X], bx
mov bx, [Y]
shr bx,1
mov [CenterSquare_Y], bx
mov bx, [Xapple]
shr bx,1
mov [CenterApple_X], bx
mov bx, [Yapple]
shr bx,1
mov [CenterApple_Y], bx
call check_collision
RET
ENDP draw
PROC check_collision ; checks for collison with snake
mov ax, [CenterSquare_X]
sub ax, [CenterApple_X]
jns DeltaX
neg ax
DeltaX:
cmp ax, [SnakeBody]
jnb NoCollision
mov ax, [CenterSquare_Y]
sub ax, [CenterApple_Y]
jns DeltaY ; Jump short if not sign (SF=0)
neg ax
DeltaY:
cmp ax, [SnakeBody]
jnb NoCollision ; Jump short if not below
Collision:
CALL rnd_y
call rnd_x
Inc [Player_Score]
cmp [Player_Score], 59
jne score_Show
inc [level]
mov [Player_Score], 49
mov cx, [level]
ADD [SnakeBody], cx
ADD [Speed], cx
score_Show:
call set_cursor
call Score
NoCollision:
RET
ENDP check_collision ; hitbox code
;Randoms
PROC rand2num1toValue_Y
push dx
push bx
xor dx, dx ; Compute randval(DX) mod 10 to get num
mov bx, 26 ; between 1 and given value
div bx
inc dx ; DX = modulo from division
mov [Random_y], ax
pop bx
pop dx
RET
ENDP rand2num1toValue_Y
PROC rand2num1toValue_X
push dx
push bx
xor dx, dx ; Compute randval(DX) mod 10 to get num
mov bx, 26 ; between 1 and given value
div bx
inc dx ; DX = modulo from division
mov [Random_x], dx
pop bx
pop dx
ret
ENDP rand2num1toValue_X
; Set LCG PRNG seed to system timer ticks
; Inputs: AX = seed
; Modifies: AX
; Return: nothing
Proc srandsystime
xor ax, ax ; Int 1Ah/AH=0 to get system timer in CX:DX
int 1Ah
mov [seed], dx ; seed = 16-bit value from DX
ret
ENDP srandsystime
PROC rand
push dx
mov ax, 25173 ; LCG Multiplier
mul [word ptr seed] ; DX:AX = LCG multiplier * seed
add ax, 13849 ; Add LCG increment value
mov [seed], ax ; Update seed
; AX = (multiplier * seed + increment) mod 65536
pop dx
ret
ENDP rand
Proc rnd_x
call srandsystime ; Seed PRNG with system time, call once only
call rand ; Get a random number in AX
call rand2num1toValue_X
mov [Xapple], ax
ret
ENDP rnd_x
Proc rnd_y
call srandsystime ; Seed PRNG with system time, call once only
call rand ; Get a random number in AX
call rand2num1toValue_Y
mov [yapple], ax
RET
ENDP rnd_y
PROC game_logic
CHECK_TIME: ;time checking loop
MOV AH, 2Ch ;get the system time
INT 21h ;is the current time equal to the previous one(TIME_AUX)?
CMP DL, [TIME_AUX] ;is the current time equal to the previous one(TIME_AUX)?
JE CHECK_TIME ; if it is the same, skip updating the game state
; If it reaches this point, it's because the time has passed
MOV [TIME_AUX], DL ;update time
CALL moves
call draw
cmp [level] , 1
RET
ENDP game_logic
start:
MOV AX, @data
MOV DS, AX
MOV AX, 13h
INT 10h ; Set video mode 13h (320x200 pixels, 256 colors)
game_loop:
CALL game_logic
JMP game_loop
exit:
MOV AX, 4C00h
INT 21h
END start```
mov bx, [X] shr bx,1 mov [CenterSquare_X], bx mov bx, [Y] shr bx,1 mov [CenterSquare_Y], bx mov bx, [Xapple] shr bx,1 mov [CenterApple_X], bx mov bx, [Yapple] shr bx,1 mov [CenterApple_Y], bx
您的代码“获取苹果和蛇的中心”是错误,因为您仍然将坐标减半而不是尺寸!我已经在我的之前的回答中报告了这一点。不过,您确实纠正了 X 和 Y 的混合。
您的 Square 和 Apple 的尺寸相同。您已将其设置为 5。
接下来是你的正方形和苹果的样子。我用“S”标记了正方形的中心,用“A”标记了苹果的中心:
Xsquare Xapple
| |
v v
Ysquare ->.***** Yapple ->.*****
***** *****
**S** <- Ysquare + 2 **A** <- Yapple + 2
***** *****
***** *****
^ ^
| |
Xsquare + 3 Xapple + 3
这就是计算中心的方法:
mov si, [SnakeBody] ; 5 (best to keep this an odd number: 3, 5, 7, ...
shr si, 1 ; 2
mov bx, [X]
lea ax, [bx + si + 1] ; X + 3
mov [CenterSquare_X], ax
mov bx, [Y]
lea ax, [bx + si] ; Y + 2
mov [CenterSquare_Y], ax
mov bx, [Xapple]
lea ax, [bx + si + 1] ; Xapple + 3
mov [CenterApple_X], ax
mov bx, [Yapple]
lea ax, [bx + si] ; Yapple + 2
mov [CenterApple_Y], ax
为了在 X 方向上发生碰撞,X 坐标之间的差必须小于 5。 Y方向的差值也是如此。并且这两个条件必须同时成立。
Xsqr Xapp DifferenceX is 6 -> No collision
| |
v v
.*****.*****
***** *****
**S** **A**
***** *****
***** *****
Xsqr Xapp DifferenceX is 5 -> No collision
| |
v v
.****.*****
**********
**S****A**
**********
**********
XsqrXapp DifferenceX is 4 -> Collision (only if DifferenceY is < 5)
| |
v v
.***.@****
****@****
**S*@*A**
****@****
****@****
PROC check_collision
mov ax, [CenterSquare_X]
sub ax, [CenterApple_X]
jns DeltaX
neg ax
DeltaX:
cmp ax, [SnakeBody]
jnb NoCollision
mov ax, [CenterSquare_Y]
sub ax, [CenterApple_Y]
jns DeltaY
neg ax
DeltaY:
cmp ax, [SnakeBody]
jnb NoCollision
Collision:
... Here you do whatever it is that you need to do
NoCollision:
ret
ENDP check_collision