Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Unity Turret Tutorial

Discussion in 'Community Learning & Teaching' started by lordconstant, Jul 18, 2015.

  1. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    All that code seems fine.

    Make sure above this function your not missing any braces or semi colons. If thats not the case please post the whole script as it will be easier to help.

    Also use code tags when posting code.
    Just use wrap the following two statements in square brackets [ ] instead of normal brackets & put your code between
    (code=CSharp)
    //Your code here
    (/code)
     
  2. TheRussianBrit

    TheRussianBrit

    Joined:
    Jun 18, 2017
    Posts:
    3
    @lordconstant I cant seem to find where to place it here's the code

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Collections.Generic.List;
    public class friendlyTurretAI : MonoBehaviour {
    public enum AIstates { NEAREST, FURTHERST, WEAKEST, STRONGEST };
    public AIstates aiState = AIstates.NEAREST;
    FrindlyTrackingSystem m_tracker;
    friendlyShoot m_shooter;
    friendlyRotate m_range;
    // Use this for initialization
    void Start () {
    m_tracker = GetComponent<FrindlyTrackingSystem>();
    m_shooter = GetComponent<friendlyShoot>();
    m_range = GetComponent<friendlyRotate>();
    }

    // Update is called once per frame
    void Update () {
    if (!m_tracker || !m_shooter || !m_range)
    return;
    switch (aiState)
    {
    case AIstates.NEAREST:
    TargetNearest;
    break;
    case AIstates.FURTHERST:
    TargetFurthest;
    break;
    case AIstates.STRONGEST:
    break;
    case AIstates.WEAKEST:
    break;
    }
    }
    void TargetNearest()
    {
    List<GameObject> validTargets = m_range.GetValidTargets();
    GameObject curTarget = null;
    float closestDist = 0.0f;

    for(int i = 0; i < validTargets.count; i++)
    {
    float dist = Vector3.Distance(transform.position, validTargets.transform.position);
    if(!curTarget || dist < closestDist)
    {
    curTarget = validTargets;
    closestDist = dist;
    }
    m_tracker.SetTarget(curTarget);
    m_shooter.SetTarget(curTarget);
    }
    }
    void TargetFurthest()
    {
    List<GameObject> validTargets = m_range.GetValidTargets();
    GameObject curTarget = null;
    float furthestDist = 0.0f;
    for (int i = 0; i < validTargets.count; i++)
    {
    float dist = Vector3.Distance(transform.position, validTargets.transform.position);
    if (!curTarget || dist < furthestDist)
    {
    curTarget = validTargets;
    furthestDist = dist;
    }
    m_tracker.SetTarget(curTarget);
    m_shooter.SetTarget(curTarget);
    }
    }
    }
     
  3. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Your lists are intialised fine so im not sure why your getting the error there a couple of possibilities though. When you calling TargetNearest & TargetFarthest make sure to do add thd braces TargetNearest(); Some compilers dont deal well without and it can be misleading aswell (Looks like a variable).

    Also check the return type on FriendlyRotate.GetValidTargets, I would expect the error on its decleration, but it may just be misplacing it.
     
  4. TheRussianBrit

    TheRussianBrit

    Joined:
    Jun 18, 2017
    Posts:
    3
  5. Unityboi1

    Unityboi1

    Joined:
    Jun 24, 2017
    Posts:
    6
    Just found this, looks good. But being a beginner probably not something I should be exploring this early I guess :(

    If you could explain the TrackerSystem a bit more in depth with text (that green // text) that'd be awesome

    Thanks again :)
     
  6. EMJ_Studios

    EMJ_Studios

    Joined:
    Dec 7, 2015
    Posts:
    13
    Hi there, are you still planning on making a smart turret ai tutorial? your other vids have been great and have helped me add a very important element to my game. It all works perfectly, I managed to get your codes to work with my existing ones and have edited a few things to my liking (the beam is a flame thrower for example). So if you are still working on this I'd rather wait for you to finish it, instead of trying to figure it out from other sources. Thanks dude!
     
  7. EMJ_Studios

    EMJ_Studios

    Joined:
    Dec 7, 2015
    Posts:
    13
    Maybe I can help a little here :)

    Code (CSharp):
    1. public class TrackingSystem : MonoBehaviour {
    2.  
    3.     public float speed = 5.0f;
    4.  
    5.     GameObject  n_target = null;
    6.     Vector3 n_lastKnownPosition = Vector3.zero;
    7.     Quaternion n_lookAtRotation;
    8.  
    9.     // Update is called once per frame
    10.     void Update () {
    11.         if(n_target){ //If there is a target
    12.         if (n_lastKnownPosition != n_target.transform.position) { // if the last known position (set to zero) is not the same as the targets position (which its not)
    13.             n_lastKnownPosition = n_target.transform.position;// make it the same. This updates every fram and because the target is changing its position the "last known position" will match that every update(frame)
    14.                 n_lookAtRotation = Quaternion.LookRotation (new Vector3 (n_lastKnownPosition.x, transform.position.y, n_lastKnownPosition.z) - transform.position);
    15.                    
    16.         }
    17.         if(transform.rotation != n_lookAtRotation){
    18.                 transform.rotation = Quaternion.RotateTowards (transform.rotation, n_lookAtRotation, speed * Time.deltaTime);
    19.         }
    20.    
    21.         }
    22.     }
    23.  
    24.     public void setTarget(GameObject target){
    25.         n_target = target;
    26.     }
    27.  
    28. }
    29.  
     
    Unityboi1 and lordconstant like this.
  8. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Ive actually got the smart tracking code working, but the video had a few audio issues the I got bog downed with work. I'll throw up the code tomorrow for it so you can atleast see how it works and I'll try to redo the video this weekend.
     
  9. Unityboi1

    Unityboi1

    Joined:
    Jun 24, 2017
    Posts:
    6
    Thanks a lot! I think my pea sized brain can understand it better now :p
     
  10. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    So heres the codes for the smart tracking

    In my tracking system I use this to get a target position(I store the position in my smart tracking script)

    Code (CSharp):
    1.  
    2. float targetSpeed = Vector3.Distance(m_lastTargetPosition, m_target.transform.position);
    3.                 targetSpeed = targetSpeed < 0.0f ? -targetSpeed : targetSpeed;
    4.                 Vector3 targetDir = Vector3.Normalize(m_target.transform.position - m_lastTargetPosition);
    5.                 m_smartTracking.CalculateAimPosition(m_target.transform.position, targetDir, targetSpeed);
    6.  
    Heres the smart tracking script:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class SmartTracking : MonoBehaviour {
    6.     Vector3 m_targetPos;
    7.     float m_projSpeed;
    8.  
    9.     // Use this for initialization
    10.     void Start ()
    11.     {
    12.         ShootingSystem shootSys = GetComponent<ShootingSystem>();
    13.  
    14.         if(shootSys == null)
    15.             return;
    16.  
    17.         if(shootSys.projectile == null)
    18.             return;
    19.  
    20.         BaseProjectile baseProj = shootSys.projectile.GetComponent<BaseProjectile>();
    21.  
    22.         if(baseProj == null)
    23.             return;
    24.  
    25.         m_projSpeed = baseProj.speed;
    26.     }
    27.  
    28.     public void CalculateAimPosition(Vector3 targetPos, Vector3 targetDir, float targetSpeed)
    29.     {
    30.         float projSpeed = m_projSpeed * Time.deltaTime;
    31.         //How far away the target is
    32.         float targetDist = Vector3.Distance(targetPos, transform.position);
    33.         //How far away the target is in terms of time
    34.         float travelTime = targetDist / projSpeed;
    35.         //How far the target will have moved by the time the projectile reach's it
    36.         float travelDist = travelTime * targetSpeed;
    37.  
    38.         //Use the targets next position to calculate how far forward we should aim
    39.         float nextTargetDist = Vector3.Distance(targetPos + (targetDir * travelDist), transform.position);
    40.         float nextTargetTime = nextTargetDist / projSpeed;
    41.  
    42.         //How much further along the target will be in terms of time
    43.         float timeDiff = nextTargetTime - travelTime;
    44.         //How fast our projectile will catch up
    45.         float catchUpSpeed = travelDist / timeDiff;
    46.         //The speed difference between the projectile & target
    47.         float speedDiff = catchUpSpeed - targetSpeed;
    48.  
    49.         //If its a negative & the targets moving away, we cant catch up
    50.         if(nextTargetTime > travelTime && speedDiff <= 0.0f)
    51.         {
    52.             m_targetPos = targetPos;
    53.             return;
    54.         }
    55.  
    56.         //How long will our projectile take to get to the target
    57.         float offsetTime = travelDist / speedDiff;
    58.         //How far forwards we need to aim
    59.         float aimOffset = (targetSpeed * (travelTime + offsetTime));
    60.  
    61.         m_targetPos = targetPos + (targetDir * aimOffset);
    62.     }
    63.  
    64.     public Vector3 GetTargetPos()
    65.     {
    66.         return m_targetPos;
    67.     }
    68. }
    69.  
    After its calculated call GetTargetPos to get the position.

    It only works with a constant momentum, so a target that accelerates can be missed! (Working out how to change the code to accommodate this)
     
  11. EMJ_Studios

    EMJ_Studios

    Joined:
    Dec 7, 2015
    Posts:
    13
    Perfect, thanks dude I'll try this out and let you all see how it goes :D
     
  12. Aur0k

    Aur0k

    Joined:
    Oct 20, 2017
    Posts:
    6
    Hey dude

    Where exactly i have to copy that float targetSpeed code snippet? I tried it but most lines are red end evil :(
     
  13. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Therea quite abit of code could yoy tell me which script your having an issue with.
     
  14. Aur0k

    Aur0k

    Joined:
    Oct 20, 2017
    Posts:
    6
    1. float targetSpeed = Vector3.Distance(m_lastTargetPosition, m_target.transform.position);
    2. targetSpeed = targetSpeed < 0.0f ? -targetSpeed : targetSpeed;
    3. Vector3 targetDir = Vector3.Normalize(m_target.transform.position - m_lastTargetPosition);
    4. m_smartTracking.CalculateAimPosition(m_target.transform.position, targetDir, targetSpeed);


    5. i copied this how it is in my tracking system script


     
  15. Aur0k

    Aur0k

    Joined:
    Oct 20, 2017
    Posts:
    6

    i love your tutorials dude, there are no better turret guys than you there. but there is one problem i noticed. the flying bullets can only hit the locked target, so if another model with health passes through the shooting passage it wont take any damage. i would like a fire and forget bullet that damages any object on the way..

    and maybe player controlled turret with mouse and variable turning axis? all your code is super variable i love it.
     
  16. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Sorry read this before bed & forgot to respond today.

    I grab the component in the start function to save querying it each frame.

    Heres how my update function the tracking system looks:
    Code (CSharp):
    1.  
    2. void Update ()
    3.     {
    4.         if(m_target)
    5.         {
    6.             if(m_smartTracking)
    7.             {
    8.                 float targetSpeed = Vector3.Distance(m_lastTargetPosition, m_target.transform.position);
    9.                 targetSpeed = targetSpeed < 0.0f ? -targetSpeed : targetSpeed;
    10.                 Vector3 targetDir = Vector3.Normalize(m_target.transform.position - m_lastTargetPosition);
    11.                 m_smartTracking.CalculateAimPosition(m_target.transform.position, targetDir, targetSpeed);
    12.                 m_targetPos = m_smartTracking.GetTargetPos();
    13.             }
    14.             else
    15.             {
    16.                 m_targetPos = m_target.transform.position;
    17.             }
    18.  
    19.             if(m_lastKnownPosition != m_target.transform.position)
    20.             {
    21.                 m_lastKnownPosition = m_targetPos;
    22.                 m_lastKnownPosition.y = transform.position.y;
    23.  
    24.                 m_lookAtRotation = Quaternion.LookRotation(m_lastKnownPosition - transform.position);
    25.             }
    26.  
    27.             if(transform.rotation != m_lookAtRotation)
    28.             {
    29.                 transform.rotation = Quaternion.RotateTowards(transform.rotation, m_lookAtRotation, speed * Time.deltaTime);
    30.             }
    31.  
    32.             m_lastTargetPosition = m_target.transform.position;
    33.         }
    34.     }
    35.  
    In the shooting system you just call the GetTargetPos function:

    Code (CSharp):
    1.  
    2. if(m_smartTracking)
    3.         {
    4.             m_targetPos = m_smartTracking.GetTargetPos();
    5.         }
    6.         else
    7.         {
    8.             m_targetPos = m_target.transform.position;
    9.         }
    10.  
     
  17. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    For a player controlled turret a few key things would need changing (It was meant for an automated turret, but I could've written it better to easily work as either).
    The tracking should find a position to aim at & not be responsible for rotation (A separate script should handle that).
    The shooting system requires a GameObject currently, but it should be expanded to accept a position/direction as an alternate.

    For some reason the scripts I have on my computer actually damage the first thing hit regardless of target, here's the bits that need changing:

    NormalProjectile:
    Code (CSharp):
    1. void OnCollisionEnter(Collision other)
    2.     {
    3.         MessageHandler msgHandler = other.gameObject.GetComponent<MessageHandler>();
    4.  
    5.         if(msgHandler)
    6.         {
    7.             DamageData dmgData = new DamageData();
    8.             dmgData.damage = m_damage;
    9.  
    10.             msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    11.         }
    12.  
    13.         if(other.gameObject.GetComponent<BaseProjectile>() == null)
    14.             Destroy(gameObject);
    15.     }
    Tracking projectile:
    Code (CSharp):
    1. void OnCollisionEnter(Collision other)
    2.     {
    3.         MessageHandler msgHandler = other.gameObject.GetComponent<MessageHandler>();
    4.  
    5.         if(msgHandler)
    6.         {
    7.             DamageData dmgData = new DamageData();
    8.             dmgData.damage = m_damage;
    9.  
    10.             msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    11.         }
    12.  
    13.      
    14.         if(other.gameObject.GetComponent<BaseProjectile>() == null)
    15.             Destroy(gameObject);
    16.     }
    Beam Projectile (Swap out the raycast in update to this):
    Code (CSharp):
    1. if(Physics.Raycast(m_launcher.transform.position, m_launcher.transform.forward, out hit, beamLength))
    2.             {
    3.                 MessageHandler msgHandler = hit.transform.gameObject.GetComponent<MessageHandler>();
    4.  
    5.                 if(msgHandler)
    6.                 {
    7.                     if(m_attackTimer >= m_attackSpeed)
    8.                     {
    9.                         DamageData dmgData = new DamageData();
    10.                         dmgData.damage = m_damage;
    11.  
    12.                         msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    13.  
    14.                         m_attackTimer = 0.0f;
    15.                     }
    16.                 }
    17.             }
     
    Last edited: Oct 22, 2017
  18. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    You still needed to declare the missing variables in the script & grab the SmartTracking component:

    Code (CSharp):
    1.  
    2. SmartTracking m_smartTracking;
    3. Vector3 m_targetPos;
    4. Vector3 m_lastTargetPosition;
    5.  
    6. void Start()
    7. {
    8.     m_smartTracking = gameObject.GetComponent<SmartTracking>();
    9. }
    10.  
     
  19. MrKory

    MrKory

    Joined:
    Sep 20, 2013
    Posts:
    66
    This system works perfectly for me except one thing, and I can't figure out how to make the change to get it to behave as I would like.

    When bullets, or missiles are fired from a turret they always point in the same direction regardless of the turrets orientation. Example: The turret tracks something which is higher than the turret and rotates 90 degrees to the left or right. When the ammo gameObject is instantiated it is facing the wrong direction, and not rotated to match the barrel of the turret. The ammo travels as expected and hits the target, but it seems to use world space instead of local space.

    If there is an easy way to fix this please share a fix or some ideas of how to do it.
    Thanks,
    -Kory
     
    lordconstant likes this.
  20. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    In the fire projectile code just take the travelDir & set the bullets transform.forward to travelDir.Normalised
     
    MrKory likes this.
  21. MrKory

    MrKory

    Joined:
    Sep 20, 2013
    Posts:
    66
    Thanks for the reply.. I'll give it a try and see how it works.
     
  22. Aur0k

    Aur0k

    Joined:
    Oct 20, 2017
    Posts:
    6
    I did everything from scratch again to understand more of this code, glorious. :)
    now everything works fine.

    When are you going to continiue this awesome work?
     
  23. cruising

    cruising

    Joined:
    Nov 22, 2013
    Posts:
    329
    How do i limit the targeting angles up/down and Right/left?
    And also i want the beam to not constant shooting and be visible, i want it to shoot once and after some sec it shoots again.
    Any help? :)
     
  24. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Not looked at this in a while but what you would want to do is add burst fire support so active shooting for x amount of time then y delay before firing again. Have the beam active while firing & inactive during the delay.

    I think I helped someone setup limiting the targeting, Ill see if I can fish out the code.
     
  25. cruising

    cruising

    Joined:
    Nov 22, 2013
    Posts:
    329
    Yes thats correct, but that is something i dont know how to do.
    And i hope you find the code for the limits.
     
  26. Keyosbringer

    Keyosbringer

    Joined:
    Apr 27, 2019
    Posts:
    3
    Hey, so my turret doesn't seem to be shooting anything, idk what's wrong so I hope you could help me. I'm a complete noob on coding so far lol.

    I think you switched m_target to just target at one point, but then it would give an error because I was missing something

    Here's the code for you to check:

    ShootingSystem.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class ShootingSystem : MonoBehaviour
    6. {
    7.     public float fireRate;
    8.     public int damage;
    9.     public float fieldOfView;
    10.     public bool beam;
    11.     public GameObject projectile;
    12.     public List<GameObject> projectileSpawns;
    13.  
    14.     List<GameObject> m_lastProjectiles = new List<GameObject>();
    15.     float m_fireTimer = 0.0f;
    16.     GameObject m_target;
    17.  
    18.     // Update is called once per frame
    19.     void Update()
    20.     {
    21.         if (!m_target)
    22.         {
    23.             if (beam)
    24.                 RemoveLastProjectiles();
    25.  
    26.             return;
    27.         }
    28.  
    29.         if (beam && m_lastProjectiles.Count <= 0)
    30.         {
    31.             float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(m_target.transform.position - transform.position));
    32.  
    33.             if (angle < fieldOfView)
    34.             {
    35.                 SpawnProjectiles();
    36.             }
    37.         }
    38.         else if (beam && m_lastProjectiles.Count > 0)
    39.         {
    40.             float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(m_target.transform.position - transform.position));
    41.  
    42.             if (angle > fieldOfView)
    43.             { while(m_lastProjectiles.Count > 0)
    44.                 {
    45.                     Destroy(m_lastProjectiles[0]);
    46.                     m_lastProjectiles.RemoveAt(0);
    47.                 }
    48.             }
    49.         }
    50.         else
    51.         {
    52.             m_fireTimer += Time.deltaTime;
    53.  
    54.             if (m_fireTimer >= fireRate)
    55.             {
    56.                 float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(m_target.transform.position - transform.position));
    57.  
    58.                 if (angle < fieldOfView)
    59.                 {
    60.                     SpawnProjectiles();
    61.  
    62.                     m_fireTimer = 0.0f;
    63.                 }
    64.             }
    65.         }
    66.     }
    67.  
    68.     void SpawnProjectiles()
    69.     {
    70.         if (!projectile)
    71.         {
    72.             return;
    73.         }
    74.  
    75.         m_lastProjectiles.Clear();
    76.  
    77.         for (int i = 0; i < projectileSpawns.Count; i++)
    78.         {
    79.             if (projectileSpawns[i])
    80.             {
    81.                 GameObject proj = Instantiate(projectile, projectileSpawns[i].transform.position, Quaternion.Euler(projectileSpawns[i].transform.forward)) as GameObject;
    82.                 proj.GetComponent<BaseProjectile>().FireProjectile(projectileSpawns[i], m_target, damage, fireRate);
    83.  
    84.                 m_lastProjectiles.Add(proj);
    85.             }
    86.         }
    87.     }
    88.  
    89.     public void SetTarget(GameObject target)
    90.     {
    91.         m_target = target;
    92.     }
    93.  
    94.     void RemoveLastProjectiles()
    95.     {
    96.         while (m_lastProjectiles.Count > 0)
    97.         {
    98.             Destroy(m_lastProjectiles[0]);
    99.             m_lastProjectiles.RemoveAt(0);
    100.         }
    101.     }
    102. }
    103.  
     
  27. DinoPDC

    DinoPDC

    Joined:
    Mar 26, 2019
    Posts:
    2
    hey guys, by any chance, has anyone figured out a way that i can lock the rotation of my sentry gun on the z and y axis? if you guys want to know what's happening, it's that my gun keeps on just flipping upside down instead of staying right-side up. all i did was copy the TargetingSystem script above

    thanks
     
  28. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    The bit at line 15 should look like this:

    Code (CSharp):
    1. if(m_lastKnownPosition != m_target.transform.position)
    2.             {
    3.                 m_lastKnownPosition = m_targetPos;
    4.  
    5.                 m_lookAtRotation = Quaternion.LookRotation(m_lastKnownPosition - transform.position, Vector3.up);
    6.             }
    7.  
    Passing in Vector3.up will rotate around the axis (0, 1, 0) which means your turret won't flip.
     
  29. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Put in a breakpoint & see if m_target is null, if so the problem will be in your TurretAi script, make sure m_shooter.SetTarget is getting called in all of the target functions.
     
  30. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Looking into the burst fire mode now, for the locked rotation you will want this script. I've explained the changes at the bottom.

    Code (CSharp):
    1. public class TrackingSystem : MonoBehaviour {
    2.     public float speed = 3.0f;
    3.     public float angularYLimit = 90.0f;
    4.     public float angularXLimit = 90.0f;
    5.  
    6.     public bool allowYRot = true;
    7.     public bool allowXRot = true;
    8.  
    9.     GameObject m_target = null;
    10.     Vector3 m_lastKnownPosition = Vector3.zero;
    11.     Vector3 m_lastTargetPosition = Vector3.zero;
    12.     Vector3 m_targetPos = Vector3.zero;
    13.     Quaternion m_lookAtRotation;
    14.     Quaternion m_initialRotation;
    15.  
    16.     Vector3 m_intialForward;
    17.  
    18.     void Start()
    19.     {
    20.         m_initialRotation = transform.localRotation;
    21.         m_intialForward = transform.forward;
    22.     }
    23.  
    24.     // Update is called once per frame
    25.     void Update ()
    26.     {
    27.         if(m_target)
    28.         {
    29.             m_targetPos = m_target.transform.position;
    30.  
    31.             if(m_lastKnownPosition != m_target.transform.position)
    32.             {
    33.                 m_lastKnownPosition = m_targetPos;
    34.  
    35.                 Quaternion xQuat = Quaternion.identity;
    36.                 Quaternion yQuat = Quaternion.identity;
    37.  
    38.                 Vector3 lastKnownPos = m_lastKnownPosition;
    39.  
    40.                 if (allowXRot)
    41.                 {
    42.                     lastKnownPos.y = transform.position.y;
    43.                     xQuat = Quaternion.LookRotation(lastKnownPos - transform.position, Vector3.up);
    44.                 }
    45.  
    46.                 if(allowYRot)
    47.                 {
    48.                     lastKnownPos = m_lastKnownPosition;
    49.                     lastKnownPos.y = transform.position.y;
    50.                     float xzDist = (lastKnownPos - transform.position).magnitude;
    51.                     lastKnownPos = transform.position + (Vector3.forward * xzDist);
    52.                     lastKnownPos.y = m_lastKnownPosition.y;
    53.                     yQuat = Quaternion.LookRotation(lastKnownPos - transform.position, Vector3.up);
    54.                 }
    55.  
    56.                 float angDiff = Quaternion.Angle(m_initialRotation, yQuat);
    57.  
    58.                 if (Mathf.Abs(angDiff) > angularYLimit)
    59.                 {
    60.                     yQuat = Quaternion.RotateTowards(m_initialRotation, yQuat, angularYLimit);
    61.                 }
    62.  
    63.                 angDiff = Quaternion.Angle(m_initialRotation, xQuat);
    64.  
    65.                 if (Mathf.Abs(angDiff) > angularXLimit)
    66.                 {
    67.                     xQuat = Quaternion.RotateTowards(m_initialRotation, xQuat, angularXLimit);
    68.                 }
    69.  
    70.                 m_lookAtRotation = Quaternion.Inverse(m_initialRotation) * (xQuat * yQuat);
    71.             }
    72.  
    73.             if(transform.localRotation != m_lookAtRotation)
    74.             {
    75.                 bool rotateReverse = false;
    76.                 Vector3 lookAtDir = (m_lookAtRotation * m_intialForward).normalized;
    77.                 Debug.DrawRay(transform.position, lookAtDir * 5.0f, Color.red);
    78.                 Vector3 lookingDir = transform.forward;
    79.  
    80.                 float lookAtDot = Vector3.Dot(lookAtDir, m_intialForward);
    81.                 float lookingDot = Vector3.Dot(lookingDir, m_intialForward);
    82.  
    83.                 lookAtDot -= 1.0f;
    84.                 lookAtDot *= 0.5f;
    85.                 lookingDot -= 1.0f;
    86.                 lookingDot *= 0.5f;
    87.                 lookAtDot = Mathf.Abs(lookAtDot);
    88.                 lookingDot = Mathf.Abs(lookingDot);
    89.  
    90.                 float totalAngle = (lookAtDot * 180.0f) + (lookingDot * 180.0f);
    91.  
    92.                 if (totalAngle > 180.0f && angularXLimit < 180.0f)
    93.                 {
    94.                     if (lookingDir.x > 0.0f && lookAtDir.x < 0.0f)
    95.                         rotateReverse = true;
    96.  
    97.                     if (lookingDir.x < 0.0f && lookAtDir.x > 0.0f)
    98.                         rotateReverse = true;
    99.                 }
    100.  
    101.                 transform.localRotation = Quaternion.RotateTowards(transform.localRotation, m_lookAtRotation, (rotateReverse ? -speed : speed) * Time.deltaTime);
    102.             }
    103.  
    104.             m_lastTargetPosition = m_target.transform.position;
    105.         }
    106.     }
    107.  
    108.     public void SetTarget(GameObject target){
    109.         m_target = target;
    110.     }
    111. }
    I fair bit needed changing to get it working.

    When working out the look at rotation it now calculates two quaternions instead of one, one for each axis.
    We multiply these together to get our look at rotation, it had to be done this way so we can easily disable an axis by leaving the relevant quaternion as an identity (The equivalent of multiplying a number 1).

    We also check each quaternions angle to see if it exceeds the angular limit for that axis & then we clamp it if so.

    Once that's done a fix was needed for the turret taking the shortest route to complete a rotation e.g. its stopped on the left limit, then it want to go the right limit so turns through the area we don't want it to.

    To do this we check if the combined angle between where we are looking & where were meant to be looking exceeds 180 (Anything less than this is fine to take the shortest route).

    If it does then we check if the target is on the opposite side or not to avoid it turning away the second a target goes behind the turret.

    At this point we can start to go & aim in the opposite direction as once its there it will be back in the limits & can rotate normally.
     
  31. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Getting the beam to act as you wanted was fairly straight forward, code below, explanation of changes at bottom.

    Code (CSharp):
    1. public class BeamProjectile : BaseProjectile {
    2.     public float beamLength = 10.0f;
    3.     public float beamFade = 0.1f;
    4.     GameObject m_launcher;
    5.     GameObject m_target;
    6.     int m_damage;
    7.     float m_attackSpeed;
    8.     float m_attackTimer;
    9.     float m_fadeTimer;
    10.  
    11.     LineRenderer m_beamRenderer;
    12.  
    13.     private void Awake()
    14.     {
    15.         m_beamRenderer = GetComponent<LineRenderer>();
    16.     }
    17.  
    18.     // Update is called once per frame
    19.     void Update () {
    20.         m_attackTimer += Time.deltaTime;
    21.  
    22.         if(m_launcher){
    23.             m_beamRenderer.SetPosition(0, m_launcher.transform.position);
    24.             m_beamRenderer.SetPosition(1, m_launcher.transform.position + (m_launcher.transform.forward * beamLength));
    25.  
    26.             if (m_fadeTimer > 0.0f)
    27.             {
    28.                 m_fadeTimer -= Time.deltaTime;
    29.  
    30.                 if (m_fadeTimer <= 0.0f)
    31.                     m_beamRenderer.enabled = false;
    32.             }
    33.  
    34.             RaycastHit hit;
    35.  
    36.             if(Physics.Raycast(m_launcher.transform.position, m_launcher.transform.forward, out hit, beamLength))
    37.             {
    38.                 MessageHandler msgHandler = hit.transform.gameObject.GetComponent<MessageHandler>();
    39.  
    40.                 if(msgHandler)
    41.                 {
    42.                     if(m_attackTimer >= m_attackSpeed)
    43.                     {
    44.                         DamageData dmgData = new DamageData();
    45.                         dmgData.damage = m_damage;
    46.  
    47.                         msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    48.  
    49.                         m_attackTimer = 0.0f;
    50.                         m_fadeTimer = m_attackSpeed * beamFade;
    51.                         m_beamRenderer.enabled = true;
    52.                     }
    53.                 }
    54.             }
    55.         }
    56.     }
    57.    
    58.     public override void FireProjectile(GameObject launcher, GameObject target, int damage, float attackSpeed){
    59.         if(launcher){
    60.             m_launcher = launcher;
    61.             m_target = target;
    62.             m_damage = damage;
    63.             m_attackSpeed = attackSpeed;
    64.             m_attackTimer = 0.0f;
    65.             m_fadeTimer = 0.0f;
    66.             m_beamRenderer.enabled = false;
    67.         }
    68.     }
    69. }
    Cached the LineRenderer to save GetComponent calls.
    Added a beamFade variable that should be kept between 0 & 1, we multiply this by attack time to give us how long our beam should show for.
    We then use LineRenderers enabled variable to turn it on & off.

    If you want the beam the "Travel" then you could adjust the positions, 0 being the back of the beam & 1 being the front.

    Just lerp from transform position to the target position.
    Then do target - transform position & normalise it for the direction, you can multiply this by a number & add that the the front position to be the length of the beam.
     
  32. Keyosbringer

    Keyosbringer

    Joined:
    Apr 27, 2019
    Posts:
    3
    Sorry for the newbie question again, but I can't seem to connect visual studio with unity so i can't debug it? I've set visual studio 2017 as preference already with editor attaching ticked. It does say that

    "No supported VCS diff tools were found. Please install one of the following tools:
    SourceGear DiffMerge , TkDiff, P4Merge, TortoiseMerge, WinMerge, PlasticSCM Merge, Beyond Compare 4

    So do i download those? Or what am I suppose to do?
     
  33. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Those programs are used for comparing the differences between two versions of a code file. Generally related to source control, peraonally I opt for beyond compare.
     
  34. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Quoting you so it notifies
     
    Keyosbringer likes this.
  35. Keyosbringer

    Keyosbringer

    Joined:
    Apr 27, 2019
    Posts:
    3
    I found the issue, I didn't add Sphere Collider. welp, thanks for helping me though, the turret shoots now
     
    lordconstant likes this.
  36. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    No worries glad its working for you :)
     
  37. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    I was following along in your videos but I must have missed something. in your "Shooting System" script you had made a few changes that are not reflected in the scripts you posted, when I make the changes you made in your video I get the following error:

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. ShootingSystem.Update () (at Assets/Scripts/ShootingSystem.cs:45)
     
  38. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    In the Update function at the top add:

    Code (CSharp):
    1. if(m_target == null)
    2. {
    3.      return;
    4. }
    The RangeChecker is responsible for finding a target & the ai script passes that target to the shooting system. It is possible for the target to be null when there is nothing to shoot at or if the current target is destroyed.
     
    KnightRiderGuy likes this.
  39. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    Hey thanks for getting back to me this being an old forum post and all ;)
    I'm not totally sure what was happening but when I restarted the next morning it was working as it should.
    Thanks for this great tutorial BTW it's been the perfect stating point for a tank game I've been wanting to make for a long time now.
     
  40. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    No worries, all the scripts on here have fixes for issues that were in the videos (Bad setup by me). So if you run into any issues best to consult the scripts here & I'm always happy to help.
     
    KnightRiderGuy likes this.
  41. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    Thanks lordconstant
    Perhaps I can ask you to take a look at something for me?
    I'm trying to implement some of what you've done into this enemy AI script I found here in the forum.
    Right now I have my drone robot patrolling and when my player gets close it does appear to perform the attack function only I'm having some issues with the AI not quite pointing in the direction of my player, well... sometimes it does and most times it does not.??

    I'm also having some issues with the laser prefab, it seems to fire OK but it only sometimes does my player damage.??
    So I'm not sure what I have wonky in the script.
    I realise the AI script is not yours but if you have a moment and are willing I could sure use a little help ;)
    I'll post the Enemy AI script below, I've commented my additions so hopefully that will help.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EnemyAI_Drone : MonoBehaviour
    6. {
    7.     public bool beam;                           // My Addition
    8.     public float fireRate;                      // My Addition
    9.     public int damage;                          // My Addition
    10.     public GameObject m_target;                 // My Addition
    11.     public GameObject projectile;               // My Addition
    12.     public List<GameObject> projectileSpawns;   // My Addition
    13.     List<GameObject> m_lastProjectiles = new List<GameObject>();//My Addiion
    14.     float m_fireTimer = 0.0f;                   // My Addition
    15.  
    16.     PlayerDeath PlayerDeathScriptInstance = null;// My Addition
    17.  
    18.     // Audio Clip For Laser Fireing SFX
    19.     public AudioClip laserFireSFX_Clip;         // My Addition
    20.  
    21.     //Inspector initiated variables. Defaults are set for ease of use.
    22.  
    23.     public bool on = true; //Is the AI active? this can be used to place pre-set enemies in you scene.
    24.  
    25.     public bool canFly = false; //Flying alters float behavior to ignore gravity. The enemy will fly up or down only to sustain floatHeight level.
    26.  
    27.     public float floatHeight = 0.0f; //If it can fly/hover, you need to let the AI know how high off the ground it should be.
    28.  
    29.     public bool runAway = false; //Is it the goal of this AI to keep it's distance? If so, it needs to have runaway active.
    30.  
    31.     public bool runTo = false; //Opposite to runaway, within a certain distance, the enemy will run toward the target.
    32.  
    33.     public float runDistance = 25.0f; //If the enemy should keep its distance, or charge in, at what point should they begin to run?
    34.  
    35.     public float runBufferDistance = 50.0f; //Smooth AI buffer. How far apart does AI/Target need to be before the run reason is ended.
    36.  
    37.     public int walkSpeed = 10; //Standard movement speed.
    38.  
    39.     public int runSpeed = 15; //Movement speed if it needs to run.
    40.  
    41.     public int randomSpeed = 10; //Movement speed if the AI is moving in random directions.
    42.  
    43.     public float rotationSpeed = 20.0f; //Rotation during movement modifier. If AI starts spinning at random, increase this value. (First check to make sure it's not due to visual radius limitations)
    44.  
    45.     public float visualRadius = 100.0f; //How close does the player need to be to be seen by the enemy? Set to 0 to remove this limitation.
    46.  
    47.     public float moveableRadius = 200.0f; //If the player is too far away, the AI will auto-matically shut down. Set to 0 to remove this limitation.
    48.  
    49.     public float attackRange = 10.0f; //How close does the enemy need to be in order to attack?
    50.  
    51.     public float attackTime = 0.50f; //How frequent or fast an enemy can attack (cool down time).
    52.  
    53.     public bool useWaypoints = false; //If true, the AI will make use of the waypoints assigned to it until over-ridden by another functionality.
    54.  
    55.     public bool reversePatrol = true; //if true, patrol units will walk forward and backward along their patrol.
    56.  
    57.     public Transform[] waypoints; //define a set path for them to follow.
    58.  
    59.     public bool pauseAtWaypoints = false; //if true, patrol units will pause momentarily at each waypoint as they reach them.
    60.  
    61.     public float pauseMin = 1.0f; //If pauseAtWaypoints is true, the unit will pause momentarily for minmum of this time.
    62.  
    63.     public float pauseMax = 3.0f; //If pauseAtWaypoints is true, the unit will pause momentarily formaximum of this time.
    64.  
    65.     public float huntingTimer = 5.0f; //Search for player timer in seconds. Minimum of 0.1
    66.  
    67.     public bool estimateElevation = false; //This implements a pause between raycasts for heights and guestimates the need to move up/down in height based on the previous raycast.
    68.  
    69.     public float estRayTimer = 1.0f; //The amount of time in seconds between raycasts for gravity and elevation checks.
    70.  
    71.     public bool requireTarget = true; //Waypoint ONLY functionality (still can fly and hover).
    72.  
    73.     public Transform target; //The target, or whatever the AI is looking for.
    74.  
    75.  
    76.  
    77.  
    78.  
    79.     //private script handled variables
    80.  
    81.     private bool initialGo = false; //AI cannot function until it is initialized.
    82.  
    83.     private bool go = true; //An on/off override variable
    84.  
    85.     private Vector3 lastVisTargetPos; //Monitor target position if we lose sight of target. provides semi-intelligent AI.
    86.  
    87.     CharacterController characterController; //CC used for enemy movement and etc.
    88.  
    89.     private bool playerHasBeenSeen = false; //An enhancement to how the AI functions prior to visibly seeing the target. Brings AI to life when target is close, but not visible.
    90.  
    91.     private bool enemyCanAttack = false; //Used to determine if the enemy is within range to attack, regardless of moving or not.
    92.  
    93.     private bool enemyIsAttacking = false; //An attack interuption method.
    94.  
    95.     private bool executeBufferState = false; //Smooth AI buffer for runAway AI. Also used as a speed control variable.
    96.  
    97.     private bool walkInRandomDirection = false; //Speed control variable.
    98.  
    99.     private float lastShotFired; //Used in conjuction with attackTime to monitor attack durations.
    100.  
    101.     private float lostPlayerTimer; //Used for hunting down the player.
    102.  
    103.     private bool targetIsOutOfSight; //Player tracking overload prevention. Makes sure we do not call the same coroutines over and over.
    104.  
    105.     private Vector3 randomDirection; //Random movement behaviour setting.
    106.  
    107.     private float randomDirectionTimer; //Random movement behaviour tracking.
    108.  
    109.     private float gravity = 20.0f; //force of gravity pulling the enemy down.
    110.  
    111.     private float antigravity = 2.0f; //force at which floating/flying enemies repel
    112.  
    113.     private float estHeight = 0.0f; //floating/flying creatures using estimated elevation use this to estimate height necessities and gravity impacts.
    114.  
    115.     private float estGravityTimer = 0.0f; //floating/flying creatures using estimated elevation will use this to actually monitor time values.
    116.  
    117.     private int estCheckDirection = 0; //used to determine if AI is falling or not when estimating elevation.
    118.  
    119.     private bool wpCountdown = false; //used to determine if we're moving forward or backward through the waypoints.
    120.  
    121.     private bool monitorRunTo = false; //when AI is set to runTo, they will charge in, and then not charge again to after far enough away.
    122.  
    123.     private int wpPatrol = 0; //determines what waypoint we are heading toward.
    124.  
    125.     private bool pauseWpControl; //makes sure unit pauses appropriately.
    126.  
    127.     private bool smoothAttackRangeBuffer = false; //for runAway AI to not be so messed up by their visual radius and attack range.
    128.  
    129.  
    130.  
    131.  
    132.  
    133.  
    134.     //---Starting/Initializing functions---//
    135.  
    136.     void Start()
    137.     {
    138.  
    139.         GameObject tempObj = GameObject.Find("Player");                 // My Addition
    140.         PlayerDeathScriptInstance = tempObj.GetComponent<PlayerDeath>();// My Addition
    141.  
    142.         StartCoroutine(Initialize()); //co-routine is used incase you need to interupt initiialization until something else is done.
    143.  
    144.     }
    145.  
    146.  
    147.  
    148.     IEnumerator Initialize()
    149.     {
    150.  
    151.         if ((estimateElevation) && (floatHeight > 0.0f))
    152.         {
    153.  
    154.             estGravityTimer = Time.time;
    155.  
    156.         }
    157.  
    158.         characterController = gameObject.GetComponent<CharacterController>();
    159.  
    160.         initialGo = true;
    161.  
    162.         yield return null;
    163.  
    164.     }
    165.  
    166.  
    167.  
    168.  
    169.  
    170.  
    171.  
    172.     //---Main Functionality---//
    173.  
    174.     void Update()
    175.     {
    176.        
    177.  
    178.  
    179.         if (!on || !initialGo)
    180.         {
    181.  
    182.             return;
    183.  
    184.         }
    185.         else
    186.         {
    187.  
    188.             AIFunctionality();
    189.  
    190.         }
    191.  
    192.     }
    193.  
    194.  
    195.  
    196.     void AIFunctionality()
    197.     {
    198.  
    199.         if ((!target) && (requireTarget))
    200.         {
    201.  
    202.             return; //if no target was set and we require one, AI will not function.
    203.  
    204.         }
    205.  
    206.  
    207.  
    208.         //Functionality Updates
    209.  
    210.         lastVisTargetPos = target.position; //Target tracking method for semi-intelligent AI
    211.  
    212.         Vector3 moveToward = lastVisTargetPos - transform.position; //Used to face the AI in the direction of the target
    213.  
    214.         Vector3 moveAway = transform.position - lastVisTargetPos; //Used to face the AI away from the target when running away
    215.  
    216.         float distance = Vector3.Distance(transform.position, target.position);
    217.  
    218.  
    219.  
    220.         if (go)
    221.         {
    222.  
    223.             MonitorGravity();
    224.  
    225.         }
    226.  
    227.  
    228.  
    229.         if (!requireTarget)
    230.         {
    231.  
    232.             //waypoint only functionality
    233.  
    234.             Patrol();
    235.            
    236.  
    237.         }
    238.         else if (TargetIsInSight())
    239.         {
    240.  
    241.             if (!go)
    242.             { //useWaypoints is false and the player has exceeded moveableRadius, shutdown AI until player is near.
    243.  
    244.                 return;
    245.  
    246.             }
    247.  
    248.  
    249.  
    250.             if ((distance > attackRange) && (!runAway) && (!runTo))
    251.             {
    252.  
    253.                 enemyCanAttack = false; //the target is too far away to attack
    254.                 Debug.Log("Player Is Too Far Away For Drone To Attack");
    255.                 StopCoroutine(Attack());// My Addition
    256.  
    257.                 MoveTowards(moveToward); //move closer
    258.  
    259.             }
    260.             else if ((smoothAttackRangeBuffer) && (distance > attackRange + 5.0f))
    261.             {
    262.  
    263.                 smoothAttackRangeBuffer = false;
    264.  
    265.                 WalkNewPath();
    266.  
    267.             }
    268.             else if ((runAway || runTo) && (distance > runDistance) && (!executeBufferState))
    269.             {
    270.  
    271.                 //move in random directions.
    272.  
    273.                 if (monitorRunTo)
    274.                 {
    275.  
    276.                     monitorRunTo = false;
    277.  
    278.                 }
    279.  
    280.                 if (runAway)
    281.                 {
    282.  
    283.                     WalkNewPath();
    284.  
    285.                 }
    286.                 else
    287.                 {
    288.  
    289.                     MoveTowards(moveToward);
    290.  
    291.                 }
    292.  
    293.             }
    294.             else if ((runAway || runTo) && (distance < runDistance) && (!executeBufferState))
    295.             { //make sure they do not get too close to the target
    296.  
    297.                 //AHH! RUN AWAY!...  or possibly charge :D
    298.  
    299.                 enemyCanAttack = false; //can't attack, we're running!
    300.  
    301.                 if (!monitorRunTo)
    302.                 {
    303.  
    304.                     executeBufferState = true; //smooth buffer is now active!
    305.  
    306.                 }
    307.  
    308.                 walkInRandomDirection = false; //obviously we're no longer moving at random.
    309.  
    310.                 if (runAway)
    311.                 {
    312.  
    313.                     MoveTowards(moveAway); //move away
    314.  
    315.                 }
    316.                 else
    317.                 {
    318.  
    319.                     MoveTowards(moveToward); //move toward
    320.  
    321.                 }
    322.  
    323.             }
    324.             else if (executeBufferState && ((runAway) && (distance < runBufferDistance)) || ((runTo) && (distance > runBufferDistance)))
    325.             {
    326.  
    327.                 //continue to run!
    328.  
    329.                 if (runAway)
    330.                 {
    331.  
    332.                     MoveTowards(moveAway); //move away
    333.  
    334.                 }
    335.                 else
    336.                 {
    337.  
    338.                     MoveTowards(moveToward); //move toward
    339.  
    340.                 }
    341.  
    342.             }
    343.             else if ((executeBufferState) && (((runAway) && (distance > runBufferDistance)) || ((runTo) && (distance < runBufferDistance))))
    344.             {
    345.  
    346.                 monitorRunTo = true; //make sure that when we have made it to our buffer distance (close to user) we stop the charge until far enough away.
    347.  
    348.                 executeBufferState = false; //go back to normal activity
    349.  
    350.             }
    351.  
    352.  
    353.             //start attacking if close enough
    354.  
    355.             if ((distance < attackRange) || ((!runAway && !runTo) && (distance < runDistance)))
    356.             {
    357.  
    358.                 if (runAway)
    359.                 {
    360.  
    361.                     smoothAttackRangeBuffer = true;
    362.  
    363.                 }
    364.  
    365.                 if (Time.time > lastShotFired + attackTime)
    366.                 {
    367.                     patrolling = false;
    368.                     StartCoroutine(Attack());
    369.                     Debug.Log("Ready To Attack Player");
    370.  
    371.                 }
    372.  
    373.             }
    374.  
    375.  
    376.  
    377.         }
    378.         else if ((playerHasBeenSeen) && (!targetIsOutOfSight) && (go))
    379.         {
    380.  
    381.             lostPlayerTimer = Time.time + huntingTimer;
    382.  
    383.             StartCoroutine(HuntDownTarget(lastVisTargetPos));
    384.            
    385.  
    386.         }
    387.         else if (useWaypoints)
    388.         {
    389.  
    390.             Patrol();
    391.  
    392.         }
    393.         else if (((!playerHasBeenSeen) && (go)) && ((moveableRadius == 0) || (distance < moveableRadius)))
    394.         {
    395.  
    396.             //the idea here is that the enemy has not yet seen the player, but the player is fairly close while still not visible by the enemy
    397.  
    398.             //it will move in a random direction continuously altering its direction every 2 seconds until it does see the player.
    399.  
    400.             WalkNewPath();
    401.  
    402.         }
    403.  
    404.     }
    405.  
    406.     public bool patrolling = false;
    407.  
    408.     //attack stuff...
    409.  
    410.     IEnumerator Attack()
    411.     {
    412.  
    413.         enemyCanAttack = true;
    414.  
    415.  
    416.  
    417.         if (!enemyIsAttacking)
    418.         {
    419.  
    420.             enemyIsAttacking = true;
    421.  
    422.             while (enemyCanAttack)
    423.             {
    424.  
    425.                 lastShotFired = Time.time;
    426.  
    427.  
    428.                 // My Addition
    429.                 if (PlayerDeathScriptInstance.playerIsAlive == true && enemyCanAttack == true && patrolling == false)
    430.                 {
    431.                     if (!target)
    432.                     {
    433.                         if (beam)
    434.                             RemoveLastProjectiles();
    435.  
    436.                         //yield return new WaitForSeconds(attackTime);
    437.                     }
    438.                     if (beam && m_lastProjectiles.Count <= 0)
    439.                     {
    440.                         SpawnProjectiles();
    441.  
    442.                     }
    443.                     else if (beam && m_lastProjectiles.Count > 0)
    444.                     {
    445.                         RemoveLastProjectiles();
    446.  
    447.                     }
    448.                     else
    449.                     {
    450.                         m_fireTimer += Time.deltaTime;
    451.  
    452.                         if (m_fireTimer >= fireRate)
    453.                         {
    454.                             SpawnProjectiles();
    455.  
    456.                             m_fireTimer = 0.0f;
    457.  
    458.                         }
    459.                     }
    460.                 }
    461.                
    462.                 // End My Addition
    463.  
    464.  
    465.                
    466.  
    467.                 //implement attack variables here
    468.                
    469.                 yield return new WaitForSeconds(attackTime);
    470.  
    471.             }
    472.  
    473.         }
    474.  
    475.     }
    476.  
    477.  
    478.  
    479.  
    480.  
    481.  
    482.  
    483.     //----Helper Functions---//
    484.  
    485.     //verify enemy can see the target
    486.  
    487.     bool TargetIsInSight()
    488.     {
    489.  
    490.         //determine if the enemy should be doing anything other than standing still
    491.  
    492.         if ((moveableRadius > 0) && (Vector3.Distance(transform.position, target.position) > moveableRadius))
    493.         {
    494.  
    495.             go = false;
    496.  
    497.         }
    498.         else
    499.         {
    500.  
    501.             go = true;
    502.  
    503.         }
    504.  
    505.  
    506.  
    507.         //then lets make sure the target is within the vision radius we allowed our enemy
    508.  
    509.         //remember, 0 radius means to ignore this check
    510.  
    511.         if ((visualRadius > 0) && (Vector3.Distance(transform.position, target.position) > visualRadius))
    512.         {
    513.  
    514.             return false;
    515.  
    516.         }
    517.  
    518.  
    519.  
    520.         //Now check to make sure nothing is blocking the line of sight
    521.  
    522.         RaycastHit sight;
    523.  
    524.         if (Physics.Linecast(transform.position, target.position, out sight))
    525.         {
    526.  
    527.             if (!playerHasBeenSeen && sight.transform == target)
    528.             {
    529.  
    530.                 playerHasBeenSeen = true;
    531.                 Debug.Log("Player Has Been Seen");
    532.  
    533.             }
    534.  
    535.             return sight.transform == target;
    536.  
    537.         }
    538.         else
    539.         {
    540.  
    541.             return false;
    542.  
    543.         }
    544.  
    545.     }
    546.  
    547.  
    548.  
    549.     //target tracking
    550.  
    551.     IEnumerator HuntDownTarget(Vector3 position)
    552.     {
    553.  
    554.         //if this function is called, the enemy has lost sight of the target and must track him down!
    555.  
    556.         //assuming AI is not too intelligent, they will only move toward his last position, and hope they see him
    557.  
    558.         //this can be fixed later to update the lastVisTargetPos every couple of seconds to leave some kind of trail
    559.  
    560.         targetIsOutOfSight = true;
    561.  
    562.         while (targetIsOutOfSight)
    563.         {
    564.  
    565.             Vector3 moveToward = position - transform.position;
    566.  
    567.             MoveTowards(moveToward);
    568.  
    569.  
    570.  
    571.             //check if we found the target yet
    572.  
    573.             if (TargetIsInSight())
    574.             {
    575.  
    576.                 targetIsOutOfSight = false;
    577.  
    578.                 break;
    579.  
    580.             }
    581.  
    582.  
    583.  
    584.             //check to see if we should give up our search
    585.  
    586.             if (Time.time > lostPlayerTimer)
    587.             {
    588.  
    589.                 targetIsOutOfSight = false;
    590.  
    591.                 playerHasBeenSeen = false;
    592.  
    593.                 break;
    594.  
    595.             }
    596.  
    597.             yield return null;
    598.  
    599.         }
    600.  
    601.     }
    602.  
    603.  
    604.  
    605.     void Patrol()
    606.     {
    607.         Debug.Log("Patrolling");
    608.         patrolling = true;// My Addition
    609.         if (pauseWpControl)
    610.         {
    611.  
    612.             return;
    613.  
    614.         }
    615.  
    616.         Vector3 destination = CurrentPath();
    617.  
    618.         Vector3 moveToward = destination - transform.position;
    619.  
    620.         float distance = Vector3.Distance(transform.position, destination);
    621.  
    622.         MoveTowards(moveToward);
    623.  
    624.         if (distance <= 1.5f + floatHeight)
    625.         {// || (distance < floatHeight+1.5f)) {
    626.  
    627.             if (pauseAtWaypoints)
    628.             {
    629.  
    630.                 if (!pauseWpControl)
    631.                 {
    632.  
    633.                     pauseWpControl = true;
    634.  
    635.                     StartCoroutine(WaypointPause());
    636.  
    637.                 }
    638.  
    639.             }
    640.             else
    641.             {
    642.  
    643.                 NewPath();
    644.  
    645.             }
    646.  
    647.         }
    648.  
    649.     }
    650.  
    651.  
    652.  
    653.     IEnumerator WaypointPause()
    654.     {
    655.  
    656.         yield return new WaitForSeconds(Random.Range(pauseMin, pauseMax));
    657.  
    658.         NewPath();
    659.  
    660.         pauseWpControl = false;
    661.  
    662.     }
    663.  
    664.  
    665.  
    666.     Vector3 CurrentPath()
    667.     {
    668.  
    669.         return waypoints[wpPatrol].position;
    670.  
    671.     }
    672.  
    673.  
    674.  
    675.     void NewPath()
    676.     {
    677.  
    678.         if (!wpCountdown)
    679.         {
    680.  
    681.             wpPatrol++;
    682.  
    683.             if (wpPatrol >= waypoints.GetLength(0))
    684.             {
    685.  
    686.                 if (reversePatrol)
    687.                 {
    688.  
    689.                     wpCountdown = true;
    690.  
    691.                     wpPatrol -= 2;
    692.  
    693.                 }
    694.                 else
    695.                 {
    696.  
    697.                     wpPatrol = 0;
    698.  
    699.                 }
    700.  
    701.             }
    702.  
    703.         }
    704.         else if (reversePatrol)
    705.         {
    706.  
    707.             wpPatrol--;
    708.  
    709.             if (wpPatrol < 0)
    710.             {
    711.  
    712.                 wpCountdown = false;
    713.  
    714.                 wpPatrol = 1;
    715.  
    716.             }
    717.  
    718.         }
    719.  
    720.     }
    721.  
    722.  
    723.  
    724.     //random movement behaviour
    725.  
    726.     void WalkNewPath()
    727.     {
    728.  
    729.         if (!walkInRandomDirection)
    730.         {
    731.  
    732.             walkInRandomDirection = true;
    733.  
    734.             if (!playerHasBeenSeen)
    735.             {
    736.  
    737.                 randomDirection = new Vector3(Random.Range(-0.15f, 0.15f), 0, Random.Range(-0.15f, 0.15f));
    738.  
    739.             }
    740.             else
    741.             {
    742.  
    743.                 randomDirection = new Vector3(Random.Range(-0.5f, 0.5f), 0, Random.Range(-0.5f, 0.5f));
    744.  
    745.             }
    746.  
    747.             randomDirectionTimer = Time.time;
    748.  
    749.         }
    750.         else if (walkInRandomDirection)
    751.         {
    752.  
    753.             MoveTowards(randomDirection);
    754.  
    755.         }
    756.  
    757.  
    758.  
    759.         if ((Time.time - randomDirectionTimer) > 2)
    760.         {
    761.  
    762.             //choose a new random direction after 2 seconds
    763.  
    764.             walkInRandomDirection = false;
    765.  
    766.         }
    767.  
    768.     }
    769.  
    770.  
    771.  
    772.     //standard movement behaviour
    773.  
    774.     void MoveTowards(Vector3 direction)
    775.     {
    776.  
    777.         direction.y = 0;
    778.  
    779.         int speed = walkSpeed;
    780.  
    781.  
    782.  
    783.         if (walkInRandomDirection)
    784.         {
    785.  
    786.             speed = randomSpeed;
    787.  
    788.         }
    789.  
    790.  
    791.  
    792.         if (executeBufferState)
    793.         {
    794.  
    795.             speed = runSpeed;
    796.  
    797.         }
    798.  
    799.  
    800.  
    801.         //rotate toward or away from the target
    802.  
    803.         transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
    804.  
    805.         transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
    806.  
    807.  
    808.         //slow down when we are not facing the target
    809.  
    810.         Vector3 forward = transform.TransformDirection(Vector3.forward);
    811.  
    812.         float speedModifier = Vector3.Dot(forward, direction.normalized);
    813.  
    814.         speedModifier = Mathf.Clamp01(speedModifier);
    815.  
    816.  
    817.  
    818.         //actually move toward or away from the target
    819.  
    820.         direction = forward * speed * speedModifier;
    821.  
    822.         if ((!canFly) && (floatHeight <= 0.0f))
    823.         {
    824.  
    825.             direction.y -= gravity;
    826.  
    827.         }
    828.  
    829.         characterController.Move(direction * Time.deltaTime);
    830.  
    831.     }
    832.  
    833.  
    834.  
    835.     //continuous gravity checks
    836.  
    837.     void MonitorGravity()
    838.     {
    839.  
    840.         Vector3 direction = new Vector3(0, 0, 0);
    841.  
    842.  
    843.  
    844.         if ((!canFly) && (floatHeight > 0.0f))
    845.         {
    846.  
    847.             //we need to make sure our enemy is floating.. using evil raycasts! bwahahahah!
    848.  
    849.             if ((estimateElevation) && (estRayTimer > 0.0f))
    850.             {
    851.  
    852.                 if (Time.time > estGravityTimer)
    853.                 {
    854.  
    855.                     RaycastHit floatCheck;
    856.  
    857.                     if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck))
    858.                     {
    859.  
    860.                         if (floatCheck.distance < floatHeight - 0.5f)
    861.                         {
    862.  
    863.                             estCheckDirection = 1;
    864.  
    865.                             estHeight = floatHeight - floatCheck.distance;
    866.  
    867.                         }
    868.                         else if (floatCheck.distance > floatHeight + 0.5f)
    869.                         {
    870.  
    871.                             estCheckDirection = 2;
    872.  
    873.                             estHeight = floatCheck.distance - floatHeight;
    874.  
    875.                         }
    876.                         else
    877.                         {
    878.  
    879.                             estCheckDirection = 3;
    880.  
    881.                         }
    882.  
    883.                     }
    884.                     else
    885.                     {
    886.  
    887.                         estCheckDirection = 2;
    888.  
    889.                         estHeight = floatHeight * 2;
    890.  
    891.                     }
    892.  
    893.                     estGravityTimer = Time.time + estRayTimer;
    894.  
    895.                 }
    896.  
    897.  
    898.  
    899.                 switch (estCheckDirection)
    900.                 {
    901.  
    902.                     case 1:
    903.  
    904.                         direction.y += antigravity;
    905.  
    906.                         estHeight -= direction.y * Time.deltaTime;
    907.  
    908.                         break;
    909.  
    910.                     case 2:
    911.  
    912.                         direction.y -= gravity;
    913.  
    914.                         estHeight -= direction.y * Time.deltaTime;
    915.  
    916.                         break;
    917.  
    918.                     default:
    919.  
    920.                         //do nothing
    921.  
    922.                         break;
    923.  
    924.                 }
    925.  
    926.  
    927.  
    928.             }
    929.             else
    930.             {
    931.  
    932.                 RaycastHit floatCheck;
    933.  
    934.                 if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck, floatHeight + 1.0f))
    935.                 {
    936.  
    937.                     if (floatCheck.distance < floatHeight)
    938.                     {
    939.  
    940.                         direction.y += antigravity;
    941.  
    942.                     }
    943.  
    944.                 }
    945.                 else
    946.                 {
    947.  
    948.                     direction.y -= gravity;
    949.  
    950.                 }
    951.  
    952.             }
    953.  
    954.         }
    955.         else
    956.         {
    957.  
    958.             //bird like creature! Again with the evil raycasts! :p
    959.  
    960.             if ((estimateElevation) && (estRayTimer > 0.0f))
    961.             {
    962.  
    963.                 if (Time.time > estGravityTimer)
    964.                 {
    965.  
    966.                     RaycastHit floatCheck;
    967.  
    968.                     if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck))
    969.                     {
    970.  
    971.                         if (floatCheck.distance < floatHeight - 0.5f)
    972.                         {
    973.  
    974.                             estCheckDirection = 1;
    975.  
    976.                             estHeight = floatHeight - floatCheck.distance;
    977.  
    978.                         }
    979.                         else if (floatCheck.distance > floatHeight + 0.5f)
    980.                         {
    981.  
    982.                             estCheckDirection = 2;
    983.  
    984.                             estHeight = floatCheck.distance - floatHeight;
    985.  
    986.                         }
    987.                         else
    988.                         {
    989.  
    990.                             estCheckDirection = 3;
    991.  
    992.                         }
    993.  
    994.                     }
    995.  
    996.                     estGravityTimer = Time.time + estRayTimer;
    997.  
    998.                 }
    999.  
    1000.  
    1001.  
    1002.                 switch (estCheckDirection)
    1003.                 {
    1004.  
    1005.                     case 1:
    1006.  
    1007.                         direction.y += antigravity;
    1008.  
    1009.                         estHeight -= direction.y * Time.deltaTime;
    1010.  
    1011.                         break;
    1012.  
    1013.                     case 2:
    1014.  
    1015.                         direction.y -= antigravity;
    1016.  
    1017.                         estHeight -= direction.y * Time.deltaTime;
    1018.  
    1019.                         break;
    1020.  
    1021.                     default:
    1022.  
    1023.                         //do nothing
    1024.  
    1025.                         break;
    1026.  
    1027.                 }
    1028.  
    1029.  
    1030.  
    1031.             }
    1032.             else
    1033.             {
    1034.  
    1035.                 RaycastHit floatCheck;
    1036.  
    1037.                 if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck))
    1038.                 {
    1039.  
    1040.                     if (floatCheck.distance < floatHeight - 0.5f)
    1041.                     {
    1042.  
    1043.                         direction.y += antigravity;
    1044.  
    1045.                     }
    1046.                     else if (floatCheck.distance > floatHeight + 0.5f)
    1047.                     {
    1048.  
    1049.                         direction.y -= antigravity;
    1050.  
    1051.                     }
    1052.  
    1053.                 }
    1054.  
    1055.             }
    1056.  
    1057.         }
    1058.  
    1059.  
    1060.  
    1061.         if ((!estimateElevation) || ((estimateElevation) && (estHeight >= 0.0f)))
    1062.         {
    1063.  
    1064.             characterController.Move(direction * Time.deltaTime);
    1065.  
    1066.         }
    1067.  
    1068.     }
    1069.  
    1070.     // My Addition
    1071.     void SpawnProjectiles()
    1072.     {
    1073.         if (patrolling == false)
    1074.         {
    1075.             if (!projectile)
    1076.             {
    1077.                 return;
    1078.             }
    1079.  
    1080.             m_lastProjectiles.Clear();
    1081.  
    1082.             for (int i = 0; i < projectileSpawns.Count; i++)
    1083.             {
    1084.                 if (projectileSpawns[i])
    1085.                 {
    1086.                     GameObject proj = Instantiate(projectile, projectileSpawns[i].transform.position, Quaternion.Euler(projectileSpawns[i].transform.forward)) as GameObject;
    1087.                     proj.GetComponent<BaseProjectile>().FireProjectile(projectileSpawns[i], m_target, damage, fireRate);
    1088.  
    1089.                     m_lastProjectiles.Add(proj);
    1090.                     // Play Audio For Laser Fire
    1091.                     AudioSource audio = GetComponent<AudioSource>();
    1092.                     audio.clip = laserFireSFX_Clip;
    1093.                     audio.Play();
    1094.                     audio.loop = false;
    1095.                 }
    1096.             }
    1097.         }
    1098.     }
    1099.  
    1100.     public void SetTarget(GameObject target)
    1101.     {
    1102.         m_target = target;
    1103.     }
    1104.  
    1105.     void RemoveLastProjectiles()
    1106.     {
    1107.         while (m_lastProjectiles.Count > 0)
    1108.         {
    1109.             Destroy(m_lastProjectiles[0]);
    1110.             m_lastProjectiles.RemoveAt(0);
    1111.         }
    1112.     }
    1113.     // End My Addition
    1114. }
    1115.  
     
  42. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    In MoveTowards normalise the direction after you set the y to 0. You need a unit vector when using it with Quaternion.LookRotation or it can lead to unexpected results.

    For the laser its setup to deal dmg every x intervals. The beam projectile should have abit where it sends out a damage message. Stick in a breakpoint or a debug.log if its triggering too much for a breakpoint to help. if it randomly stops keep using logs & breakpoints to figure out where the root cause is.
     
  43. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515

    Thanks, I put a bunch of debugs in to try and locate where I'm messing up, took me a while to get the drone to stop firing the laser when the player was out of range and the drone retuned to just patrolling again.... for a while it was walking around with the laser still on and pointing out in front like a big _____.... well you get the idea ;)
    Anyways, I'm not sure which part of the script you mean? In the look towards player part?
    That part seems to work fine up until he starts to attack and then the laser is off target/player

    Yeah the laser not doing damage I can't figure out because sometimes when it hits it does do damage.... I thought it might have been an issue with the players collider but missiles from the turrets all work ok.... so I'm a little stumped.??
     
  44. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    The laser doesn't do damage based on collision, it runs on update on the projectile & inflicts damage every X interval. I think by default it doesn't do immediate damage so may be worth making it do damage straight away then start the delay for the next time to deal damage.

    In the script you pasted there is a function called MoveTowards, after the direction.y = 0.0f; just call direction.Normalise();
     
  45. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    if I try that here I get an error?

    Code (CSharp):
    1. //standard movement behaviour
    2.  
    3.     void MoveTowards(Vector3 direction)
    4.     {
    5.  
    6.         direction.y = 0;
    7.         direction.Normalise();
    8.  
    9.         int speed = walkSpeed;
    Also what would I need to modify in the beamProjectile script to make it do immediate damage??

    I think it was a spelling error. But I tried this:
    Code (CSharp):
    1. direction.y = 0f;
    2.         direction.Normalize();// My Addition
    And the drone still fires it laser off target and does not rotate towards the player, I'm SO confused here :(

    OK In the beamProjectile script I commented out this part:

    Code (CSharp):
    1. if (m_launcher)
    2.         {
    3.             GetComponent<LineRenderer>().SetPosition(0, m_launcher.transform.position);
    4.             GetComponent<LineRenderer>().SetPosition(1, m_launcher.transform.position + (m_launcher.transform.forward * beamLength));
    5.             RaycastHit hit;
    6.  
    7.             if (Physics.Raycast(m_launcher.transform.position, m_launcher.transform.forward, out hit, beamLength))
    8.             {
    9.                 if (hit.transform.gameObject == m_target)
    10.                 {
    11.                     //if (m_attackTimer >= m_attackSpeed)
    12.                     //{
    13.                         DamageData dmgData = new DamageData();
    14.                         dmgData.damage = m_damage;
    15.  
    16.                         MessageHandler msgHandler = m_target.GetComponent<MessageHandler>();
    17.  
    18.                         if (msgHandler)
    19.                         {
    20.                             msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    21.                         }
    22.  
    23.                         m_attackTimer = 0.0f;
    24.                     //}
    25.                 }
    26.             }
    27.         }
    So it's having the damage effect I want when I set the damage to "1" in the inspector so that way each time the beam hits my player it goes down a little for the time the beam is in contact with the player.... now if I can just get the drone to turn towards my player.

    I think some of the issue might be because both the beamProjectile and the DroneAI both have their own attack times which might be conflicting??
     
    Last edited: Feb 10, 2020
  46. elitglad

    elitglad

    Joined:
    Feb 16, 2020
    Posts:
    4
    Thank you, I hope you follow through with the Tut.



    ________________________________________________________
    Kodi Lucky Patcher nox
     
    Last edited: Feb 17, 2020
  47. MalikCreations

    MalikCreations

    Joined:
    Jul 2, 2020
    Posts:
    1
    Hi, ive looked everywhere for a solution to this and i cant find it so i wanted to ask you.

    Basic idea is that your towers will target the enemy closest to the end, not closest to the tower, it should also work when the enemies are at different speeds

    This is to ensure that the towers target the enemy with biggest threat and not ignore it if another enemy is nearby

    I havent found a solution to this and ive been trying to solve it for days now if u could please help
     
  48. Ignsn1alt

    Ignsn1alt

    Joined:
    Jan 11, 2022
    Posts:
    1
    hey i have a question, is it possible to make the turret get more damage if standing on something else for example
    if the turret is standing on wood stay the same, if the turret is standing on metal it gains more damage but less range and so on
     
  49. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Hey,

    Sorry for the slow response not on here often.

    You would want to add a new message for stat modification & send that when you place a turret on specific terrain.

    You can work out what the turret was placed on by doing a raycast down at that location to check the floor object & have a script attached to the floor with its effects.

    Hope this helps,

    Cheers,
    Chris
     
  50. Mrpirateyt

    Mrpirateyt

    Joined:
    Sep 5, 2022
    Posts:
    12
    Hi all, i am receiving the following Error
    nullReferenceException: Object reference not set to an instance of an object
    Shootingai.SpawnProjectiles () (at Assets/Shootingai.cs:89)
    Shootingai.Update () (at Assets/Shootingai.cs:67)

    the code at line 89 is proj.GetComponent<BaseProjectiler>().FireProjectile(projectileSpawns, m_target, damage, fireRate);

    and the code for 67 is
    SpawnProjectiles();

    I Did rename the scripts to my liking but i made sure that the names were put in correctly. it used to Fire.... well sort of it would show the projectile at the Projectile spawn point, but the projectile will not make it way towards the target, but it still effected the target because of that i change the Field of view to 20 and the rate of fire to 0.1 and sphere collider radius to 1000 then the error started to show up and now it shows a constant beam of projectiles i even reverted the Field of view back to 10 the Rate of fire to 0.2 and the radius to 700 and it is still a constant beam of the projectile but will not shoot at the target (the players Jet). the turret is trying to track a really fast object ( A jet at Irl Speeds of Mach 1.5) while sitting on a flying object following a set of waypoints could this cause a problem? also if the is some where i can send pictures to better explain this problem Please let me know

    -Mr Pirate