Search Unity

Top Down AI Enemy Movement, Face Player, and Shoot Tut

Discussion in '2D' started by MisterSkitz, Nov 12, 2019.

  1. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    833
    Hey if anyone is interested in how to make enemy AI with random movement (And this code will allow an enemy to crawl a maze if that's what you want). I also go over line casting, and briefly explain layer mask. The enemy will look at the player and fire in his direction upon detection.



    Enemy Bullet Script
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class EnemyBullet01 : MonoBehaviour
    4. {
    5.     private void OnTriggerEnter2D(Collider2D collision)
    6.     {
    7.         if (collision.tag == "Wall")
    8.         {
    9.             Destroy(gameObject);
    10.         }
    11.  
    12.         if (collision.tag == "Player")
    13.         {
    14.             Destroy(gameObject);
    15.         }
    16.     }
    17. }
    18.  
    Player Detection Script
    @vakabaka Thanks for your help on this! You are mentioned in the video, amigo!
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerDetection : MonoBehaviour
    4. {
    5.     public Transform origin, end, player;
    6.     public float radarSpd;
    7.     public bool playerDetected;
    8.  
    9.     public static bool playerIsDetected;
    10.  
    11.     private int playerLayer = 1 << 8;  
    12.     private Rigidbody2D enemyRb;
    13.     private Vector3 facePlayer;
    14.  
    15.     private void Start()
    16.     {
    17.         enemyRb = GetComponentInParent<Rigidbody2D>();
    18.         playerIsDetected = false;
    19.     }
    20.  
    21.     private void Update()
    22.     {
    23.         PlayerDetector();
    24.         if (playerDetected == false)
    25.         {
    26.             Radar();
    27.         }
    28.         else { PlayerIsDetected(); }
    29.              
    30.     }
    31.  
    32.     void PlayerDetector()
    33.     {
    34.         Debug.DrawLine(origin.position, end.position, Color.red);
    35.         playerDetected = Physics2D.Linecast(origin.position, end.position, playerLayer);
    36.     }
    37.  
    38.     void Radar()
    39.     {
    40.         end.RotateAround(origin.position, Vector3.forward, radarSpd * Time.deltaTime);
    41.         playerIsDetected = false;
    42.     }
    43.  
    44.     void PlayersPosition()
    45.     {
    46.         facePlayer = player.position - enemyRb.transform.GetChild(0).GetChild(0).position;
    47.         enemyRb.transform.GetChild(0).GetChild(0).up = -facePlayer;
    48.     }
    49.  
    50.     void PlayerIsDetected()
    51.     {
    52.         if(playerDetected == true)
    53.         {
    54.             playerIsDetected = true;
    55.             end.position = player.position;
    56.             PlayersPosition();
    57.         }
    58.     }
    59. }
    60.  
    Wall Detection Script

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class EnemyDetection : MonoBehaviour
    4. {
    5.     public Transform originPointDown, endPointDown, originPointLeft,
    6.                      endPointLeft, originPointRight, endPointRight,
    7.                      originPointUp, endPointUp;
    8.  
    9.     public bool wallDetectedDown, wallDetectedLeft, wallDetectedRight, wallDetectUp;
    10.  
    11.     public static bool pathOpenDown, pathOpenLeft, pathOpenRight, pathOpenUp;
    12.     private int wallLayer = 1 << 10;
    13.  
    14.     private void Update()
    15.     {
    16.          WallDetector();
    17.     }
    18.  
    19.     void WallDetector()
    20.     {
    21.         Debug.DrawLine(originPointDown.position, endPointDown.position, Color.green);
    22.         Debug.DrawLine(originPointLeft.position, endPointLeft.position, Color.green);
    23.         Debug.DrawLine(originPointRight.position, endPointRight.position, Color.green);
    24.         Debug.DrawLine(originPointUp.position, endPointUp.position, Color.green);
    25.         wallDetectedDown = Physics2D.Linecast(originPointDown.position, endPointDown.position, wallLayer);
    26.         wallDetectedLeft = Physics2D.Linecast(originPointLeft.position, endPointLeft.position, wallLayer);
    27.         wallDetectedRight = Physics2D.Linecast(originPointRight.position, endPointRight.position, wallLayer);
    28.         wallDetectUp = Physics2D.Linecast(originPointUp.position, endPointUp.position, wallLayer);
    29.         CheckResults();
    30.     }
    31.  
    32.     void CheckResults()
    33.     {
    34.         if( wallDetectedDown == true)
    35.         {
    36.             pathOpenDown = false;
    37.         }
    38.         else { pathOpenDown = true; }
    39.  
    40.         if ( wallDetectedLeft == true)
    41.         {
    42.             pathOpenLeft = false;
    43.         }
    44.         else { pathOpenLeft = true; }
    45.  
    46.         if ( wallDetectedRight == true)
    47.         {
    48.             pathOpenRight = false;
    49.         }
    50.         else { pathOpenRight = true; }
    51.  
    52.         if(wallDetectUp == true)
    53.         {
    54.             pathOpenUp = false;
    55.         }
    56.         else { pathOpenUp = true; }
    57.     }
    58. }
    Enemy Movement Script

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3.  
    4. public class Enemy01Move : MonoBehaviour
    5. {
    6.     [Header("Enemy Weapon Field")]
    7.     public GameObject eBul;
    8.     public Transform eBulSpawn;
    9.  
    10.     private Rigidbody2D enemyRb;
    11.     private float enemySpd = 5f;
    12.     private float bulSpd = 20f;
    13.     private float shootTimer = 1.0f;
    14.     private int moveDir;
    15.     private int curDir; // You can store your current direction here but I'm not using it in this code. This is how you would do it though
    16.     private bool canChange;
    17.     private bool canFire;
    18.     private bool readyTofire;
    19.     private Vector2 moveDirection;
    20.  
    21.     private void Start()
    22.     {
    23.         enemyRb = GetComponent<Rigidbody2D>();
    24.         moveDir = Random.Range(1, 5); // 1-4  -- 1=down 2=left 3=right 4=up
    25.         if(moveDir < 1 || moveDir > 4)
    26.         {
    27.             while(moveDir < 1 || moveDir > 4) { moveDir = Random.Range(1, 5); }
    28.         }
    29.  
    30.         canChange = true; // Allows the enemy to pick a random direction when true
    31.         canFire = false; // Enemy can only fire if this is true
    32.         readyTofire = false; // When true, it will set canFire true. To properly set the fire rate
    33.     }
    34.  
    35.     private void Update()
    36.     {
    37.         if (PlayerDetection.playerIsDetected == false)
    38.         {
    39.             MovementHandler();
    40.             readyTofire = false;
    41.             shootTimer = 1.0f;
    42.         }
    43.         else
    44.         {
    45.             StopMoving();                      
    46.         }
    47.  
    48.         if(readyTofire == true)
    49.         {
    50.             shootTimer -= Time.deltaTime;
    51.            // Debug.Log(shootTimer);
    52.             if (shootTimer > 0f && canFire == true)
    53.             {
    54.                 StartCoroutine(FireRate());
    55.             }
    56.  
    57.             if(shootTimer < -1f)
    58.             {
    59.                 shootTimer = 1.0f;              
    60.             }
    61.         }
    62.     }
    63.  
    64.     private void FixedUpdate()
    65.     {
    66.         enemyRb.MovePosition(enemyRb.position + (moveDirection * enemySpd) * Time.fixedDeltaTime);
    67.     }
    68.  
    69.     void ChangeDirection()
    70.     {
    71.         // Can move in any direction
    72.         if (EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == true &&
    73.                                                                                                  EnemyDetection.pathOpenUp == true)
    74.         {
    75.             moveDir = Random.Range(1, 5); // 1-4  -- 1=down 2=left 3=right 4=up
    76.             if (moveDir < 1 || moveDir > 4)
    77.             {
    78.                 while (moveDir < 1 || moveDir > 4)
    79.                 {
    80.                     moveDir = Random.Range(1, 5);
    81.                     if (moveDir == curDir)
    82.                     {
    83.                         moveDir = Random.Range(1, 5);
    84.                     }
    85.                 }// End of While loop
    86.             }// End of IF moveDir statement
    87.         } // End of all paths open IF statement
    88.  
    89.         // Cannot move right
    90.         else if(EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == false &&
    91.                                                                                                      EnemyDetection.pathOpenUp == true)
    92.         {
    93.             moveDir = Random.Range(1, 5); // 1-4  -- 1=down 2=left 3=right 4=up
    94.             while(moveDir < 1 || moveDir > 4 || moveDir == 3)
    95.             {
    96.                 moveDir = Random.Range(1, 5);
    97.             }// End of While loop
    98.         }// End of Right path closed all other paths open Statement
    99.  
    100.         // Cannot move Left or Right
    101.         else if(EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == false &&
    102.                                                                                                       EnemyDetection.pathOpenUp == true)
    103.         {
    104.             moveDir = Random.Range(1, 5);
    105.             while(moveDir < 1 || moveDir > 4 || moveDir == 2 || moveDir == 3)
    106.             {
    107.                 moveDir = Random.Range(1, 5);
    108.             }// End of While Loop
    109.         }// End of Up or Down path open IF Statement
    110.  
    111.         // Can only move up
    112.         else if(EnemyDetection.pathOpenDown == false && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == false &&
    113.                                                                                                        EnemyDetection.pathOpenUp == true)
    114.         {
    115.             moveDir = 4;
    116.         } // End of Up movement only IF statement
    117.  
    118.         // Can only move Up or Right
    119.         else if(EnemyDetection.pathOpenDown == false && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == true &&
    120.                                                                                                       EnemyDetection.pathOpenUp == true)
    121.         {
    122.             moveDir = Random.Range(1,5);// 1-4  -- 1=down 2=left 3=right 4=up
    123.             while (moveDir < 3 || moveDir > 4) // can only be 3 or 4
    124.             {
    125.                 moveDir = Random.Range(1, 5);
    126.             }
    127.         }// End of Up or Right Movement IF statement
    128.  
    129.         // Can move Left, Right or Up
    130.         else if(EnemyDetection.pathOpenDown == false && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == true &&
    131.                                                                                                      EnemyDetection.pathOpenUp == true)
    132.         {
    133.             moveDir = Random.Range(1, 5);
    134.             while (moveDir > 4 || moveDir < 2) // Cannot be 1
    135.             {
    136.                 moveDir = Random.Range(1, 5);
    137.             }
    138.         }// End of Left,Right,Up IF statement
    139.  
    140.         // Can only move Left or Up
    141.         else if(EnemyDetection.pathOpenDown == false && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == false &&
    142.                                                                                                       EnemyDetection.pathOpenUp == true)
    143.         {
    144.             moveDir = Random.Range(1, 5); // 1 = down 2 = left 3 = right 4 = up
    145.             while (moveDir < 1 || moveDir > 4 || moveDir == 1 || moveDir == 3)
    146.             {
    147.                 moveDir = Random.Range(1, 5);
    148.             }
    149.         }// End of Left/Up IF statement
    150.  
    151.         // Cannot move left
    152.         else if(EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == true &&
    153.                                                                                                      EnemyDetection.pathOpenUp == true)
    154.         {
    155.             moveDir = Random.Range(1, 5);
    156.             while(moveDir < 1 || moveDir > 4 || moveDir == 2)
    157.             {
    158.                 moveDir = Random.Range(1, 5);
    159.             }
    160.         }// End of Down, Right, Up IF statement
    161.  
    162.         // Can move any direction except Up
    163.         else if(EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == true &&
    164.                                                                                                    EnemyDetection.pathOpenUp == false)
    165.         {
    166.             moveDir = Random.Range(1, 5);
    167.             while(moveDir < 1 || moveDir > 3)
    168.             {
    169.                 moveDir = Random.Range(1, 5);
    170.             }
    171.         }// End of any direction BUT Up statement
    172.  
    173.         // Cannot move Right or Up
    174.         else if(EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == false &&
    175.                                                                                                    EnemyDetection.pathOpenUp == false)
    176.         {
    177.             moveDir = Random.Range(1, 5); // 1 = down 2 = left 3 = right 4 = up
    178.             while(moveDir < 1 || moveDir > 2)
    179.             {
    180.                 moveDir = Random.Range(1, 5);
    181.             }
    182.         }// End of can't move Right or Up statement
    183.  
    184.         // Cannot move Left or Up
    185.         else if (EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == true &&
    186.                                                                                                    EnemyDetection.pathOpenUp == false)
    187.         {
    188.             moveDir = Random.Range(1, 5);
    189.             while(moveDir < 1 || moveDir > 3)
    190.             {
    191.                 moveDir = Random.Range(1, 5);
    192.             }
    193.         }// End of Can't move Left or Up statement
    194.  
    195.         // Can only move Down
    196.         else if (EnemyDetection.pathOpenDown == true && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == false &&
    197.                                                                                                    EnemyDetection.pathOpenUp == false)
    198.         {
    199.             moveDir = 1;
    200.         } // End of can only move Down statement
    201.  
    202.         // Can only move Left
    203.         else if (EnemyDetection.pathOpenDown == false && EnemyDetection.pathOpenLeft == true && EnemyDetection.pathOpenRight == false &&
    204.                                                                                                    EnemyDetection.pathOpenUp == false)
    205.         {
    206.             moveDir = 2;
    207.         } // End of can only move Left statement
    208.  
    209.         // Can only move Right
    210.         else if (EnemyDetection.pathOpenDown == false && EnemyDetection.pathOpenLeft == false && EnemyDetection.pathOpenRight == true &&
    211.                                                                                                    EnemyDetection.pathOpenUp == false)
    212.         {
    213.             moveDir = 3;
    214.         } // End of can only move Right
    215.  
    216.         else { moveDir = 4; } // In case we forgot a scenario or since we didn't add move up only, we'll move Up by default
    217.     }
    218.  
    219.     void MoveLeft()
    220.     {
    221.         curDir = 2;
    222.         enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, -90);
    223.         moveDirection = Vector2.left;
    224.     }
    225.  
    226.     void MoveRight()
    227.     {
    228.         curDir = 3;
    229.         enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, 90);
    230.         moveDirection = Vector2.right;
    231.     }
    232.  
    233.     void MoveUp()
    234.     {
    235.         curDir = 4;
    236.         enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, 180);
    237.         moveDirection = Vector2.up;
    238.     }
    239.  
    240.     void MoveDown()
    241.     {
    242.         curDir = 1;
    243.         enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, 0);
    244.         moveDirection = Vector2.down;
    245.     }
    246.  
    247.     void StopMoving()
    248.     {
    249.         moveDirection = Vector2.zero;
    250.         if (shootTimer >= 1.0f)
    251.         {
    252.             readyTofire = true;
    253.             canFire = true;
    254.         }
    255.     }
    256.  
    257.     void StartShooting()
    258.     {
    259.             GameObject eBull = Instantiate(eBul, eBulSpawn.position, eBulSpawn.rotation);
    260.             Rigidbody2D eBulRb = eBull.GetComponent<Rigidbody2D>();
    261.             eBulRb.AddForce(eBulSpawn.up * bulSpd, ForceMode2D.Impulse);
    262.             Destroy(eBull, 3f);              
    263.     }
    264.  
    265.     void MovementHandler()
    266.     {
    267.         if (moveDir == 1)
    268.         {
    269.             if (EnemyDetection.pathOpenDown == true)
    270.             {
    271.                 MoveDown();
    272.             }
    273.             else { ChangeDirection(); }
    274.         }
    275.         else if (moveDir == 2)
    276.         {
    277.             if (EnemyDetection.pathOpenLeft == true)
    278.             {
    279.                 MoveLeft();
    280.             }
    281.             else { ChangeDirection(); }
    282.         }
    283.         else if (moveDir == 3)
    284.         {
    285.             if (EnemyDetection.pathOpenRight == true)
    286.             {
    287.                 MoveRight();
    288.             }
    289.             else { ChangeDirection(); }
    290.         }
    291.         else if (moveDir == 4)
    292.         {
    293.             if (EnemyDetection.pathOpenUp == true)
    294.             {
    295.                 MoveUp();
    296.             }
    297.             else { ChangeDirection(); }
    298.         }
    299.  
    300.         if (canChange == true)
    301.         {
    302.             StartCoroutine(RandomMovement());
    303.         }
    304.     }
    305.  
    306.     IEnumerator RandomMovement()
    307.     {
    308.         canChange = false;
    309.         var timer = Random.Range(0.5f, 2.5f);
    310.         yield return new WaitForSeconds(timer);
    311.         ChangeDirection();
    312.         yield return new WaitForSeconds(0.5f);
    313.         canChange = true;
    314.     }
    315.  
    316.     IEnumerator FireRate()
    317.     {
    318.         canFire = false;
    319.         float fireRate = 0.25f;
    320.         yield return new WaitForSeconds(fireRate);
    321.         StartShooting();
    322.         yield return new WaitForSeconds(fireRate);
    323.         canFire = true;
    324.  
    325.     }
    326.  
    327.     private void OnCollisionEnter2D(Collision2D collision)
    328.     {
    329.         if(collision.gameObject.tag == "Wall")
    330.         {
    331.             ChangeDirection();
    332.         }
    333.     }
    334. }
     
    Last edited: Nov 12, 2019
    blu3drag0n, vakabaka, vhman and 2 others like this.
  2. vhman

    vhman

    Joined:
    Aug 13, 2018
    Posts:
    360
    Hi All,

    I will add that with https://github.com/h8man/NavMeshPlus - NavMesh for 2d it is easier to make wandering between waypoints, as a guard. Even with restricted movement in 4 directions, you still can utilize various NavMesh tools.
     
    MisterSkitz likes this.
  3. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,153
    Good, what I am missing in your videos is the example how this will work in the Unity. I mean, is it not better to show your work in action at beginn and talk about it without to go immediately deep in the code ?
     
    MisterSkitz likes this.
  4. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    833
    My laptop isn't doing very well performance wise so if I click Run the video will lag and even if I stop recording my screen, it will cut the video off from where the lag started. So unfortunately, for now, this is the best I can do. Sucks! I know!
    But you are right and that's exactly how I want to do my videos. Thanks, amigo!
     
    vakabaka likes this.
  5. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    833
    You are completely correct! I love the NavMesh!
    However, my videos, believe it or not is mostly to offer code examples for beginning coders. I really just started doing these videos so haven't got into packages and resources yet. I do try to encourage custom design and delving into code at entry level. Learn the hard stuff, then show alternate/easier resources. My first line of series, I don't even plan on getting deep into animating but I do want to introduce it at some point. :)

    Thank you for the feedback!
     
    wxrg66 likes this.
  6. wxrg66

    wxrg66

    Joined:
    Apr 3, 2021
    Posts:
    17
    Would using a LayerMask In wall Layers achieve the same effect?