Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Calculating a semi-random direction away from targets

Discussion in 'Scripting' started by Nanako, Dec 13, 2015.

  1. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Hello all. I'm trying to write some rudimentary AI to make prey animals flee away from predators. I have all the detection and alerting in hand, what i really need help with is coding for how the animal decides which direction to flee in

    It would be very simple to just make it run in the exact opposite direction away from the threat, but that would also be too predictable, i'd like the angle to be somewhat randomised because this is a frightened animal, not a logical machine. That and, i'd like to design a system that can cope with multiple threats. Probably in the region of 3-5

    My other idea was a brute force approach - to just generate random points in a small radius, and check whether that point is farther away from the threats than its current position, but that has a couple of flaws too. Like the fact that there would be no valid positions to run to when surrounded, and the creature would freeze. That and, at extremely close ranges, it would sometimes cause the creature to attempt to run through/past a predator

    I have a vague third idea about calculating the average/centre position of all the threats, and basing decisions on getting away from that, but i've not really formed it into a useful concept

    So this is a matter of math and code design really, i need some high level suggestions on methods for calculating useful directions/targetpoints to run to. The overarching rules, in order of priority, being:
    1. Standing still is never good when threats are nearby
    2. No movement should take the creature closer to a threat, except where this conflicts with rule 1

    Any thoughts?
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I'd say generate a generic graph on the fly.

    For ease of demonstration, we'll give the graph a static size. Lets say 8 directions (we could increase this, decrease it, or even have the size dynamically created depending on the number of baddies, but lets just go with 8 for demonstration).

    You'll have a float array of 8 slots, slot 0 is east, 1 is northeast, 2 is north, 3 is northwest, so on and around where 7 is southeast.

    Now, loop over each baddy that is nearby and calculate in which direction they're nearest. Then calculate a weight for them, the more powerful they are, the higher the weight. You might also want to reduce the weight if they're more distant (this way they're more concerned by nearer enemies). Then sum that weight value into the respective slot of the array.

    Then we sum in a halving value all the way around the array away from the slot they're in.

    So if we had a bear to the north with weight of 10, distant enough to be reduced to 8, we'd get the array:

    [2, 4, 8, 4, 2, 1, 0.5, 1]

    Now what we do is select the slot with the lowest value, and that's our safest path out of here.

    You can add a slight angle change to it so that they head nearly in the direction of that path. Since this radial is broken in 8, you can add on a random angle of -22 -> 22, so that way they always run away from the bear, but ever so slightly different each time.

    Note this works even as you add more enemies, because the weights will pile up in the areas where enemies are, and not so much where the enemies aren't. It won't be perfect (which is good, because AI shouldn't be perfect), but will always lean towards a path that is best suited.

    Sure, they might run in the direction of the 2 rabid rabbits, but that's much better than the 3 bears in the other directions.

    You could also put a threshold that if ALL slots are greater than some value (they're utterly surrounded), the prey just freezes and gives up.



    Note, the nice thing about a static sized array is you can recycle the array. This way you're not generating garbage every calculation.


    Also note, all the scalars I speak of, like the 'halving', could be tweaked. For example, you could have each monster have a different scalar for it. A bear might be super scary, so it's prowess is like a 0.9, meaning ALL slots get a huge impact by its presence. Where as a benign frog might get a prowess of 0.1, meaning only slots really close to it get impacted by its presence.

    With this you could also increase the 'random angle' applied. Where you select the slot with lowest value, but if adjacent slots are below some threshold, you expand the range of the random angle. In our numbers up there, if the threshold was 2, you could create a threshold that allows them to run away in a random direction southward, on a nearly 180 degree arc.

    You can apply other weight scales. For instance, say there is a bear to the north, can be seen, it gets its full weight. But say there is another bear to the south, unseen, but could be heard. Give them a half weight. The slots will line up to move away from both bears, but more so in the direction of the hidden bear, because the AI doesn't quite know if there actually is one there. In a huge gathering this could allow for surprise attacks, where the prey runs down a path it thinks is open, but actually has something in it. And since you can calc this, you could make the prey pick it as its still the safest path, but be weary, because it heard something in that direction.
     
    Last edited: Dec 13, 2015
    Kiwasi and Nanako like this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I wanted to add another approach that could be used for a more intelligent prey.

    This would be useful for finding the safest path out when surrounded, by zig zagging between enemies.

    With this one you have a grid of nodes around the AI unit. Each node is associated with its neighbour. And then there is one last node just considered 'outside', that all edge nodes link to.

    Code (csharp):
    1.  
    2. \               /
    3. \             /
    4.   e e e e e e e
    5.   e + + + + + e
    6.   e + + + + + e
    7.   e + + o + + e
    8.   e + + + + + e
    9.   e + + + + + e
    10.   e e e e e e e
    11. /             \
    12. /               \
    13.  
    All e nodes have a link to an abstract node considered 'outside'. This way any e is considered an exit from the grid. outsides distance from all e's is 0, and its weight is 0.

    You then weight each node with nearby baddies.

    And you can A* from 'o' to 'outside'

    This would find the safest path through the grid to any edge.

    It's more intense calculations, but with a nice large graph, you could get cool zig-zagging through the mess of enemies. Great for human AIs that are navigating through a battle field for instance.

    You cold even break 'outside' into subgroups... like N, S, E, and W. Attach each outside node to the appropriate 'e's, and then pick the goal that is best suited. For example, use the previous example to pick a 'best' direction, and then zig zag through. Or say some overarching goal is the N (home is north, the commander of battle is N), so you pick N as the outside to head towards. Again giving you the best zig-zag path that overall heads towards the final goal.
     
    Kiwasi likes this.
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This is the same approach that the AI uses in my Revenge of the Ghosts. Each node is scored by its proximity to enemies, dots and bonuses. Then I use A* to find an optimal path. Its a relatively straightforward way to create somewhat intelligent behaviour.

    If your environment is static, then you can reuse the same nodes repeatedly. This cuts down the processing time quite a bit.
     
  5. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Thanks guys, reading over this. The zigzagging approach is a little too complex for my needs i think, but the first one seems good, I'm currently working on implementing it now
     
  6. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Minor issue
    What's the best way of doing this?
    An idea i have in mind right now, is running Vector3.Angle 8 times, using each direction as the "from", and the vector to the threat as the "to". Then checking which one results in the smallest angle.

    That seems a little expensive though, is there a cheaper approach?
     
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Calculate the 'angle from north'. That way you only need to do one angle calculation.

    (Actually I believe the way Unity implements it you may need to do two calcs, one for angle size and the other for direction)
     
  8. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Also precalculate the angle from north for each of the 8 directions, and then compare numbers?
    And how can i get the direction of that angle ?
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I would have done an angle off east (it's why I made slot 0 east).

    I'm assuming that y is up.

    So just get the vector from the prey to the predator, ignore the y, and calculate at:

    Code (csharp):
    1.  
    2. var a = Mathf.Atan2(v.z, v.x);
    3.  
    This will return angles counter clockwise from +x axis around the y axis.

    You could maybe use the y-difference as a height advantage in determining the weight.
     
    Kiwasi likes this.