New AI Action: Proximity trigger

Hey guys!

Got a neat script I wanted to share in case it is of use to anybody else:

AI Action - Proximity Trigger

This action can be used to give an enemy an area of 'awareness' that can be used to trigger other enemy actions.
So for example, A bat attached to the roof will stay still until you walk underneath it, or an enemy will rush toward you if you get too close to it.
I am sure others could figure out even better uses for it.

Sadly, I am not that great of an ASM programmer, so there are some small bugs:
The horizontal checking distance does not match on the left or right. I am not sure why, but the range on the right extends slightly further.
The Vertical checking distance is only 1 way. I tried to make it check above the enemy as well, but have not had luck so far. However I feel that is the least likely case to run into: to detect the player within a certain distance above an enemy. It also is less sensitive in the vertical checking than horizontal. It seems you can dip into the vertical range for a moment without triggering it.

The code has 2 versions:
Check within a rectangle sideways and below:
Default setup

Horizontal checking only:
Requires you to comment out one small code segment. When you change it to this mode it will always trigger when the player enters the horizontal range, no matter how far above or below they are.

Instructions on using both modes are included in the script.

Thanks again to Mugi for helping me make the script!

As I said before, I am sure there are much better ASM programmers here, so if any of you have any suggestions let me know.
I'll provide it two ways:
You can read the code here:
Code:
;; AI Proximity Trigger Script:
;; Transition to another state when player comes near
;; By: Chronicler of Legends, help from Mugi
;; NEEDS 2 USER CONSTANTS:
 ; ENEMY_HPROXIMITY - Distance to trigger when the player enters Horizontally
 ; ENEMY_VPROXIMITY - Distance to trigger when the player enters Vertically

LDY player1_object ;Get the player object in Y

;; Test to see how far the player is from this monster

 ; Horizontal Test (LEFT AND RIGHT)
 SEC ;Set Carry
 LDA Object_x_hi,x  ;Get the enemy's origin
 SBC Object_x_hi,y  ;Subtract player's origin
 BCS AIPT_DistanceIsPositive
 EOR #$FF     ; C flag is clear here,
 ADC #$01     ; form two's complement
AIPT_DistanceIsPositive:
 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_HPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC AIPT_insideHProximity
 
 JMP aiProximityNotTriggered

AIPT_insideHProximity:
 ; Vertical Test (IF YOU ONLY WANT TO TEST HORIZONTAL COMMENT EVERYTHING OUT BETWEEN HERE AND ------
 ; This checks only for the player BELOW the enemy. Comment this out and use the other for
 ; 4-way checking
 LDA Object_y_hi,y  ;Get the player's origin
 SBC Object_y_hi,x  ;Subtract enemy's origin

 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_VPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC aiTriggerProximity
 
 JMP aiProximityNotTriggered
;--------------------------

JMP aiTriggerProximity

aiTriggerProximity:
ChangeObjectState #$01, #$00

aiProximityNotTriggered:
RTS

Download and screenshot included as well
 

Attachments

  • aiAction_ProximityTrigger.zip
    856 bytes · Views: 137
  • ProximityScript.gif
    ProximityScript.gif
    298.7 KB · Views: 4,128

Kasumi

New member
An explanation for why left and right have different ranges might be that object width is not taken into account.
jItzHoL.png

Suppose the collision point checked is the top left of the object. (Marked by the orange arrows.) If the objects are the same width (the top example), the distance will be the same for left/right. (Yellow is the range checked.)
If the objects are not the same width (bottom example), this breaks down. The orange arrows are the same distance apart in both of the bottom examples, but visually the purple object is way more inside the red object in the case on the right.

Ideally you'd be doing a distance check from each object's middle point (which would make width not matter, or at least, only matter for 1 pixel based on if the widths are even or odd).

NES Maker actually has a lot of position state. The difference between Object_left and Object_right should be the width of the bounding box. Dividing that by two (lsr) should get half the width. Adding half the width (the result of that lsr) to Object_left should get how far the object's midpoint is from Object_x_hi. Adding Object_x_hi to that gets the actual mid point.

At least, I think. NES Maker itself does this in one part of the code.
 

Mugi

Member
Oh nice you got it running!
I might actually even use that, it seems to be a handy thing to have.

And i totally didnt think about the fact that the hitbox checks are done to the top left of the hitbox.... *facepalm*
 
Trying to improve the scripts accuracy by getting the player and enemy's midpoint, but having some trouble.

Here is what I have so far modifying the script:

Code:
;; AI Proximity Trigger Script:
;; Transition to another state when player comes near
;; By: Chronicler of Legends, help from Mugi
;; NEEDS 2 USER CONSTANTS:
 ; ENEMY_HPROXIMITY - Distance to trigger when the player enters Horizontally
 ; ENEMY_VPROXIMITY - Distance to trigger when the player enters Vertically

LDY player1_object ;Get the player object in Y

;; Test to see how far the player is from this monster

 ; Horizontal Test (LEFT AND RIGHT)
 
 ; !!! NEW CODE HERE !!!
 ; Get the Enemy and Player's Horizontal midpoints
 LDA Object_right,x  ;Get the enemy's Horizontal Midpoint and store in temp1
 SEC
 SBC Object_left,x
 LSR
 STA temp1
 LDA Object_right,y  ;Get the player's Horizontal Midpoint and store in temp2
 SEC
 SBC Object_left,y
 LSR
 STA temp2
 
 SEC ;Set Carry
 ;LDA Object_x_hi,x  ;Get the enemy's top-left corner
 ;SBC Object_x_hi,y  ;Subtract player's origin
 LDA temp1 ; Subtract the player's midpoint from enemy's
 SBC temp2
 BCS AIPT_DistanceIsPositive
 EOR #$FF     ; C flag is clear here,
 ADC #$01     ; form two's complement
AIPT_DistanceIsPositive:
 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_HPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC AIPT_insideHProximity
 
 JMP aiProximityNotTriggered

AIPT_insideHProximity:
 ; Vertical Test (IF YOU ONLY WANT TO TEST HORIZONTAL COMMENT EVERYTHING OUT BETWEEN HERE AND ------
 ; This checks only for the player BELOW the enemy. Comment this out and use the other for
 ; 4-way checking
 LDA Object_y_hi,y  ;Get the player's origin
 SBC Object_y_hi,x  ;Subtract enemy's origin

 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_VPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC aiTriggerProximity
 
 JMP aiProximityNotTriggered
;--------------------------

JMP aiTriggerProximity

aiTriggerProximity:
ChangeObjectState #$01, #$00

aiProximityNotTriggered:
RTS

This doesn't seem to function the way I wanted, the check instantly comes out true, and the enemy gets triggered.
 
Fixed it not responding, but I must still be calculating this wrong.
the right side still detects further by about 1 full tile.

Code:
;; AI Proximity Trigger Script:
;; Transition to another state when player comes near
;; By: Chronicler of Legends, help from Mugi
;; NEEDS 2 USER CONSTANTS:
 ; ENEMY_HPROXIMITY - Distance to trigger when the player enters Horizontally
 ; ENEMY_VPROXIMITY - Distance to trigger when the player enters Vertically

LDY player1_object ;Get the player object in Y

;; Test to see how far the player is from this monster

 ; Horizontal Test (LEFT AND RIGHT)
 
 ; Get the Enemy and Player's Horizontal midpoints
 LDA Object_right,x  ;Get the enemy's Horizontal Midpoint and store in temp1
 SEC
 SBC Object_left,x
 LSR
 ADC Object_x_hi,x
 STA temp1
 LDA Object_right,y  ;Get the player's Horizontal Midpoint and store in temp2
 SEC
 SBC Object_left,y
 LSR
 ADC Object_x_hi,y
 STA temp2
 
 SEC ;Set Carry
 ;LDA Object_x_hi,x  ;Get the enemy's top-left corner
 ;SBC Object_x_hi,y  ;Subtract player's origin
 LDA temp1 ; Subtract the player's midpoint from enemy's
 SBC temp2
 BCS AIPT_DistanceIsPositive
 EOR #$FF     ; C flag is clear here,
 ADC #$01     ; form two's complement
AIPT_DistanceIsPositive:
 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_HPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC AIPT_insideHProximity
 
 JMP aiProximityNotTriggered

AIPT_insideHProximity:
 ; Vertical Test (IF YOU ONLY WANT TO TEST HORIZONTAL COMMENT EVERYTHING OUT BETWEEN HERE AND ------
 ; This checks only for the player BELOW the enemy. Comment this out and use the other for
 ; 4-way checking
 LDA Object_y_hi,y  ;Get the player's origin
 SBC Object_y_hi,x  ;Subtract enemy's origin

 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_VPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC aiTriggerProximity
 
 JMP aiProximityNotTriggered
;--------------------------

JMP aiTriggerProximity

aiTriggerProximity:
ChangeObjectState #$01, #$00

aiProximityNotTriggered:
RTS

EDIT:
I rethought the logic behind it, and I think the Object_right, Object_left, etc are offsets from the objects position used to determine the object's bounding box.
I add the objects position plus its offset to find the position of the left and right side of the bounding box, and find the midpoint between those two.
I do this for both objects and then do the comparison like normal, but no luck.

Here is the code after edit:
Code:
;; AI Proximity Trigger Script:
;; Transition to another state when player comes near
;; By: Chronicler of Legends, help from Mugi
;; NEEDS 2 USER CONSTANTS:
 ; ENEMY_HPROXIMITY - Distance to trigger when the player enters Horizontally
 ; ENEMY_VPROXIMITY - Distance to trigger when the player enters Vertically

LDY player1_object ;Get the player object in Y

;; Test to see how far the player is from this monster

 ; Horizontal Test (LEFT AND RIGHT)
 
 ; Get the Enemy and Player's Horizontal midpoints
 LDA Object_x_hi,x ;Get the enemy's right side
 CLC
 ADC Object_right,x  
 CLC
 STA temp1
 
 LDA Object_x_hi,x ;Get the enemy's left side
 CLC
 ADC Object_left,x
 CLC
 STA temp2
 
 LDA temp1 ; find the midpoint and store in temp1
 SEC
 SBC temp2
 LSR
 STA temp1
 
 LDA Object_x_hi,y ;Get the player's right side
 CLC
 ADC Object_right,y
 CLC
 STA temp2
 
 LDA Object_x_hi,y ;Get the player's left side
 CLC
 ADC Object_left,y
 CLC
 STA temp3
 
 LDA temp2 ;find the midpoint and store in temp1
 SEC
 SBC temp3
 LSR
 STA temp2
 
 LDA #$00 ;reset temp3
 STA temp3
 
 ; Get the Enemy and Player's Horizontal midpoints
 ;LDA Object_right,x  ;Get the enemy's Horizontal Midpoint and store in temp1
 ;SEC
 ;SBC Object_left,x
 ;LSR
 ;ADC Object_x_hi,x
 ;STA temp1
 ;LDA Object_right,y  ;Get the player's Horizontal Midpoint and store in temp2
 ;SEC
 ;SBC Object_left,y
 ;LSR
 ;ADC Object_x_hi,y
 ;STA temp2
 
 SEC ;Set Carry
 ;LDA Object_x_hi,x  ;Get the enemy's top-left corner
 ;SBC Object_x_hi,y  ;Subtract player's origin
 LDA temp1 ; Subtract the player's midpoint from enemy's
 SBC temp2
 BCS AIPT_DistanceIsPositive
 EOR #$FF     ; C flag is clear here,
 ADC #$01     ; form two's complement
AIPT_DistanceIsPositive:
 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_HPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC AIPT_insideHProximity
 
 JMP aiProximityNotTriggered

AIPT_insideHProximity:
 ; Vertical Test (IF YOU ONLY WANT TO TEST HORIZONTAL COMMENT EVERYTHING OUT BETWEEN HERE AND ------
 ; This checks only for the player BELOW the enemy. Comment this out and use the other for
 ; 4-way checking
 LDA Object_y_hi,y  ;Get the player's origin
 SBC Object_y_hi,x  ;Subtract enemy's origin

 ; Now you have a number in the accumulator, which shows distance to player
 CMP #ENEMY_VPROXIMITY ;;This checks horizontal proximity to player in decimal value
 BCC aiTriggerProximity
 
 JMP aiProximityNotTriggered
;--------------------------

JMP aiTriggerProximity

aiTriggerProximity:
ChangeObjectState #$01, #$00

aiProximityNotTriggered:
RTS
 

AllDarnDavey

Active member
I'm having trouble getting any version of this to work (probably user error).

Am I reading how it works right? Create 2 User Constants for X and Y range, set monster to use this AI action, and monster should switch to Action 01 when player enters range?
What are good default values for ENEMY_HPROXIMITY, and ENEMY_VPROXIMITY? Maybe I'm just setting them too high or too low.
 

AllDarnDavey

Active member
Thanks! That helped, although I think my main problem was I had the EndAction set to Loop instead of Repeat.
 

DanielT1985

Member
Is there a way to make a script that moves toward the player? Like similar to the "Move random 8 direction, 4 direction or LR" code, but it's main focus is moving toward where the player is.
 

AllDarnDavey

Active member
Here's one:
http://nesmakers.com/viewtopic.php?p=13776#p13776

It's only left and right for platform games, would need some extra work to make work in 4 directions for top down games. It pairs well with the proximity trigger.
 

DanielT1985

Member
AllDarnDavey said:
Here's one:
http://nesmakers.com/viewtopic.php?p=13776#p13776

It's only left and right for platform games, would need some extra work to make work in 4 directions for top down games. It pairs well with the proximity trigger.

Well what I'm working on is a platformer game, so this works well for me. Thank you for this.
 
AllDarnDavey said:
Here's one:
http://nesmakers.com/viewtopic.php?p=13776#p13776

It's only left and right for platform games, would need some extra work to make work in 4 directions for top down games. It pairs well with the proximity trigger.
I am trying to make an adventure game, not a platform game. Know anyone who might be able to help me make one that works in 4 or 8 directions? I suck at assembly, so It would really be nice to have some help.
 

dale_coop

Moderator
Staff member
This proximity script when triggered doesn't move to the next step... it actually always moves to Action Step 1. So, you need to assign that script to action step 0 (with a "repeat" end of timer and a proper timer value... to verify the player's proximity, regularly).
Could it be your issue?
 
Top Bottom