• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Solved] Preventing a unit from moving too far away.

Status
Not open for further replies.
Level 2
Joined
Nov 17, 2016
Messages
9
Hey there. I'm currently working on a JoJo's Bizarre Adventure map. I'm currently trying to create a trigger which essentially prevents a unit from moving too far away from another unit, to emulate these things called 'Stands' from Jojo. I'm currently not having much success, however.

Could I please get some help?
 

Attachments

  • limit stand range.png
    limit stand range.png
    17.1 KB · Views: 75

Rheiko

Spell Reviewer
Level 27
Joined
Aug 27, 2013
Messages
4,215
You can check the distance between a unit using if blocks. If the distance between the point of issued order and the center point of region is greater than x, order the unit to stop. But the flaw is it won't detect auto-attack or anything that moves your unit without giving it an order to move.

Or you can just check the position of the unit using periodical timer.
 
Level 2
Joined
Nov 17, 2016
Messages
9
Thanks for the reply. I don't really like using periodical timers if I can avoid it, but I think that I'm not going to be able to check it any other way. One idea I had was that I had a region that would move to the Stand user and check if the Stand exits the region. This isn't a particularly elegant solution, however, if there is multiple stand users on the same map.

How would you do a periodical timer check?
 
Level 25
Joined
Oct 18, 2008
Messages
945
you can't order (matching unit) to do anything. change to (picked unit)

once you've fixed that it'll crash because it orders the unit and triggers itself. also you can get around this by giving it non point-targeted commands that cause it to move like attack or a spell.

you need to do
turn off (this trigger)
order unit to do things
turn on (this trigger)

it seems to me that a simpler way would be to periodically check the distance between them and change the unit's location if it's too much. it's how slark's pounce leash is coded in dota.
 
Level 2
Joined
Nov 17, 2016
Messages
9
Thanks for the extra replies. After a long time of looking at it, I decided to try out some hashtables.


  • Stand Limit
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in (Units of type Stand User) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Number of units in (Units within 125.00 of (Position of (Picked unit)) matching ((Unit-type of (Matching unit)) Equal to Short Range Stand))) Equal to 0
            • Then - Actions
              • Unit - Move (Load 2 of (Custom value of (Picked unit)) in StandUsers) instantly to (Position of (Picked unit))
            • Else - Actions
              • Do nothing
It works. And now I have a hashtable where I can refer to specific stand users and stands. Thanks guys.
 
Last edited:
You should store the heroes into variables and directly check for their distance.

"(Number of units in (Units within 125.00 of (Position of (Picked unit)) matching ((Unit-type of (Matching unit)) Equal to Short Range Stand))) Equal to 0"

^That's a huge and bad workaround for it.

Also, in case you don't know about memory leaks yet, read this.Things That Leak
Memory leaks should be prevented.
 
Level 25
Joined
Oct 18, 2008
Messages
945
if you make it like this the unit doesn't get its orders interrupted by the movement and also stays at the edge of allowed range instead of going to the middle, if you feel that'd be more user friendly. I yanked this out of my map where I have a similar thing.

  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Distance between TempPoint and MTempPoint[1]) Less than or equal to 420.00
      • Then - Actions
      • Else - Actions
        • Set MTempPoint[2] = (TempPoint offset by 420.00 towards (Angle from TempPoint to MTempPoint[1]) degrees)
        • Set X = (X of MTempPoint[2])
        • Set Y = (Y of MTempPoint[2])
        • Custom script: call SetUnitX(udg_TempUnit, udg_X)
        • Custom script: call SetUnitY(udg_TempUnit, udg_Y)
also instead of that mess with checking amounts of nearby units which would allow one to go outside the range if two were initially in the same area, you can make the linked ones refer to each other individually by using a hashtable.

  • Hashtable - Save Handle Of(Triggering unit) as (Key lolwhatever) of (Key (Casting unit)) in Hashtablevariable
  • Set TempUnit = (Load (Key lolwhatever) of (Key (Picked unit)) in Hashtablevariable)
first you'll need to create a hashtable, which is this simple:

  • Events
    • Time - Elapsed game time is 0.01 seconds
  • Conditions
  • Actions
    • Hashtable - Create a hashtable
    • Set Hashtablevariable = (Last created hashtable)
What comparison do I use to check the distance between the two units? Is it an integer comparison?

It's a real. Integers are numbers without decimals.
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,287
You should store the heroes into variables and directly check for their distance.

"(Number of units in (Units within 125.00 of (Position of (Picked unit)) matching ((Unit-type of (Matching unit)) Equal to Short Range Stand))) Equal to 0"

^That's a huge and bad workaround for it.
This is not necessarily true with all cases. Area searches use quad trees which scale a lot better than performing a linear test for distance to check for all objects. If he is mapping stands to heroes then testing distance directly between the stands and the heroes is probably faster. However if he wants to test if a hero is not near any of a hundred or more stands on the map then an area search will be faster as it scales better.
 
Level 2
Joined
Nov 17, 2016
Messages
9
Thanks for the help, doom_sheep. This is what I ended up creating:
  • Create Hashtables
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set StandUsers = (Last created hashtable)
Hashtable.

  • Assign User
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • (Unit-type of (Entering unit)) Equal to Stand User
    • Actions
      • Set StandUserCount = (StandUserCount + 1)
      • Unit - Set the custom value of (Entering unit) to StandUserCount
      • Hashtable - Save Handle Of(Entering unit) as 1 of (Custom value of (Entering unit)) in StandUsers
Saving a stand user into a hashtable. I didn't quite understand how the key (lolwhatever) thing worked, so I decided to stick with what I know. Is there anything inherently wrong with approaching it this way?

  • Assign Stand
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • (Unit-type of (Entering unit)) Equal to Short Range Stand
    • Actions
      • Set Group = (Units owned by (Owner of (Entering unit)) of type Stand User)
      • Unit Group - Pick every unit in Group and do (Actions)
        • Loop - Actions
          • Hashtable - Save Handle Of(Entering unit) as 2 of (Custom value of (Picked unit)) in StandUsers
      • Custom script: call DestroyGroup (udg_Group)
Assigning a stand to a player's specific stand user. Also my first attempt at preventing memory leaks.

  • Stand Limit
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Set Distance = (Units of type Stand User)
      • Unit Group - Pick every unit in Distance and do (Actions)
        • Loop - Actions
          • Set UnitPoint1 = (Position of (Picked unit))
          • Set UnitPoint2 = (Position of (Load 2 of (Custom value of (Picked unit)) in StandUsers))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Distance between UnitPoint1 and UnitPoint2) Greater than or equal to 200.00
            • Then - Actions
              • Set UnitPoint3 = (UnitPoint1 offset by 199.00 towards (Angle from UnitPoint1 to UnitPoint2) degrees)
              • Unit - Move (Load 2 of (Custom value of (Picked unit)) in StandUsers) instantly to UnitPoint3
            • Else - Actions
      • Custom script: call RemoveLocation (udg_UnitPoint1)
      • Custom script: call RemoveLocation (udg_UnitPoint2)
      • Custom script: call RemoveLocation (udg_UnitPoint3)
      • Custom script: call DestroyGroup (udg_Distance)
This is my attempt at replicating your trigger. I noticed that when I had the offset at 200, the stand sometimes got stuck and was unable to move. By setting it at 199, it allows the stand to move freely within the user's range. It works pretty much exactly the way I need it to.

Does this look okay?
 
Last edited:
Level 25
Joined
Oct 18, 2008
Messages
945
  • Hashtable - Save Handle Of(Triggering unit) as (Key lolwhatever) of (Key (Casting unit)) in Hashtablevariable
triggering unit = value that is being saved
lolwhatever = name that i am giving to value to refer to it later with
casting unit = unit that the value is saved on

  • Set TempUnit = (Load (Key lolwhatever) of (Key (Picked unit)) in Hashtablevariable)
setting tempunit is just an action that i chose because it wants a unit
lolwhatever = name of value that I am looking for that I would have saved in the other trigger
picked unit = unit the value is saved on

hashtable variable will be the same in every line in your map. I don't know if there's a reason you'd ever need to have more than one hashtable variable in a map. but you need one so the map knows you want to have hashtable values.

--

  • Set X = (X of MTempPoint[2])
  • Set Y = (Y of MTempPoint[2])
  • Custom script: call SetUnitX(udg_TempUnit, udg_X)
  • Custom script: call SetUnitY(udg_TempUnit, udg_Y)
this is what's known as smooth movement. it moves "Tempunit" to to "MTempPoint[2]" without interrupting its orders. X and Y are real variables. the four lines achieve basicaly the same as the normal move unit action, but you have to do it like this because GUI only has the version of unit movement that also makes the unit forget all its orders.
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Set Distance = (Units of type Stand User)
This is really bad.
First of all, the underlying Blizzard.j function is leaking, which in this trigger gets executed 20 times a second, which will result in the group not cleaned properly, which is a pain for your memory.
Secondly, as it runs 20 times a second, you should have this as a global group where newly created units of type "Stand User" are added to and removed when they die.

The algorithm behind the linking between the Stand User and Short Range Stand tells me that you only want it to work with 1 stand per player.
In which case, I can recommend you to use an array of units instead using the player id as key.
If it is not the case, then just delete those triggers... there is nothing useful in it... well, except the distance check and the new location calculation.
 
Level 2
Joined
Nov 17, 2016
Messages
9
Ahh, I get it now, doom_sheep. It's far less convoluted than using the custom value of a unit, and allows you to save multiple different bits of information onto a single unit. That's significantly better than what I was doing before.

  • Stand Limit
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in StandUserGroup and do (Actions)
        • Loop - Actions
          • Set TempUnit = (Load (Key stand) of (Key (Picked unit)) in Hasttable)
          • Set UnitPoint1 = (Position of (Picked unit))
          • Set UnitPoint2 = (Position of TempUnit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Distance between UnitPoint1 and UnitPoint2) Less than or equal to 200.00
            • Then - Actions
            • Else - Actions
              • Set UnitPoint3 = (UnitPoint1 offset by 200.00 towards (Angle from UnitPoint1 to UnitPoint2) degrees)
              • Set X = (X of UnitPoint3)
              • Set Y = (Y of UnitPoint3)
              • Custom script: call SetUnitX(udg_TempUnit, udg_X)
              • Custom script: call SetUnitY(udg_TempUnit, udg_Y)
          • Custom script: call RemoveLocation (udg_UnitPoint1)
          • Custom script: call RemoveLocation (udg_UnitPoint2)
          • Custom script: call RemoveLocation (udg_UnitPoint3)
It works precisely the way you said it would. The movement is smooth and the unit's orders aren't canceled. Stand users now have their own static group as well. Thanks a lot for the advice/feedback, everyone.
 
Status
Not open for further replies.
Top