• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Detecting and moving specific units according to distance from Hero?

Status
Not open for further replies.
Level 2
Joined
Sep 7, 2016
Messages
9
Here's the idea behind what I'm trying to accomplish;

The main hero is a necromancer. After the necro kills a unit, a random undead minion is spawned through triggers. Its important to note these minions are unable to be controlled and are owned by player 2 (an ally of Player 1)

They need to automatically follow the necromancer and attack random targets that are in range of the necromancer, and the minions also need to retreat if the necromancer gets out of range.

Its also important to note that I don't want them to just follow him via patrol command. Their collision is off and will be surrounding the necro in a big circle. With a simple follow command, they'll all stack on each other and it'll look incredibly tacky. I'm looking for a big mob of undead.

I don't need the entirety of how to make all of it work, I just need some building blocks to get this going. I've never been so stumped before.

Actual Question
How can I get my minions to follow my necromancer? And how can I check to make sure they won't fall behind BUT also be able to attack units within range?


gif for fun :thumbs_up:
 

Attachments

  • LOL.gif
    LOL.gif
    12.9 MB · Views: 85
Level 23
Joined
Feb 6, 2014
Messages
2,466
You could order them periodically and code the part whether the zombies will attack or not. That way it is uncontrollable but at the same time, it also belongs to the player who summoned not Player 2.

So, every 0.03125 second, order all zombies to "move" to a location with respect to the summoner's position. You could make it so that each zombie has a different polar angle with respect to the summoner. Example, if their are two zombies, zombie1 will be ordered to move to summoner position with 0 degrees polar offset. Zombie2 will be ordered to move to summoner position with 180 degrees polar offset. If their are 3 zombies, the polar angle difference is 120 degrees and so forth.
At the same time, when picking all enemies within some radius to the summoner and it there are enemies present, order each zombies to "attack" the closest picked enemy.

Basic outline
1. Pick all enemies within range, add them to a unit group (I'll call it DetectedEnemies)
2. If number of units in DetectedEnemies is zero, then order all the zombies to "move" with respect to a certain polar angle and small radius so that they will surround the summoner.
3. If number of units in DetectedEnemies is greater than zero, pick each zombie to attack the nearest unit in DetectedEnemies.
 
Level 2
Joined
Sep 7, 2016
Messages
9
So, every 0.03125 second, order all zombies to "move" to a location with respect to the summoner's position. You could make it so that each zombie has a different polar angle with respect to the summoner. Example, if their are two zombies, zombie1 will be ordered to move to summoner position with 0 degrees polar offset. Zombie2 will be ordered to move to summoner position with 180 degrees polar offset. If their are 3 zombies, the polar angle difference is 120 degrees and so forth.

I should mention that the number of minions is somewhat random. Just like the food system, there is a maximum amount of minions to have at a time. Some cost 5 food, some cost 1. Maybe I can have 6 abominations, or maybe I can have 35 weak zombies. The problem with your suggestion is I can't make them run to a specific spot around the zombie becasue I don't know how many minions I'll have at a time.

If I do the "Move to location of summoner with polar offset" every .03 seconds they're going to constantly jumble around the summoner even when he's still.

Also, when doing a "attack random unit in range" for minion with a periodic timer trigger, they begin to bug out and their animations keep resetting and they go completely wonky.

I like the idea of setting specific polar offsets for each minion, but with how random the number of minions is, it just won't translate well.
 
Last edited:
Level 2
Joined
Sep 7, 2016
Messages
9
I've attached a gif of a previous attempt that I scrapped. The minions follow him, but they just won't stop moving.

minions.gif
 
Level 4
Joined
Jul 26, 2016
Messages
88
You could still use polar offset, by having an array in which you store all your minions. That way you can do like a semi-circle behind your cast


every 6 seconds of game time


for x in 1 to 24 do


if distance between caster and minions[x] > 900
if x <= 12
order minions[x] to move towards position of caster offset towards (-facing of necromancer) - 90degrees + ((pi/12)*x radians) [or 180/12 * x degrees] offset by 230
if x > 12
order minions[x] to move towards position of caster offset towards (-facing of necromancer) - 90degrees + ((pi/12)*x radians) [or 180/12 * (x-12) degrees] offset by 300

if distance between caster and minions[x] <= 900
if x <= 12
order minions[x] to attack-move towards position of caster offset towards (-facing of necromancer) - 90degrees + ((pi/12)*x radians) [or 180/12 * x degrees] offset by 230
if x > 12
order minions[x] to attack-move towards position of caster offset towards (-facing of necromancer) - 90degrees + ((pi/12)*x radians) [or 180/12 * (x-12) degrees] offset by 300


This would allow for behaviour that, if the caster is still nearby, units would remain engaged in combat, (attack-move command, which will retain them aggroed towards nearby enemies),but if the caster is fucking off, (distance > 900) the units will follow him, ignoring remaining enemies (move command). The units will line up in a nice semi-circle behind the caster as they are moving towards him/staying idle.
 
Level 2
Joined
Sep 7, 2016
Messages
9
that sounds pretty effective, I'll give it a shot tomorrow. I'll report back in a bit!
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
I've attached a gif of a previous attempt that I scrapped. The minions follow him, but they just won't stop moving.

View attachment 247725
You could use this code with one modification. Check if the hero has moved appreciably since the last set of orders, and if not then just don't order anything to move.
  • Set HeroPos[2] = Position of (<caster>)
  • If (All conditions are true) then do (actions) else do (actions)
    • If - Conditions
      • (Distance between HeroPos[2] and HeroPos[1]) greater than or equal to <move threshold>
    • Then - Actions
      • ------- remove the previously stored location to prevent memory leaks; if you don't understand this just make sure you put "udg_" before whatever you named the variable in the variable editor-------
      • Custom script: call RemoveLocation(udg_HeroPos[1])
      • Set HeroPos[1] = HeroPos[2]
      • ------- Do your move ordering stuff -------
    • Else - Actions
 
Level 2
Joined
Sep 7, 2016
Messages
9
You could use this code with one modification. Check if the hero has moved appreciably since the last set of orders, and if not then just don't order anything to move.
  • Set HeroPos[2] = Position of (<caster>)
  • If (All conditions are true) then do (actions) else do (actions)
    • If - Conditions
      • (Distance between HeroPos[2] and HeroPos[1]) greater than or equal to <move threshold>
    • Then - Actions
      • ------- remove the previously stored location to prevent memory leaks; if you don't understand this just make sure you put "udg_" before whatever you named the variable in the variable editor-------
      • Custom script: call RemoveLocation(udg_HeroPos[1])
      • Set HeroPos[1] = HeroPos[2]
      • ------- Do your move ordering stuff -------
    • Else - Actions

This seems to be what I'm looking for, and after testing it out it seems to work pretty well. My only concern now is how to get HeroPos[1] set without doing a periodic timer trigger (since that would be a nightmare for memory leaks). Set it when he's issued an order?

What do you mean by checking his last set of orders?
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
What's periodically causing the zombies to get ordered around in the above .gif if not a periodic timer? Nothing wrong with periodically repeating triggers-- you can turn them on and off (actions - trigger) when necessary so they are only running while the spell is active. You're right that the magnitude of their leaks is compounded by how frequently they run, but that just means you need to know the basics of removing locations, groups, and special effects. That covers the vast majority of all leaks encountered in GUI.

Here's a tutorial that explains how to do it, they're all the same basic idea: save in a variable and destroy after you used it. Groups have a handy other way, which you can see explained here where you just set a variable before you 'create' the group using a specific type of action:
  • Custom script: set bj_wantDestroyGroup = true
  • Unit Group - Pick every unit in (Group) and do (Actions)
Clearly something is happening every X seconds to order them to move, and I just meant to compare the current position with the previous position to see how much the hero moved. Then order the zombies to attack if he moved enough, which is the order I was referring to.
 
Level 2
Joined
Sep 7, 2016
Messages
9

I appreciate your helpfullness :) +rep

- EDITED -

Disregard, I answered my previous question, so I'll ask another simple one.

How can I ensure minimum memory leaks for all my moving minions? Obviously they're all moving toward a specific position, but I can't save that position as a variable since there are multiple different spots all being used at the same time by different random units.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,037
Veryll said:
Replacing a variable with a new value still has leaks, correct?
Yes, I see why you're confused. I should also have added a Custom script: call RemoveLocation(udg_HeroPos[2]) in the Else actions of the snippet I put above, which would eliminate the remaining position leak. Note that you used [2] where I used [1] and vice versa. I will use your notation below.

I believe what you need is a better understanding of spell flow and triggers, because I'm guessing you have 3 if not 4 triggers for this ability each doing part of it. Realistically you need 3-5 depending on if you want to turn your trigger on and off:
  • Zombie Move
    • Events
      • Time - Every 0.50 seconds of game-time
    • Conditions
    • Actions
      • Set HeroPos[1] = Position of (<caster>)
      • If (All conditions are true) then do (actions) else do (actions)
        • If - Conditions
          • (Distance between HeroPos[2] and HeroPos[1]) greater than or equal to <move threshold>
        • Then - Actions
          • Custom script: call RemoveLocation(udg_HeroPos[2])
          • Set HeroPos[2] = HeroPos[1]
          • Unit group - Pick every unit in ZOMBIE_GROUP and do (Actions)
            • Actions
              • -------- Order them to move to random locations nearby HeroPos[2] or whatever you're doing --------
        • Else - Actions
          • Custom script: call RemoveLocation(udg_HeroPos[1])
  • Spawn Zombies
    • Events
      • Unit - A unit dies
    • Conditions
      • <Your conditions for spawning a zombie>
    • Actions
      • -------- Spawn a zombie, then --------
      • Unit group - Add (last created unit) to ZOMBIE_GROUP
  • Zombie Removal
    • Events
      • Unit - A unit dies
    • Conditions
      • ((Triggering unit) is in group ZOMBIE_GROUP) equal to true
    • Actions
      • Unit group - Remove (Triggering unit) from ZOMBIE_GROUP
  • Zombie Turn On
    • Events
      • <Whatever is necessary>
    • Conditions
      • <Whatever is necessary>
    • Actions
      • Trigger - Enable Zombie Move
  • Zombie Turn Off
    • Events
      • <Whatever is necessary>
    • Conditions
      • <Whatever is necessary>
    • Actions
      • Trigger - Disable Zombie Move
Both locations are only every created/used in the periodic trigger and if you look you'll see that only one of them is removed on a given iteration. This is fine because every time we make a new location that we save (temporarily) in HeroPos[2] the previous one is removed. You're right that if you did this you'd leak locations:
  • Set Pos = Position of (triggering unit)
  • Set Pos = Position of (target unit of ability being cast)
 
Level 2
Joined
Sep 7, 2016
Messages
9
You could still use polar offset, by having an array in which you store all your minions. That way you can do like a semi-circle behind your cast

I'm actually having a bit of a difficult time figuring out how to enable each of these arrayed units without accidentally overwriting another already existing arrayed unit OR causing errors due to not clearing the variable after that specific arrayed unit dies. I managed to save a few minions by making X in Minion[x] as however many are currently active, but it only functioned correctly once.

I believe what you need is a better understanding of spell flow and triggers, because I'm guessing you have 3 if not 4 triggers for this ability each doing part of it. Realistically you need 3-5 depending on if you want to turn your trigger on and off:

I have a few other abilities that use a myriad of triggers that flow in and out of each other, the problem is I have almost no experience with multiple units. I've been tampering with making heroes, quests, dialog, etc for a few years, but this is the first time I'm making a hero that actually has units with a little bit more passive control to them. I'm certainly not on the level of most people here, but I do have a pretty good understanding how these things work - just not groups of units, I'm clueless when that comes into play. (Although, I will admit, I also don't really know how to format triggers into these forums...)

Once again, though, I really appreciate the step-by-step help!
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
How can I ensure minimum memory leaks for all my moving minions? Obviously they're all moving toward a specific position, but I can't save that position as a variable since there are multiple different spots all being used at the same time by different random units.
You literally just have to do this for every location you use in your map. If you want, check out the triggers for some approved GUI spells and you'll get a feel of how to work properly with leakable objects.
  • Set LocVariable = <necessary location, however you get it-- could be (target point of ability being cast) or (OtherVariable offset by 15.00 towards 270.00 degrees) or anything else, just set it to a variable instead of just using as part of some other action>
  • -------- Do whatever you need with the location --------
  • -------- When you're done with it and it will never be used again remove it (for example if you have a spawn location for multiple units over the course of the game it might make sense to simply keep SpawnLoc around the whole game and never remove it) --------
  • Custom script: call RemoveLocation(udg_LocVariable)
I have a few other abilities that use a myriad of triggers that flow in and out of each other
I would really recommend you separate out spells and whatnot into their own separate triggers because GUI is inherently hard to organize and it will make debugging stuff easier. Also being sure one trigger isn't messing with the variables for another trigger. Unless you're going hogwild you're never gonna have too many triggers.
(Although, I will admit, I also don't really know how to format triggers into these forums...)
You can quote/reply to my post to see how its done but just right click the trigger in the editor, select "copy as text", and paste it here in between [trigger] and [/trigger] tags.
I'm actually having a bit of a difficult time figuring out how to enable each of these arrayed units without accidentally overwriting another already existing arrayed unit OR causing errors due to not clearing the variable after that specific arrayed unit dies. I managed to save a few minions by making X in Minion[x] as however many are currently active, but it only functioned correctly once.
This is why I suggested using a unit group instead (ZOMBIE_GROUP in my above post)-- figuring out which array index corresponds to a unit is inherently difficult. Instead you can count the number of units in the group (which will accurate if you properly remove them from it when they die) and thus arrange them properly with some math, no arrays required:

  • Set ARC_LENGTH = 150.00 //150 degree arc
  • Set ARC_RADIUS = 200.00
  • Set ARC_START = 180.00 - (ARC_LENGTH / 2) //Angular offset of arc start from facing of the caster-- makes the middle of the arc behind the caster, half the arc on each side
  • Set NUM = Number of units in ZOMBIE_GROUP
  • Set FACE = Current facing of <Hero>
  • If (all conditions are true) then do (then actions) else do (else actions)
    • If - Conditions
      • NUM equal to 1
    • Then - Actions
      • Set UNIT_ANGLE = ARC_LENGTH / 2
      • Set INDEX = 1
    • Else - Actions
      • Set UNIT_ANGLE = ARC_LENGTH / (NUM-1)
      • Set INDEX = 0
  • Unit Group - Pick every unit in ZOMBIE_GROUP and do (actions)
    • Loop - Actions
      • Set TempPoint = HeroPos[2] offset by ARC_RADIUS towards (FACE + ARC_START + (UNIT_ANGLE * INDEX)) degrees
      • Unit - Order (picked unit) to attack-move to TempPoint
      • Custom script: call RemoveLocation(udg_TempPoint)
      • Set INDEX = INDEX + 1
Why the If/Else? The formula I wrote (FACE + ARC_START + (UNIT_ANGLE * INDEX)) has a special case when the group only has 1 unit in it. Without that if block it would order the one unit to move to one side of the arc rather than in the center of it behind the caster. Also ARC_LENGTH / (NUM-1) would produce a divide by 0 and crash the game. By setting UNIT_ANGLE and INDEX cleverly in the if block you can use one set of actions to accomplish the right output even in the special case.
 
Status
Not open for further replies.
Top