Collision detection in TASM asm

51 Views Asked by At

The algo im trying to implement is https://gbdev.io/gb-asm-tutorial/part3/collision.html

This is the code to get the Centers of Both the apple and the snake

    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

this is the code fore the collision testing:

 
    
    
    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

For some reason this code only works when the x of the apple is divisible by 5 and the y of the apple is divisible by 5 although the algo should work for cases that is it not on those axiss.

after looking thru a debugger i coudnt find the reason, the collision was working the rnds were working and the centers were working.

My full code:

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```


1

There are 1 best solutions below

3
Sep Roland On
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

Your code "to get the Centers of Both the apple and the snake" is wrong because you're still halving the coordinates instead of the sizes! I already reported this in my previous answer. You did correct the mixing of X's and Y's though.

Both your Square and your Apple have the same size. You have set it at 5.
Next is what your square and apple look like. I have marked the center of the square by 'S' and the center of the apple by 'A':

          Xsquare                 Xapple
          |                       |
          v                       v
Ysquare ->.*****         Yapple ->.*****
           *****                   *****
           **S** <- Ysquare + 2    **A** <- Yapple + 2
           *****                   *****
           *****                   *****
             ^                       ^
             |                       |
             Xsquare + 3             Xapple + 3

This is how you calculate the centers:

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

For there to be a collision in the X direction, the difference between the X coordinates has to be smaller than 5. The same goes for the difference in the Y direction. And both conditions must be true at the same time.

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