Search Unity

AI Navmesh Help

Discussion in 'Scripting' started by SomeGuy3, Apr 1, 2016.

  1. SomeGuy3

    SomeGuy3

    Joined:
    Dec 19, 2015
    Posts:
    65
    Hello,
    I am trying to make a fence and have an ai patrol it, currently when the ai detects an enemy, it makes to a shooting distance and then attacks, this is fine.
    The issue I seem to be having though is that when it detects an enemy it attempts to move around the fence even if the enemy is close, rather than shoot it through the fence which I am wanting it to do.
    Is there a way to make the ai not want to move around the fence which has an navmesh obstacle attached.
    Many thanks for the help.
     
  2. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    I'm guessing you are checking the navMeshAgent.remainingDistance to determine whether or not you will stop and fire. If you are able to shoot through fences, then you should do an actual distance comparison between the unit and the target(Vector3.Distance), as well as a line of sight check (To check for actual, non-fence obstacles that would block a bullet). If both of those pass, stop and fire - otherwise, navigate around the fence so that you can get closer.
     
    ericbegue likes this.
  3. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Make sure your fence is low enough or does not collide with the raycast used for the player visibility test.

    Patrolling, navigating and shooting the target on sight is already a complex AI. I don't know how your AI is currently implemented but you might be interested in Behaviour Tree, its a technique that simplifies the design of AI. I would suggest to have a look at Panda BT, it's a script-based Behaviour Tree engine:

    http://www.pandabehaviour.com

    I'm the author of this package, if you have any question, I'd be glad to help.
     
  4. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    We can't really give you much technical advice without seeing what code you already have set up that's making that reaction. Can you please post it using the Code tags?
     
  5. SomeGuy3

    SomeGuy3

    Joined:
    Dec 19, 2015
    Posts:
    65
    I'm using a mixture of setDestination and stoppingDistance to make the ai move, I have moved the collider down already so that the raycast can see over it, however the ai seems to still want to move around the carved object.
    This is the code I'm using:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class AI_Sight : MonoBehaviour {
    6.  
    7.     private NavMeshAgent agent;
    8.     private AI_Stats myAI_Stats;
    9.     private AI_Attack myAI_Attack;
    10.     private float height = 1f;
    11.     public float sightDist;
    12.     public float approachSpeed = 2f;
    13.     RaycastHit hit;
    14.  
    15.     void Start()
    16.     {
    17.         agent = GetComponent<NavMeshAgent>();
    18.         myAI_Stats = GetComponent<AI_Stats>();
    19.         myAI_Attack = GetComponent<AI_Attack>();
    20.     }
    21.     void Update()
    22.     {
    23.  
    24.         if (Physics.Raycast(transform.position + Vector3.up * height, transform.forward, out hit, sightDist))
    25.         {
    26.             Attack();
    27.         }
    28.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(5, transform.up) * transform.forward, out hit, sightDist))
    29.         {
    30.             Attack();
    31.         }
    32.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(-5, transform.up) * transform.forward, out hit, sightDist))
    33.         {
    34.             Attack();
    35.         }
    36.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(10, transform.up) * transform.forward, out hit, sightDist))
    37.         {
    38.             Attack();
    39.         }
    40.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(-10, transform.up) * transform.forward, out hit, sightDist))
    41.         {
    42.             Attack();
    43.         }
    44.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(15, transform.up) * transform.forward, out hit, sightDist))
    45.         {
    46.             Attack();
    47.         }
    48.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(-15, transform.up) * transform.forward, out hit, sightDist))
    49.         {
    50.             Attack();
    51.         }
    52.         if (Physics.Raycast(transform.position + Vector3.up * height, Quaternion.AngleAxis(20, transform.up) * transform.forward, out hit, sightDist))
    53.         {
    54.             Attack();
    55.         }
    56.     }
    57.     void Attack()
    58.     {
    59.         if (hit.transform.gameObject.tag != transform.gameObject.tag)
    60.         {
    61.             if (hit.transform.gameObject.tag == "Raider")
    62.             {
    63.                 agent.transform.LookAt(hit.collider.gameObject.transform.localPosition);
    64.                 agent.SetDestination(hit.collider.gameObject.transform.localPosition);
    65.                 agent.stoppingDistance = (myAI_Attack.attackDist - 0.5f);
    66.                 agent.speed = approachSpeed;
    67.             }
    68.  
    69.             if (hit.transform.gameObject.tag == "RaiderLeader")
    70.             {
    71.                 agent.transform.LookAt(hit.collider.gameObject.transform.localPosition);
    72.                 agent.SetDestination(hit.collider.gameObject.transform.localPosition);
    73.                 agent.stoppingDistance = (myAI_Attack.attackDist - 0.5f);
    74.                 agent.speed = approachSpeed;
    75.             }
    76.         }
    77.     }
    78. }
    79.  
     
  6. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    stoppingDistance is still going to be a distance along the movement path; if you want the attack to be "line of sight" (shooting) rather than "distance along movement path" (which would be more appropriate for melee) you can't rely on that to ensure the attacker is going to stop.

    If you want line of sight distance and not distance along movement path you're looking at a simple "distance" check (Vector3.Distance(..) etc.)
     
    Nigey likes this.
  7. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    As @LeftyRighty suggested you would need to implement a IsPlayerVisible method.

    Also your AI logic would be much easier to manage as a Behaviour Tree.

    If you are wondering what a behaviour tree looks like, your description above translated into a BT script would be:

    Code (CSharp):
    1. // Do the first successfull action: Attack, Chase or Patrol
    2. tree "Root"
    3.     fallback
    4.         tree "Attack"
    5.         tree "ChasePlayer"
    6.         tree "Patrol"
    7.  
    8. // Attack the player if within attack range.    
    9. tree "Attack"
    10.     while IsPlayerWithinAttackRange
    11.         sequence
    12.             AimAt_Player
    13.             Shoot
    14.  
    15. // Chase the player if visible.        
    16. tree "ChasePlayer"
    17.     while IsPlayerVisible
    18.         sequence
    19.             MoveTo_PlayerPosition
    20.  
    21. // Patrol along a given path made of waypoints.
    22. tree "Patrol"
    23.     while
    24.         sequence // stop Patroling when the player is visible or attackable
    25.             not IsPlayerVisible
    26.             not IsPlayerWithinAttackRange
    27.         sequence
    28.             repeat MoveTo_NextPatrolWaypoint
    29.  
    For inspiration, the Panda BT package contains an example named "Shooter" that demonstrates behaviours such as: Patrolling, Chasing, Shooting, Moving To Cover, ...

    This technique might look a bit unusual, but it will save you a lot of troubles and debugging time once you'll get a grip on it. If you have any question, please ask.
     
  8. SomeGuy3

    SomeGuy3

    Joined:
    Dec 19, 2015
    Posts:
    65
    I've edited my sight to this:
    Code (CSharp):
    1.  
    2. agent.transform.LookAt(hit.collider.gameObject.transform.localPosition);
    3.                 if (Vector3.Distance(gameObject.transform.position, hit.collider.gameObject.transform.localPosition) <= (myAI_Attack.attackDist - 0.5f))
    4.                 {
    5.                     agent.destination = gameObject.transform.position;
    6.                 }
    7.                 else agent.destination = (hit.collider.gameObject.transform.localPosition);
    8.                 agent.speed = approachSpeed ;
    9.  
    This seems to allow my ai to look over obstacles whilst also moving to enemies, does this seem like it will create any issues?
    Also I have been wanting to make an AI behavior tree but haven't find many good examples to work off from online that explains it well, any tips or advice on how it works? How to implement the scripts I current have into it? so on... Thanks a lot for the help.
     
  9. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    First, you have to split your script into simple tasks such as: SetDestination_*, MoveToDestination, IsPlayerVisible, IsPlayerWithinAttackRange. Once you have defined these tasks you can use them as building blocks to build up the behaviour tree of your AI.

    Let's say you want to implement the following tasks: SetDestination_PlayerPosition, MoveToDestination, IsPlayerVisiblie. Then your MonoBehaviour script will become:

    Code (CSharp):
    1. using UnityEngine;
    2. using Panda; // We are using Panda BT.
    3.  
    4. public class UnitAI : MonoBehaviour
    5. {
    6.  
    7.     private NavMeshAgent agent;
    8.     public Vector3 destination;
    9.     public Transform player;
    10.     public float fieldOfView = 45.0f;
    11.     public float viewRange = 30.0f;
    12.  
    13.     [Task] // <-- Marks a method as a task usable from a BT Script.
    14.     void SetDestination_PlayerPosition()
    15.     {
    16.         destination = player.position;
    17.         Task.current.Succeed();
    18.     }
    19.  
    20.  
    21.     [Task]
    22.     void MoveTo_Destination()
    23.     {
    24.         if( Task.current.isStarting )
    25.         {
    26.             // The task has just started, set the destination and make the unit move.
    27.             agent.destination = destination;
    28.             agent.Resume();
    29.         }
    30.  
    31.         if( agent.remainingDistance < 0.5f)
    32.         { // We have reached the destination, we are done with this task.
    33.             Task.current.Succeed();
    34.         }
    35.     }
    36.  
    37.     [Task]
    38.     bool IsPlayerVisible()
    39.     {
    40.  
    41.         // Get some information about the player relative location.
    42.         Vector3 displacemementToPlayer = player.position - this.transform.position;
    43.         Vector3 playerDirection = displacemementToPlayer.normalized;
    44.         float distanceToPlayer = displacemementToPlayer.magnitude;
    45.  
    46.         float angle = Vector3.Angle(this.transform.forward, playerDirection);
    47.  
    48.         // Test whether the player is withing FoV and view range.
    49.         bool isVisible = (angle < fieldOfView) && (distanceToPlayer < viewRange);
    50.  
    51.         // TODO: Use raycasting to test whether player is occluded by walls or obstacle.
    52.  
    53.         return isVisible;
    54.     }
    55.  
    56. }
    In general, implementing a task in C# consists of indicating when the taks is completed ( either in success or in failure). The task method is called once per frame while it is running.

    Then we can used these tasks from a BT script to define, for example the ChasePlayer sub-tree:

    Code (CSharp):
    1. // Chase the player if visible.  
    2. tree "ChasePlayer"
    3.     while IsPlayerVisible
    4.         sequence
    5.             SetDestination_PlayerPosition
    6.             MoveTo_Destination
    BT scripts are executed through the PandaBehaviour component, which you attached on a GameObject you want to drive by a behaviour tree.

    Does this make sens to you, or is there something that is not clear enough? Just tell me, I'll do my best to clarify.
     
    Last edited: Apr 1, 2016
  10. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    Panda BT is a very simple language, it contains only 10 keywords:

    tree, sequence, fallback, parallel, race, random, repeat, while, not, mute

    (yep, that's all of them.)

    Despite the few number of keywords, a lot can be done using them, it's really powerful. In fact I don't know any behaviour that could not be described with them (If you know any, please let me know!).

    If you want to have a grasp about these keywords (what they do and how they can be used), you can play a bit with this demo:
    http://www.pandabehaviour.com/?page_id=236

    Or course, you can always check the documentation:
    http://www.pandabehaviour.com/?page_id=23#Structural_nodes