Search Unity

Unity Turret Tutorial

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

  1. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    I am currently creating a series of videos to show how to make an automated turret in unity. It is going to be spread across a few videos, but the first one is up now.

    Part 1 - Tracking



    TrackingSystem.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class TrackingSystem : MonoBehaviour {
    6.     public float speed = 3.0f;
    7.  
    8.     GameObject m_target = null;
    9.     Vector3 m_lastKnownPosition = Vector3.zero;
    10.     Quaternion m_lookAtRotation;
    11.  
    12.     // Update is called once per frame
    13.     void Update () {
    14.         if(m_target){
    15.             if(m_lastKnownPosition != m_target.transform.position){
    16.                 m_lastKnownPosition = m_target.transform.position;
    17.                 m_lookAtRotation = Quaternion.LookRotation(m_lastKnownPosition - transform.position);
    18.             }
    19.  
    20.             if(transform.rotation != m_lookAtRotation){
    21.                 transform.rotation = Quaternion.RotateTowards(transform.rotation, m_lookAtRotation, speed * Time.deltaTime);
    22.             }
    23.         }
    24.     }
    25.  
    26.     public void SetTarget(GameObject target){
    27.         m_target = target;
    28.     }
    29. }
    30.  
    EnemyAi.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class EnemyAi : MonoBehaviour {
    6.     public Transform pointA;
    7.     public Transform pointB;
    8.     public float speed;
    9.  
    10.     // Update is called once per frame
    11.     void Update () {
    12.         transform.position = Vector3.Lerp(pointA.position, pointB.position, Mathf.Pow(Mathf.Sin(Time.time * speed), 2));
    13.     }
    14. }
    15.  
    Part 2 - Projectiles & Shooting

    A -

    B -


    BaseProjectile.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public abstract class BaseProjectile : MonoBehaviour {
    6.     public float speed = 5.0f;
    7.  
    8.     public abstract void FireProjectile(GameObject launcher, GameObject target, int damage, float attackSpeed);
    9. }
    10.  
    NormalProjectile.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class NormalProjectile : BaseProjectile {
    6. using UnityEngine;
    7. using System.Collections;
    8.  
    9. public class NormalProjectile : BaseProjectile {
    10.     Vector3 m_direction;
    11.     bool m_fired;
    12.     GameObject m_launcher;
    13.     GameObject m_target;
    14.     int m_damage;
    15.  
    16.     // Update is called once per frame
    17.     void Update () {
    18.         if(m_fired){
    19.             transform.position += m_direction * (speed * Time.deltaTime);
    20.         }
    21.     }
    22.  
    23.     public override void FireProjectile(GameObject launcher, GameObject target, int damage, float attackSpeed){
    24.         if(launcher && target){
    25.             m_direction = (target.transform.position - launcher.transform.position).normalized;
    26.             m_fired = true;
    27.             m_launcher = launcher;
    28.             m_target = target;
    29.             m_damage = damage;
    30.  
    31.             Destroy(gameObject, 10.0f);
    32.         }
    33.     }
    34.  
    35.     void OnCollisionEnter(Collision other)
    36.     {
    37.         if(other.gameObject == m_target)
    38.         {
    39.             DamageData dmgData = new DamageData();
    40.             dmgData.damage = m_damage;
    41.  
    42.             MessageHandler msgHandler = m_target.GetComponent<MessageHandler>();
    43.  
    44.             if(msgHandler)
    45.             {
    46.                 msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    47.             }
    48.         }
    49.  
    50.         if(other.gameObject.GetComponent<BaseProjectile>() == null)
    51.             Destroy(gameObject);
    52.     }
    53. }
    54.  
    TrackingProjectile.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class TrackingProjectile : BaseProjectile {
    6.     GameObject m_target;
    7.     GameObject m_launcher;
    8.     int m_damage;
    9.  
    10.     Vector3 m_lastKnownPosition;
    11.  
    12.     // Update is called once per frame
    13.     void Update () {
    14.         if(m_target){
    15.             m_lastKnownPosition = m_target.transform.position;
    16.         }
    17.         else
    18.         {
    19.             if(transform.position == m_lastKnownPosition)
    20.             {
    21.                 Destroy(gameObject);
    22.             }
    23.         }
    24.  
    25.         transform.position = Vector3.MoveTowards(transform.position, m_lastKnownPosition, speed * Time.deltaTime);
    26.     }
    27.  
    28.     public override void FireProjectile(GameObject launcher, GameObject target, int damage, float attackSpeed){
    29.         if(target){
    30.             m_target = target;
    31.             m_lastKnownPosition = target.transform.position;
    32.             m_launcher = launcher;
    33.             m_damage = damage;
    34.         }
    35.     }
    36.  
    37.     void OnCollisionEnter(Collision other)
    38.     {
    39.         if(other.gameObject == m_target)
    40.         {
    41.             DamageData dmgData = new DamageData();
    42.             dmgData.damage = m_damage;
    43.          
    44.             MessageHandler msgHandler = m_target.GetComponent<MessageHandler>();
    45.          
    46.             if(msgHandler)
    47.             {
    48.                 msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    49.             }
    50.         }
    51.      
    52.         if(other.gameObject.GetComponent<BaseProjectile>() == null)
    53.             Destroy(gameObject);
    54.     }
    55. }
    56.  
    BeamProjectile.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class BeamProjectile : BaseProjectile {
    6.     public float beamLength = 10.0f;
    7.     GameObject m_launcher;
    8.     GameObject m_target;
    9.     int m_damage;
    10.     float m_attackSpeed;
    11.     float m_attackTimer;
    12.  
    13.     // Update is called once per frame
    14.     void Update () {
    15.         m_attackTimer += Time.deltaTime;
    16.  
    17.         if(m_launcher){
    18.             GetComponent<LineRenderer>().SetPosition(0, m_launcher.transform.position);
    19.             GetComponent<LineRenderer>().SetPosition(1, m_launcher.transform.position + (m_launcher.transform.forward * beamLength));
    20.             RaycastHit hit;
    21.  
    22.             if(Physics.Raycast(m_launcher.transform.position, m_launcher.transform.forward, out hit, beamLength))
    23.             {
    24.                 if(hit.transform.gameObject == m_target)
    25.                 {
    26.                     if(m_attackTimer >= m_attackSpeed)
    27.                     {
    28.                         DamageData dmgData = new DamageData();
    29.                         dmgData.damage = m_damage;
    30.                      
    31.                         MessageHandler msgHandler = m_target.GetComponent<MessageHandler>();
    32.                      
    33.                         if(msgHandler)
    34.                         {
    35.                             msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    36.                         }
    37.  
    38.                         m_attackTimer = 0.0f;
    39.                     }
    40.                 }
    41.             }
    42.         }
    43.     }
    44.  
    45.     public override void FireProjectile(GameObject launcher, GameObject target, int damage, float attackSpeed){
    46.         if(launcher){
    47.             m_launcher = launcher;
    48.             m_target = target;
    49.             m_damage = damage;
    50.             m_attackSpeed = attackSpeed;
    51.             m_attackTimer = 0.0f;
    52.         }
    53.     }
    54. }
    55.  
    56.  
    ShootingSystem.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class ShootingSystem : MonoBehaviour {
    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.         if(!m_target)
    21.         {
    22.             if(beam)
    23.                 RemoveLastProjectiles();
    24.  
    25.             return;
    26.         }
    27.  
    28.         if(beam && m_lastProjectiles.Count <= 0){
    29.             float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(m_target.transform.position - transform.position));
    30.          
    31.             if(angle < fieldOfView){
    32.                 SpawnProjectiles();
    33.             }
    34.         }else if(beam && m_lastProjectiles.Count > 0){
    35.             float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(m_target.transform.position - transform.position));
    36.  
    37.             if(angle > fieldOfView){
    38.                 RemoveLastProjectiles();
    39.             }
    40.         }else{
    41.             m_fireTimer += Time.deltaTime;
    42.  
    43.             if(m_fireTimer >= fireRate){
    44.                 float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(m_target.transform.position - transform.position));
    45.              
    46.                 if(angle < fieldOfView){
    47.                     SpawnProjectiles();
    48.                  
    49.                     m_fireTimer = 0.0f;
    50.                 }
    51.             }
    52.         }
    53.     }
    54.  
    55.     void SpawnProjectiles(){
    56.         if(!projectile){
    57.             return;
    58.         }
    59.  
    60.         m_lastProjectiles.Clear();
    61.  
    62.         for(int i = 0; i < projectileSpawns.Count; i++){
    63.             if(projectileSpawns[i]){
    64.                 GameObject proj = Instantiate(projectile, projectileSpawns[i].transform.position, Quaternion.Euler(projectileSpawns[i].transform.forward)) as GameObject;
    65.                 proj.GetComponent<BaseProjectile>().FireProjectile(projectileSpawns[i], m_target, damage, fireRate);
    66.  
    67.                 m_lastProjectiles.Add(proj);
    68.             }
    69.         }
    70.     }
    71.  
    72.     public void SetTarget(GameObject target){
    73.         m_target = target;
    74.     }
    75.  
    76.     void RemoveLastProjectiles()
    77.     {
    78.         while(m_lastProjectiles.Count > 0){
    79.             Destroy(m_lastProjectiles[0]);
    80.             m_lastProjectiles.RemoveAt(0);
    81.         }
    82.     }
    83. }
    84.  
    Part 3 - TurretAi



    RangeChecker.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class RangeChecker : MonoBehaviour {
    7.     public List<string> tags;
    8.  
    9.     List<GameObject> m_targets = new List<GameObject>();
    10.  
    11.     void OnTriggerEnter(Collider other)
    12.     {
    13.         bool invalid = true;
    14.  
    15.         for(int i = 0; i < tags.Count; i++)
    16.         {
    17.             if(other.CompareTag(tags[i]))
    18.             {
    19.                 invalid = false;
    20.                 break;
    21.             }
    22.         }
    23.  
    24.         if(invalid)
    25.             return;
    26.  
    27.         m_targets.Add(other.gameObject);
    28.     }
    29.  
    30.     void OnTriggerExit(Collider other)
    31.     {
    32.         for(int i = 0; i < m_targets.Count; i++)
    33.         {
    34.             if(other.gameObject == m_targets[i])
    35.             {
    36.                 m_targets.Remove(other.gameObject);
    37.                 return;
    38.             }
    39.         }
    40.     }
    41.  
    42.     public List<GameObject> GetValidTargets()
    43.     {
    44.         return m_targets;
    45.     }
    46.  
    47.     public bool InRange(GameObject go)
    48.     {
    49.         for(int i = 0; i < m_targets.Count; i++)
    50.         {
    51.             if(go == m_targets[i])
    52.                 return true;
    53.         }
    54.  
    55.         return false;
    56.     }
    57. }
    58.  
    TurretAi.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class TurretAi : MonoBehaviour {
    7.     public enum AiStates{NEAREST, FURTHEST, WEAKEST, STRONGEST};
    8.  
    9.     public AiStates aiState = AiStates.NEAREST;
    10.  
    11.     TrackingSystem m_tracker;
    12.     ShootingSystem m_shooter;
    13.     RangeChecker   m_range;
    14.  
    15.     // Use this for initialization
    16.     void Start () {
    17.         m_tracker =  GetComponent<TrackingSystem>();
    18.         m_shooter =  GetComponent<ShootingSystem>();
    19.         m_range   =  GetComponent<RangeChecker>();
    20.     }
    21.  
    22.     // Update is called once per frame
    23.     void Update () {
    24.         if(!m_tracker || !m_shooter || !m_range)
    25.             return;
    26.  
    27.         switch(aiState)
    28.         {
    29.         case AiStates.NEAREST:
    30.             TargetNearest();
    31.             break;
    32.         case AiStates.FURTHEST:
    33.             TargetFurthest();
    34.             break;
    35.         case AiStates.WEAKEST:
    36.             TargetWeakest();
    37.             break;
    38.         case AiStates.STRONGEST:
    39.             TargetStrongest();
    40.             break;
    41.         }
    42.     }
    43.  
    44.     void TargetNearest()
    45.     {
    46.         List<GameObject> validTargets = m_range.GetValidTargets();
    47.  
    48.         GameObject curTarget = null;
    49.         float closestDist = 0.0f;
    50.  
    51.         for(int i = 0; i < validTargets.Count; i++)
    52.         {
    53.             float dist = Vector3.Distance(transform.position, validTargets[i].transform.position);
    54.  
    55.             if(!curTarget || dist < closestDist)
    56.             {
    57.                 curTarget = validTargets[i];
    58.                 closestDist = dist;
    59.             }
    60.         }
    61.  
    62.         m_tracker.SetTarget(curTarget);
    63.         m_shooter.SetTarget(curTarget);
    64.     }
    65.  
    66.     void TargetFurthest()
    67.     {
    68.         List<GameObject> validTargets = m_range.GetValidTargets();
    69.      
    70.         GameObject curTarget = null;
    71.         float furthestDist = 0.0f;
    72.      
    73.         for(int i = 0; i < validTargets.Count; i++)
    74.         {
    75.             float dist = Vector3.Distance(transform.position, validTargets[i].transform.position);
    76.          
    77.             if(!curTarget || dist > furthestDist)
    78.             {
    79.                 curTarget = validTargets[i];
    80.                 furthestDist = dist;
    81.             }
    82.         }
    83.      
    84.         m_tracker.SetTarget(curTarget);
    85.         m_shooter.SetTarget(curTarget);
    86.     }
    87.  
    88.     void TargetWeakest()
    89.     {
    90.         List<GameObject> validTargets = m_range.GetValidTargets();
    91.  
    92.         GameObject curTarget = null;
    93.         int lowestHealth = 0;
    94.  
    95.         for(int i = 0; i < validTargets.Count; i++)
    96.         {
    97.             int maxHp = validTargets[i].GetComponent<Health>().maxHealth;
    98.  
    99.             if(!curTarget || maxHp < lowestHealth)
    100.             {
    101.                 lowestHealth = maxHp;
    102.                 curTarget = validTargets[i];
    103.             }
    104.         }
    105.  
    106.         m_tracker.SetTarget(curTarget);
    107.         m_shooter.SetTarget(curTarget);
    108.     }
    109.  
    110.     void TargetStrongest()
    111.     {
    112.         List<GameObject> validTargets = m_range.GetValidTargets();
    113.      
    114.         GameObject curTarget = null;
    115.         int highestHealth = 0;
    116.      
    117.         for(int i = 0; i < validTargets.Count; i++)
    118.         {
    119.             int maxHp = validTargets[i].GetComponent<Health>().maxHealth;
    120.          
    121.             if(!curTarget || maxHp > highestHealth)
    122.             {
    123.                 highestHealth = maxHp;
    124.                 curTarget = validTargets[i];
    125.             }
    126.         }
    127.      
    128.         m_tracker.SetTarget(curTarget);
    129.         m_shooter.SetTarget(curTarget);
    130.     }
    131. }
    132.  
     
    Last edited: Aug 31, 2015
  2. sadsack

    sadsack

    Joined:
    May 27, 2015
    Posts:
    156
    Thank you, I hope you follow through with the Tut.
    renny
     
  3. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Part 2 is out now
     
  4. Smingleigh

    Smingleigh

    Joined:
    Nov 24, 2012
    Posts:
    9
    This is great, thanks for going the extra step to show how to implement different types of projectiles.
     
    lordconstant likes this.
  5. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Glad you like it. I will hopefully do the next part this weekend, been busy with work lately.
     
  6. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Part 3 is finally out.

    I apologise for the long delay & the next part will hopefully be out very soon.
     
  7. Smingleigh

    Smingleigh

    Joined:
    Nov 24, 2012
    Posts:
    9
    More good stuff, thanks for the time you put into this. I've learnt a lot.
     
  8. JoakimCarlsson

    JoakimCarlsson

    Joined:
    Aug 27, 2014
    Posts:
    65
    Looking good, best of luck to you.
     
    EliasMasche likes this.
  9. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Turn's out I can't have more than five videos per forum post, so I can't have everything at the top sorry.

    Part 4A - Messaging & Health



    MessageHandler.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public abstract class MessageData{};
    7. public enum MessageType{DAMAGED, HEALTHCHANGED, DIED};
    8. public delegate void MessageDelegate(MessageType msgType, GameObject go, MessageData msgData);
    9.  
    10. public class MessageHandler : MonoBehaviour {
    11.     public List<MessageType> messages;
    12.  
    13.     List<MessageDelegate> m_messageDelegates = new List<MessageDelegate>();
    14.  
    15.     public void RegisterDelegate(MessageDelegate msgDele)
    16.     {
    17.         m_messageDelegates.Add(msgDele);
    18.     }
    19.  
    20.     public bool GiveMessage(MessageType msgType, GameObject go, MessageData msgData)
    21.     {
    22.         bool approved = false;
    23.  
    24.         for(int i = 0; i < messages.Count; i++)
    25.         {
    26.             if(messages[i] == msgType)
    27.             {
    28.                 approved = true;
    29.                 break;
    30.             }
    31.         }
    32.  
    33.         if(!approved)
    34.             return false;
    35.  
    36.         for(int i = 0; i < m_messageDelegates.Count; i++)
    37.         {
    38.             m_messageDelegates[i](msgType, go, msgData);
    39.         }
    40.  
    41.         return true;
    42.     }
    43. }
    44.  
    45. public class DamageData : MessageData{
    46.     public int damage;
    47. }
    48.  
    49. public class DeathData : MessageData{
    50.     public GameObject attacker;
    51.     public GameObject attacked;
    52. }
    53.  
    54. public class HealthData : MessageData{
    55.     public int maxHealth;
    56.     public int curHealth;
    57. }
    58.  
    Health.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Health : MonoBehaviour {
    6.     public int maxHealth = 100;
    7.  
    8.     int m_curHealth;
    9.     MessageHandler m_messageHandler;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         m_curHealth = maxHealth;
    14.         m_messageHandler = GetComponent<MessageHandler>();
    15.  
    16.         if(m_messageHandler)
    17.         {
    18.             m_messageHandler.RegisterDelegate(RecieveMessage);
    19.         }
    20.     }
    21.  
    22.     void RecieveMessage(MessageType msgType, GameObject go, MessageData msgData)
    23.     {
    24.         switch(msgType)
    25.         {
    26.         case MessageType.DAMAGED:
    27.             DamageData dmgData = msgData as DamageData;
    28.  
    29.             if(dmgData != null)
    30.             {
    31.                 DoDamage(dmgData.damage, go);
    32.             }
    33.             break;
    34.         }
    35.     }
    36.  
    37.     void DoDamage(int dmg, GameObject go)
    38.     {
    39.         m_curHealth -= dmg;
    40.  
    41.         if(m_curHealth <= 0)
    42.         {
    43.             m_curHealth = 0;
    44.  
    45.             if(m_messageHandler)
    46.             {
    47.                 DeathData deathData = new DeathData();
    48.                 deathData.attacker = go;
    49.                 deathData.attacked = gameObject;
    50.  
    51.                 m_messageHandler.GiveMessage(MessageType.DIED, gameObject, deathData);
    52.             }
    53.         }
    54.  
    55.         if(m_messageHandler)
    56.         {
    57.             HealthData hpData = new HealthData();
    58.  
    59.             hpData.maxHealth = maxHealth;
    60.             hpData.curHealth = m_curHealth;
    61.  
    62.             m_messageHandler.GiveMessage(MessageType.HEALTHCHANGED, gameObject, hpData);
    63.         }
    64.     }
    65. }
    66.  
    Part 4B - Health UI & Damage



    HealthUi.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEngine.UI;
    5.  
    6. public class HealthUi : MonoBehaviour {
    7.     public Slider slider;
    8.  
    9.     // Use this for initialization
    10.     void Start () {
    11.         MessageHandler msgHandler = GetComponent<MessageHandler>();
    12.  
    13.         if(msgHandler)
    14.         {
    15.             msgHandler.RegisterDelegate(RecieveMessage);
    16.         }
    17.     }
    18.  
    19.  
    20.     void RecieveMessage(MessageType msgType, GameObject go, MessageData msgData)
    21.     {
    22.         switch(msgType)
    23.         {
    24.         case MessageType.HEALTHCHANGED:
    25.             HealthData hpData = msgData as HealthData;
    26.          
    27.             if(hpData != null)
    28.             {
    29.                 UpdateUi(hpData.maxHealth, hpData.curHealth);
    30.             }
    31.             break;
    32.         }
    33.     }
    34.  
    35.     void UpdateUi(int maxHealth, int curHealth)
    36.     {
    37.         slider.value = (1.0f/maxHealth) * curHealth;
    38.     }
    39. }
    40.  
    Part 4C - Death & Turret Ai Continued



    Death.cs

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Death : MonoBehaviour {
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.         MessageHandler msgHandler = GetComponent<MessageHandler>();
    10.      
    11.         if(msgHandler)
    12.         {
    13.             msgHandler.RegisterDelegate(RecieveMessage);
    14.         }
    15.     }
    16.  
    17.  
    18.     void RecieveMessage(MessageType msgType, GameObject go, MessageData msgData)
    19.     {
    20.         switch(msgType)
    21.         {
    22.         case MessageType.DIED:
    23.             DeathData dthData = msgData as DeathData;
    24.  
    25.             if(dthData != null)
    26.             {
    27.                 Die();
    28.             }
    29.             break;
    30.         }
    31.     }
    32.  
    33.     void Die()
    34.     {
    35.         Destroy(gameObject);
    36.     }
    37. }
    38.  
     
    Smingleigh and EliasMasche like this.
  10. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    For the next part of the tutorials I am going to be focusing on the enemies to help with turret testing. While I am setting this up I was wondering what people would like to see next with the turret.

    so far I've got the following things I would like to incorporate:

    - Player controlled turret
    - Smart tracking (Calculates where the target will be next)
    - Aoe turret
    - Special projectiles e.g. slow targets, do damage over time after hitting a target, bouncing damage etc.

    I will probably think of a few more as I record more, but right now these are the things I want to add.

    If you have anything that you are struggling with or would just like to see incorporated just leave a reply & Ill see what I can do.

    Thanks :)
     
    MHUnity likes this.
  11. Basbo

    Basbo

    Joined:
    Oct 3, 2013
    Posts:
    11
    Hi, thanks for this great series!

    Currently I have problems with Death.cs, if Die() is executed, I instantly get this error:
    Code (CSharp):
    1. MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
    2. Your script should either check if it is null or you should not destroy the object.
    3. TurretAi.TargetNearest () (at Assets/01-Scripts/Towers/TurretAi.cs:57)
    4. TurretAi.Update () (at Assets/01-Scripts/Towers/TurretAi.cs:34)
    5.  
    Someone can help?
     
  12. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Sorry for the late reply, been really busy with work lately.

    This bug is my fault, I pre-code everything then strip it away & record myself redoing it. I forgot to remove & redo the fix in my video.

    In the RangeChecker class you need to just change your GetValidTargets() to remove null targets:

    Code (CSharp):
    1. public List<GameObject> GetValidTargets()
    2.     {
    3.         for(int i = 0; i < m_targets.Count; i++)
    4.         {
    5.             if(m_targets[i] == null)
    6.             {
    7.                 m_targets.RemoveAt(i);
    8.                 i--;
    9.             }
    10.         }
    11.  
    12.         return m_targets;
    13.     }
    This should fix the null reference exception, any more issues just ask & I'll help asap (Hopefully faster than this :p)
     
  13. TheOcelot4000

    TheOcelot4000

    Joined:
    Dec 12, 2014
    Posts:
    3
    Why am i getting this error?
    Assets/Scripts/NormalProjectile.cs(7,14): error CS0101: The namespace `global::' already contains a definition for `NormalProjectile'
     
  14. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    This is generally caused by having two classes named NormalProjectile.
     
  15. TheOcelot4000

    TheOcelot4000

    Joined:
    Dec 12, 2014
    Posts:
    3
    Ohhhhhhhh i found the duplicate thanks!
     
  16. TheOcelot4000

    TheOcelot4000

    Joined:
    Dec 12, 2014
    Posts:
    3
    Ok i Found another error But with TurretAi.cs This time Here is the error And sorry about all this im a noob at coding xD

    Assets/Scripts/TurretAi.cs(65,19): error CS0122: `TrackingSystem.SetTarget(UnityEngine.GameObject)' is inaccessible due to its protection level

    What does this mean?
     
  17. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    You missed public from the function declaration. You probably have it like this: Void SetTarget, but it needs to be like this: Public Void SetTarget
     
  18. undead504

    undead504

    Joined:
    Apr 7, 2015
    Posts:
    2
    All is good so far but i need to edit the X Axis on the Turret to keep it form looking up when the Object gets closer.
     
  19. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    First of all create a public variable to use as the minimum distance on the tracking script.
    Next if we have a target do a distance check each frame to see if its less than the minimum range.
    Since you want to limit the pitch(x axis) you can just clamp the y value of the result of the last known position and the current position, 1 would be directly up, -1 would be down.
    Once youve clamped the y value pass it to the rotate towards function.
     
    Last edited: Jun 24, 2016
  20. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Its been a long time, but finally carrying this on.

    Part 5 - Pathing


    PathGenerator.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public struct PathNode
    6. {
    7.     public Transform transform;
    8.     public float dist;
    9.     public float minDist;
    10.     public float maxDist;
    11. }
    12.  
    13. public class PathGenerator : MonoBehaviour {
    14.     public bool looped;
    15.     List<PathNode> m_nodes = new List<PathNode>();
    16.     float m_totalDist;
    17.  
    18.     // Use this for initialization
    19.     void Start () {
    20.         FindNodes(transform);
    21.         CalculatePath();
    22.     }
    23.    
    24.     void FindNodes(Transform parent)
    25.     {
    26.         foreach(Transform child in parent)
    27.         {
    28.             if(child.childCount == 0)
    29.             {
    30.                 PathNode newNode = new PathNode();
    31.                 newNode.transform = child;
    32.                 m_nodes.Add(newNode);
    33.             }
    34.  
    35.             FindNodes(child);
    36.         }
    37.     }
    38.  
    39.     void CalculatePath()
    40.     {
    41.         float totalDist = 0.0f;
    42.  
    43.         for(int i = 0; i < m_nodes.Count; i++)
    44.         {
    45.             PathNode curNode = m_nodes[i];
    46.             PathNode nextNode;
    47.  
    48.             if(looped && i+1 == m_nodes.Count)
    49.                 nextNode = m_nodes[0];
    50.             else if(i+1 != m_nodes.Count)
    51.                 nextNode = m_nodes[i+1];
    52.             else
    53.                 nextNode = new PathNode();
    54.  
    55.             float dist = 0.0f;
    56.  
    57.             if(nextNode.transform)
    58.                 dist = Vector3.Distance(curNode.transform.position, nextNode.transform.position);
    59.  
    60.             curNode.dist = dist;
    61.             curNode.minDist = totalDist;
    62.             totalDist += dist;
    63.             curNode.maxDist = totalDist;
    64.  
    65.             m_nodes[i] = curNode;
    66.         }
    67.  
    68.         m_totalDist = totalDist;
    69.     }
    70.  
    71.     public PathNode GetPointOnPath(float progress, ref Vector3 pos)
    72.     {
    73.         if(looped)
    74.             progress = progress - Mathf.Floor(progress);
    75.         else if(progress > 1.0f)
    76.             progress = 1.0f;
    77.         else if(progress < 0.0f)
    78.             progress = 0.0f;
    79.  
    80.         float curDist = m_totalDist * progress;
    81.         int id = (int)Mathf.Floor(m_nodes.Count/2.0f);
    82.  
    83.         while(true)
    84.         {
    85.             if(m_nodes[id].minDist > curDist)
    86.             {
    87.                 id--;
    88.             }
    89.             else if(m_nodes[id].maxDist < curDist)
    90.             {
    91.                 id++;
    92.             }
    93.             else if(m_nodes[id].minDist <= curDist && m_nodes[id].maxDist >= curDist)
    94.             {
    95.                 float lerpProg = m_nodes[id].dist == 0.0f ? 0.0f : (curDist - m_nodes[id].minDist) / m_nodes[id].dist;
    96.                 pos = Vector3.Lerp(m_nodes[id].transform.position, m_nodes[id+1 >= m_nodes.Count ? 0 : id+1].transform.position, lerpProg);
    97.                 return m_nodes[id];
    98.             }
    99.         }
    100.     }
    101.  
    102.     public float GetTotalDistance()
    103.     {
    104.         return m_totalDist;
    105.     }
    106. }
    107.  
    ProgressAlongPathSpeed.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class ProgressAlongPathSpeed : MonoBehaviour {
    5.     public PathGenerator path = null;
    6.     public float speed = 2.0f;
    7.  
    8.     float m_progress = 0.0f;
    9.  
    10.     // Update is called once per frame
    11.     void Update () {
    12.         m_progress += ((1.0f / path.GetTotalDistance()) * speed) * Time.deltaTime;
    13.  
    14.         Vector3 pos = new Vector3();
    15.         path.GetPointOnPath(m_progress, ref pos);
    16.         transform.position = pos;
    17.     }
    18. }
    19.  
    ProgressAlongPathTime.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class ProgressAlongPathTime : MonoBehaviour {
    5.     public PathGenerator path = null;
    6.     public float time = 10.0f;
    7.    
    8.     float m_progress = 0.0f;
    9.    
    10.     // Update is called once per frame
    11.     void Update () {
    12.         m_progress += Time.deltaTime / time;
    13.        
    14.         Vector3 pos = new Vector3();
    15.         path.GetPointOnPath(m_progress, ref pos);
    16.         transform.position = pos;
    17.     }
    18. }
    19.  
    RotateToMovement.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class RotateToMovement : MonoBehaviour {
    5.     public float turnSpeed = 3.0f;
    6.     Vector3 m_lastLoc;
    7.  
    8.     // Use this for initialization
    9.     void Start () {
    10.         m_lastLoc = transform.position;
    11.     }
    12.    
    13.     // Update is called once per frame
    14.     void Update () {
    15.         if(m_lastLoc != transform.position)
    16.         {
    17.             Vector3 dir = transform.position - m_lastLoc;
    18.             dir.Normalize();
    19.             Quaternion lookRot = Quaternion.LookRotation(dir);
    20.             transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRot, turnSpeed * Time.deltaTime);
    21.         }
    22.  
    23.         m_lastLoc = transform.position;
    24.     }
    25. }
    26.  
     
  21. NeillT

    NeillT

    Joined:
    Sep 6, 2016
    Posts:
    2
    Enjoying the tutorials , but I'm receiving 3 issues in the scripts

    HealthUI... ( msgData as HealthData )( and curhealth , bottom line)

    void RecieveMessage(MessageType msgType, GameObject go, MessageData msgData)
    {
    switch (msgType)
    {
    case MessageType.HealthChanged:
    HealthData hpData = msgData as HealthData;
    if (hpData != null)
    {
    UpdateUi(hpData.maxHealth, hpData, curHealth);

    DeathData.cs ( same msgData as DeathData/deathData )

    case MessageType.Died:
    DeathData dthData = msgData as DeathData;

    Health.cs ( no attacker or attacked , but they are there as per the scripts in your tut ) + DeathData bottom line

    DeathData deathData = new DeathData();
    deathData.attacker = go;
    deathData.attacked = gameObject;
    m_MessageHandler.GiveMessage(MessageType.Died, gameObject, DeathData);

    and again in the healthData ( hpData ) + also had to change curHealth to m_curHealth

    HealthData hpData = new HealthData();
    hpData.maxHealth = maxHealth;
    hpData.m_curHealth = m_curHealth;
    m_MessageHandler.GiveMessage(MessageType.HealthChanged, gameObject, hpData);

    Any help appreciated thanks...
     
  22. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Without the exact errors and scripts I can only speculate, but it looks like you havent inherited your death data and health data from message data. Make sure to look at the message handler as thats where the data is defined.

    Also make sure the classes are public so you can access them from anywhere.
     
  23. NeillT

    NeillT

    Joined:
    Sep 6, 2016
    Posts:
    2


    I sent them in the post .. (inbox)
     
  24. ddjjpp331

    ddjjpp331

    Joined:
    Nov 24, 2016
    Posts:
    1
    How would someone do this in 2D?
     
  25. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    The setup is the same but you would need to do vector2 instead if vector3 and swap out collider types where appropriate e.g. sphere to circle
     
  26. NightShadow1001

    NightShadow1001

    Joined:
    Nov 20, 2016
    Posts:
    3
    I am having an issue with C# not recognizing DamageData and MessageHandler(When I hover over quick help it asks if I want to generate a class, meaning its missing )
    void OnCollisionEnter(Collision other)
    {
    if (other.gameObject == m_target)
    {
    DamageData dmgData = new DamageData();
    dmgData.damage = m_damage;
    MessageHandler msgHandler = m_target.GetComponent<MessageHandler>();
    if (msgHandler)
    {
    msgHandler.GiveMessage(MessageType.DAMAGED, m_launcher, dmgData);
    }
    }
    if (other.gameObject.GetComponent<BaseProjectile>() == null)
    Destroy(gameObject);
     
  27. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Not every script is at the top, there are a few lower down with the message handler script.
     
  28. NightShadow1001

    NightShadow1001

    Joined:
    Nov 20, 2016
    Posts:
    3
    Thank you, I will try to find them :) Are they in another script that I missed? I didn't write the normal projectile or beam script, because I only need the Tracking Projectile
     
  29. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Thats fine, not all projectiles are needed for it to work. The messagehandler is a separate script that allows the other scripts go communicate without depending on each other.
     
  30. NightShadow1001

    NightShadow1001

    Joined:
    Nov 20, 2016
    Posts:
    3
    Thanks you, I fixed it and now it works! The only issue I have now is that unity is not recognizing my Turret barrel and is using one of the random parts on it as the barrel and isn't tracking it properly :(
     
  31. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    The spawn points are set on the shooting script so make sure you have set these appropriately. Also make sure all objects are childed correctly to use the turrets rotation.

    For the tracking problem you will need to be more specific. In what way is it failing? Is it turning the wrong way, refusing to track or even just spazzing out when it aims at the target?
     
  32. MHUnity

    MHUnity

    Joined:
    Jan 11, 2015
    Posts:
    21
    Cheers for the useful and dedicated work on this, lordconstant. I'm a mid level programmer but fairly new at getting used to unity assets and objects so it's good to find a set of tutorials that encompass a full build.

    I'm also adding various extra parts that I've learnt elsewhere such as object pooling, Game Manager (you may do both of these already but I've not reached that video yet!) and things like reshaping the turrets to make tham cubes with semi-spheres on top and a barrel sticking out the semi-sphere (looks classy). I'm only on video 3 at the moment but hope to get a complete system up soon :) .

    Thanks again Mr G!
     
  33. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Cheers glad your enjoying the series. Sadly its still not finished so object pooling and things are'nt implemented yet, but I will hopefully be adding a few more videos soon.

    Cheers for watching though :)
     
  34. uzuleeeee

    uzuleeeee

    Joined:
    May 10, 2017
    Posts:
    4
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class ShootingSystem : MonoBehaviour {

    public float fireRate;
    public int damage;
    public float fieldOfView;
    public bool beam;
    public GameObject projectile;
    public GameObject target;
    public List<GameObject> projectileSpawns;

    List<GameObject> m_lastProjectiles = new List<GameObject> ();
    float m_fireTimer = 0.0f;

    // Update is called once per frame
    void Update () {
    if (beam && m_lastProjectiles.Count <= 0) {
    float angle = Quaternion.Angle (transform.rotation, Quaternion.LookRotation (target.transform.position - transform.position));

    if (angle < fieldOfView) {
    SpawnProjectiles ();
    }

    } else if (beam && m_lastProjectiles.Count > 0) {
    float angle = Quaternion.Angle (transform.rotation, Quaternion.LookRotation (target.transform.position - transform.position));

    if (angle > fieldOfView) {
    while (m_lastProjectiles.Count > 0) {
    Destroy (m_lastProjectiles [0]);
    m_lastProjectiles.RemoveAt (0);
    }
    }

    } else {
    m_fireTimer += Time.deltaTime;

    float angle = Quaternion.Angle (transform.rotation, Quaternion.LookRotation (target.transform.position - transform.position));

    if (angle < fieldOfView) {
    SpawnProjectiles ();

    m_fireTimer = 0.0f;
    }
    }
    }

    void SpawnProjectiles () {

    if (!projectile) {
    return;
    }

    m_lastProjectiles.Clear ();

    for (int i = 0; i < projectileSpawns.Count; i++) {
    if (projectileSpawns) {
    GameObject proj = Instantiate (projectile, projectileSpawns.transform.position, Quaternion.Euler (projectileSpawns.transform.forward)) as GameObject;
    proj.GetComponent<BaseProjectile> ().FireProjectile (projectileSpawns , target, damage);

    m_lastProjectiles.Add (proj);
    }
    }
    }
    }
     
  35. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    The issue lies at the bottom of your update function.

    Code (CSharp):
    1. } else {
    2. m_fireTimer += Time.deltaTime;
    3.  
    4. //Your missing this if statement. It checks if enough time has passed to fire again
    5. If(m_fireTimer >= fireRate)
    6. {
    7. float angle = Quaternion.Angle (transform.rotation, Quaternion.LookRotation (target.transform.position - transform.position));
    8.  
    9. if (angle < fieldOfView) {
    10. SpawnProjectiles ();
    11.  
    12. m_fireTimer = 0.0f;
    13. }
    14. }
    15. }
     
  36. uzuleeeee

    uzuleeeee

    Joined:
    May 10, 2017
    Posts:
    4
    Thank u so much!!!
     
  37. MrKory

    MrKory

    Joined:
    Sep 20, 2013
    Posts:
    66
    Hey LordConstant! Amazing job on this tutorial. I am using it in my current project and it is proving to be quite fast, and efficient. My frameRate went way up after changing over to this. I have a huge issue however and cannot seem to figure out a solution.

    My issue is this.. How can I set this system up so that it does not shoot through walls, and other objects. I found adding a rigidBody will prevent it, but my project has a lot of objects, which would end up being an enormous amount of rigidBodies.

    Any help would be more that greatly appreciated!!
    Thanks
     
  38. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Put your walls on their own collision layer and perform a raycast against that layer when you fire, if the targets closer than what you hit or you hit nothing then your in the clear :)
     
    MrKory likes this.
  39. MrKory

    MrKory

    Joined:
    Sep 20, 2013
    Posts:
    66
    Thanks a ton for the quick reply! I tried the following code(posted below) which did the trick for me. I'm not sure if this is a proper way to solve my issue... I've still got a very long way to go with my coding skills.. but it did the trick.

    I attached this code to both the tracking and non tracking projectiles, and added a sphere collider set to trigger with a larger diameter than the standard collision. When it hits anything accept an enemy it is destroyed. Now I need to make the beam weapon work in a similar fashion, or what might be really cool is to have the beam cause a particle effect or a glow on it's entry and exit as it cuts through walls and objects... I have no idea how to do that though :(

    -Kory

    Code (CSharp):
    1.     void OnTriggerEnter(Collider collider){
    2.         if(collider.gameObject.tag != "Enemy"){
    3.             Destroy (gameObject);
    4.         }
    5.     }
     
  40. uzuleeeee

    uzuleeeee

    Joined:
    May 10, 2017
    Posts:
    4
    Sorry but it still doesn't work
     
  41. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    For getting a cutting throhgh the walls sffect on the beam projectile perform a raycast to the target
    Both methods should be acheivable using raycasts. When your firing the beams do ray cast along the barrells line & any points in intersects create your particle effect.

    Ill have a look over the weekend to give abit of a coding example.
     
    MrKory likes this.
  42. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    I cant see any other issues with the code you've posted, is it still the same issue that occurring? Make sure your fire rate is higher than 0 on your turret. If you dont mind zipping up your project and sending it too me I should be able to find your issue. It will be within the shooting script at thats the only thing that effects the fire rate.
     
  43. MrKory

    MrKory

    Joined:
    Sep 20, 2013
    Posts:
    66

    Ooops!! My solution didn't work after all. I apologize if it caused anyone any bit of frustration. I forgot to remove the rigidbodies from my "wall" colliders, which caused me to think that it worked.

    I'll spend some time setting up a raycast as mentioned above.

    -Kory
     
  44. AndiNguyen262

    AndiNguyen262

    Joined:
    Apr 17, 2017
    Posts:
    2
    Hi Chris - I've got everything working except...the tracking isn't happening.
    I'm getting this error relating to the ShootingScript
    NullReferenceException: Object reference not set to an instance of an object ShootingSystem.Update ()

    I added if(!m_target) return; but i don't know how to get data from it. Can you help me to solve this problem ?
     
  45. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    The !m_target call should stop the error in shootingsys.

    In your range checker make sure your GetValidTargets function is clearing out null targets:

    Code (CSharp):
    1. public List<GameObject> GetValidTargets()
    2.     {
    3.         for(int i = 0; i < m_targets.Count; i++)
    4.         {
    5.             if(m_targets[i] == null)
    6.             {
    7.                 m_targets.RemoveAt(i);
    8.                 i--;
    9.             }
    10.         }
    11.         return m_targets;
    12.     }
     
  46. jferris5

    jferris5

    Joined:
    Mar 12, 2017
    Posts:
    1
    Would you be able to give a code snippet of the raycast solution to MrKory's problem? I'm having the same issue where my projectiles are going through everything. I'm pretty new to Raycasts.

    Thanks a ton!
     
  47. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    You can add this to the normal projectile to do effects on a wall as you enter & pass through.
    Instead of the effects you can destroy the gameobject if you prefer.

    The hit layer is the layers you want the ray to intersect with, I threw a few walls onto their own layer to test with.

    Code (CSharp):
    1.  
    2. public GameObject hitEffect;
    3.     public LayerMask hitLayers;
    4.  
    5.     // Update is called once per frame
    6.     void Update () {
    7.         if(m_fired){
    8.             float relSpeed = speed * Time.deltaTime;
    9.             transform.position += m_direction * relSpeed;
    10.  
    11.             RaycastHit hit;
    12.  
    13.             if(Physics.Raycast(transform.position, m_direction, out hit, relSpeed, hitLayers))
    14.             {
    15.                 Quaternion hitRot = transform.rotation;
    16.                 hitRot.SetLookRotation(hit.normal);
    17.                 //Spawn particle gameobject on surface infront
    18.                 Instantiate(hitEffect, hit.point, hitRot);
    19.             }
    20.  
    21.             if(Physics.Raycast(transform.position, -m_direction, out hit, relSpeed, hitLayers))
    22.             {
    23.                 Quaternion hitRot = transform.rotation;
    24.                 hitRot.SetLookRotation(hit.normal);
    25.                 //Spawn particle gameobject on surface behind
    26.                 Instantiate(hitEffect, hit.point, hitRot);
    27.             }
    28.         }
    29.     }
    30.  
    This works for projectiles that move frame by frame, but for the beam projectile its abit trickier. You can do a raycast forward to find any walls, but getting the other side of it is difficult.I'm away for a couple of weeks, but I'll try & work out an efficient way for when I'm back.
     
  48. ditlevrisdahl

    ditlevrisdahl

    Joined:
    May 30, 2017
    Posts:
    26
    Hello LordConstant.
    I love your tutorial! Hope you will make more! :)

    I have been following along with your tutorial and everything seems to be working. I just have a small issue which I can't seem to be able to fix on my own.
    I'm using your tutorial in a TD-like setup where enemies move in and out of the range of the turret. The first enemies which enter the range of the turret are focused and killed, however if more enemes with equal health enters (and its set to kill strongest/weakest), the turret have a hardtime picking an enemy.
    The result is that the turret keep calculating which enemy to target. But the enemies are out of range again before its done. (The same happens for nearest/furthest, where it finds an enemy which is near/far, but another enemy is then nearer/further before it have even started attacking).
    I have tried increasing the tracking system speed so it turns instantly. But this just makes the turret look at the enemy, follow it along its path(jumping between whichever target is nearest/strongest/... instantly), but not shoot. I'm not sure if its a timer somewhere which determine when the turret is ready to attack once a target is locked. Any help is hightly appreciated :)

    EDIT: Seems to work fine with tracking projectiles. Just a problem with the beamprojectile.
     
    Last edited: May 30, 2017
  49. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Sorry for late response, been away this week.
    Glad your enjoying the videos :)

    If you save the current target once its chosen and dont pick a new one until its dead or out of range, that should solve the issue.

    For the beam projectile not firing at fast turning thats rathet odd. Ill test it out and see if I can find the cause.
     
    ditlevrisdahl likes this.
  50. TheRussianBrit

    TheRussianBrit

    Joined:
    Jun 18, 2017
    Posts:
    3
    @lordconstant I get the error in my turret ai script

    Assets/scripts/friendly behaviour/friendlyTurretAI.cs(4,34): error CS0305: Using the generic type `System.Collections.Generic.List<T>' requires `1' type argument(s)

    here's the line of script where I think my problem is

    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);
    }
    }

    still a great tutorial though