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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

check if two directions are similar?

Discussion in 'Scripting' started by revraptor, Feb 5, 2021.

  1. revraptor

    revraptor

    Joined:
    Oct 30, 2015
    Posts:
    6
    Hi

    I'm working on AI for my game, and I'm having an issues getting my AI to not go in directions where there's hazards.

    Right now my AI builds a list of Vector3 Posititions (interests) from a physics overlapcircle, then the unit determines which of those positions to go to. I want to also be able to build another Vector3 List of dangers (traps, obstacles), then compare the list of interests to the dangers. If the interests are in a similar direction to the dangers, I want to ignore that interest altogether (No point chasing a player if the player is behind fire for example.)

    What I'm having a problem with, is determining if two positions are a similar direction. Determining if they're the same direction is easy, but it's the fuzzy logic part that I'm struggling with.

    If I'm not mistaken, I can determine what direction the other objects are by doing this:
    Code (CSharp):
    1.  Vector3 dir = (this.transform.position - otherObject.transform.position).normalized
    But then how do I determine that the directions are similar? Ideally I can check by degree difference, but any other way of checking if they're similar would still suffice I think. Degrees is just easier for me to wrap my head around.

    I toyed around with using raycasts to the interests and seeing if they intersect a danger on their way, but that became very lag heavy once I had enemies bunching up, as they'd all be raycasting to eachother, so I prefer
     
  2. diXime

    diXime

    Joined:
    Oct 2, 2018
    Posts:
    162
    Hello,
    "this - other" is a distance, not only a direction, and as you did correctly, you get the direction by normalizing.
    What I don't quite understand is this sentence
    Position vector is a point. It doesn't have any "direction" per say. Maybe you should just check the rotation of said transform? Then again, rotations won't tell you if "other object" is hidden behind fire.
    What you can do is something of that logic
    DistanceFireAI = firePosition - AIPosition
    DistanceFireAI.normalized
    DistanceTargetAI = targetPosition - AIPosition
    DistanceTargetAI.normalized

    Now you have two directions (direction between fire and AI and direction between target and AI). Those two must have an angle close to zero for them to share the same direction. You do that by computing their dot product if I'm not mistaken.
    I advice not to check if the angle is exactly zero because it is a marginal situation, I would check if it's between acceptable bounds.

    Happy coding!
     
    ADNCG and Bunny83 like this.
  3. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    You can tell if two vectors are pointing in a similar direction by checking the angle between them, e.g. with Vector3.Angle.

    But just checking the direction won't let you tell the difference between a player who is standing behind a fire and a player who is standing in front of a fire.

    It might be better to treat hazards like walls, rather than like negative-goals.
     
    Atto2O likes this.
  4. revraptor

    revraptor

    Joined:
    Oct 30, 2015
    Posts:
    6
    I apologize to everyone for not being clear. I've only been programming for a year and have been learning from Udemy and Youtube so I don't quite have the vocabulary or knowledgebase yet to be anything more than an amateur. I appreciate taking the time to answer regardless of my poor explanation! :)

    Sorry I'm terrible at explaining things.
    I have three objects. An Enemy, an Interest, and a Danger.
    I want to calculate if the interest is in a similar direction from the enemy as the danger is, so I can do other stuff explained further down this reply.

    So I want to determine which direction the interest is from the enemy. Then determine what direction the danger is from the enemy. Then determine if those directions are either Similar, or exactly the same. How similar I would like to determine after.

    After Antistones reply I think the answer is:
    Code (CSharp):
    1.  
    2. float maxDifference = 5;
    3.  
    4. float interestAngle = Vector3.Angle(transform.position, interest.transform.position);
    5. float dangerAngle = Vector3.Angle(transform.position, danger.transform.position);
    6.  
    7. float calculatedDifference = Mathf.DeltaAngle(interestAngle, dangerAngle);
    8. if(calculatedDifference <= maxDifference)
    9. {
    10.     //Ignore Direction;
    11. }
    12.  
    But It's 4:30AM so I don't have time to actually test that in Unity right now to make sure.

    I really don't know why I didn't think of Vector3.Angle, but that sounds like exactly what I need. Thank you :)

    I get where you're coming from on the treating hazards like walls. What I explained in my post is a bit of a simplification of why I'm trying to do it in the name of not having a giant wall of text that makes people want to avoid the question.

    I'll provide full reasoning below, as I do understand how this is a bit of an edge case:

    What I'm trying to do is modify the way I combine my steering behaviours for autonomous agents. Right now I have a weighted system where they're balanced by weight, but it's quite finnicky to create unique acting AI that way, and can lead to unintended consequences if stuff balances in certain ways (If Obstacle Avoidance and Pursue balance just right, an agent can just freeze in place.) I've read a couple papers and articles about ways of fixing this, and the best one was to change how the behaviors operate.

    A standard steering behavior will return a single Vector3 or other value that is the steering force. Instead of doing this, I want to have the behaviors return their full "context." (Everything they're interested in, as well as how interested they are.)
    I want two contexts: One Interest context, one Danger context. Then what I want to do, is ignore everything dangerous and only focus on interests (For the most part. Strength will also be weighed in.)

    If a player is in front of, or behind a fire will be handled by the strength of the context. If the fire is closer to the player, it's strength will beat out the player, and vice versa.

    The reasons to do this are pretty nuanced, but I've seen it in application and it leads to some very interesting, unique, and fun AI. When executed it's also very performance friendly compared to doing similar things in a state machine or behavior tree, and negates the need for me to have pathfinding running for my enemies. The levels in my game where you fight enemies are procedurally generated, and change quite often (Walls get destroyed, obstacles can appear, etc.)

    My extreme edge case I'm testing for is 50 enemies on screen without missing any frames, while still maintaining balanced and juicy gameplay. It's a pretty unique challenge to do that in a constantly updating map without losing the fun. There wont be that many enemies all the time, but I have quite a few enemy types planned and do plan on having occasional swarms, and I want them to be just as fun.

    Unity's navmesh, or my A* implementation just simply aren't performant enough for what I'm trying to achieve. I have to constantly rebake/recalculate stuff when walls explode or rocks are mined, and I'm finding the explosions really satisfying to am hoping to be able to make chain reactions a thing. NavMesh/A* also work great for small groups of enemies, but sometimes there's literal flocks of enemies that I want to work together, which calls for flocking algorithms. The problem I'm running into with flocking algorithms is while they're great for when there is a flock, once you're down to just a few enemies all of the issues with basic flocking algorithms start really showing, which is what I'm trying to fix.

    I'm terrible at explaining things so I'm sure I've forgotten some things, but I can link you to several papers about what I'm trying to do and the advantages of it. It's not at all a complete AI solution, more just a replacement for my current movement code which uses a more generic raycasting based obstacle avoidance algorithm while making a beeline for the player. What I have is working, and really what 99% of other roguelike games do, but I'm trying to achieve something a little more unique than that (As well as challenge myself and learn new things.)
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,196
    The only thing to add to Vector3.Angle is that you usually want to zero out the y-components of the vectors, though that depends. If the player is up a 30 degree ramp, but straight ahead, you don't want that to be the same thing as it being 30 degrees to the side.

    Then again, if the player's up a ledge and you have to walk around, that's not ideal. So really you need to compare the directions of the first part of the path?
     
  6. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    317
    If you don't need the actual angle value I would probably just go with Vector3.Dot(a, b) where a and b are normalized Vector3.

    The function will return a number between -1 and 1:
    upload_2021-2-5_9-21-57.png

    Then you can set a threshold where if the result is greater than 0.8 then you can modify your weights because it means the interest and the danger are pretty much in the same direction.

    In this case you would calculate your direction vectors as such:

    Code (CSharp):
    1. a = (player.position - interest.position).normalized;
    2. b = (player.position - danger.position).normalized;
    3. float d = Vector3.Dot(a,b);
    Then just check if d is below or above a certain value.

    I'm also struggling with steering behaviors when forces perfectly balance each other out :(
     
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    No. Vector3.Angle operates on vectors, and you are passing it positions. (Same data type, different conceptual meaning.)

    You want something more like
    Code (CSharp):
    1. Vector3 vectorToInterest = interest.transform.position - transform.position;
    2. Vector3 vectorToDanger = danger.transform.position - transform.position;
    3. float angleBetweenVectors = Vector3.Angle(vectorToInterest, vectorToDanger);
    4.  
    5. if (angleBetweenVectors < someThreshold) // then they're in roughly the same direction
     
  8. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    317
    A Vector is nothing more than a direction, when you think of it as a position it's actually Vector3.zero + whateverVector;

    A normalized vector points in the same direction but has a magnitude of 1

    The code I pasted above does EXACTLY what you want, given two direction vectors return a value indicating their heading parity.

    1 means both vectors are pointing in the same direction
    0 means the vectors are perfectly perpendicular
    -1 means the vectors are pointing in opposite directions

    edit: Sorry for some reason I assumed you were replying to my post lol! please carry on!
     
    Last edited: Feb 5, 2021
  9. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    Parallel means "same direction". I guess you mean orthogonal?
     
  10. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    317
    You are absolutely correct! replaced a word!