chenxin
2020-11-28 ee2cbc86ed58ae03a092b67690d3869ebe78fd20
Assets/Scripts/TowerDefense/Targetting/Targetter.cs
@@ -10,470 +10,470 @@
namespace TowerDefense.Targetting
{
   /// <summary>
   /// 选择目标Agent的方式
   /// </summary>
   public enum EEnemySelFunc
   {
      NULL,
      Nearest,   // 离终点最近的.
      Random,    // 随机选择.
      MaxHp,     // 血量最高的.
      END
   }
   /// <summary>
   /// Class used to track targets for an affector
   /// 这个类比较古怪的地方是他本身并没有显示出来Collider,但却有Collider Component.是在
   /// void AttachCollider() 这个函数中(TargetterEditor.cs),动态的添加了不同类型的Collider Component,
   /// 然后隐藏了这个Collider Component,但是却可以在游戏内当作Collider Component来使用。
   /// 在TargetterEditor内,设置isTrigger为true
   ///
   /// event 的作用在于声明的对应公有 Action or delegate变量,不能在类的外面执行,只在类的外面 += -= 操作
   ///
   /// </summary>
   public class Targetter : MonoBehaviour
   {
      public static bool bSearchTarget = true;
      /// <summary>
      /// Fires when a targetable enters the target collider
      /// </summary>
      public event Action<Targetable> targetEntersRange;
    /// <summary>
    /// 选择目标Agent的方式
    /// </summary>
    public enum EEnemySelFunc
    {
        NULL,
        Nearest,   // 离终点最近的.
        Random,    // 随机选择.
        MaxHp,     // 血量最高的.
        END
    }
    /// <summary>
    /// Class used to track targets for an affector
    /// 这个类比较古怪的地方是他本身并没有显示出来Collider,但却有Collider Component.是在
    /// void AttachCollider() 这个函数中(TargetterEditor.cs),动态的添加了不同类型的Collider Component,
    /// 然后隐藏了这个Collider Component,但是却可以在游戏内当作Collider Component来使用。
    /// 在TargetterEditor内,设置isTrigger为true
    ///
    /// event 的作用在于声明的对应公有 Action or delegate变量,不能在类的外面执行,只在类的外面 += -= 操作
    ///
    /// </summary>
    public class Targetter : MonoBehaviour
    {
        public static bool bSearchTarget = true;
        /// <summary>
        /// Fires when a targetable enters the target collider
        /// </summary>
        public event Action<Targetable> targetEntersRange;
      /// <summary>
      /// Fires when a targetable exits the target collider
      /// </summary>
      public event Action<Targetable> targetExitsRange;
        /// <summary>
        /// Fires when a targetable exits the target collider
        /// </summary>
        public event Action<Targetable> targetExitsRange;
      /// <summary>
      /// Fires when an appropriate target is found
      /// </summary>
      public event Action<Targetable> acquiredTarget;
        /// <summary>
        /// Fires when an appropriate target is found
        /// </summary>
        public event Action<Targetable> acquiredTarget;
      /// <summary>
      /// Fires when the current target was lost
      /// </summary>
      public event Action lostTarget;
        /// <summary>
        /// Fires when the current target was lost
        /// </summary>
        public event Action lostTarget;
      /// <summary>
      /// The transform to point at the target
      /// </summary>
      public Transform turret;
        /// <summary>
        /// The transform to point at the target
        /// </summary>
        public Transform turret;
      /// <summary>
      /// 搜索敌人的方式:默认是最近,加入随机和血量最多,后期再加其它的模式
      /// </summary>
      public EEnemySelFunc searchEnemyFunc = EEnemySelFunc.Nearest;
        /// <summary>
        /// 搜索敌人的方式:默认是最近,加入随机和血量最多,后期再加其它的模式
        /// </summary>
        public EEnemySelFunc searchEnemyFunc = EEnemySelFunc.Nearest;
      /// <summary>
      /// The range of the turret's x rotation
      /// </summary>
      public Vector2 turretXRotationRange = new Vector2(0, 359);
        /// <summary>
        /// The range of the turret's x rotation
        /// </summary>
        public Vector2 turretXRotationRange = new Vector2(0, 359);
      /// <summary>
      /// If m_Turret rotates freely or only on y;
      /// </summary>
      public bool onlyYTurretRotation;
        /// <summary>
        /// If m_Turret rotates freely or only on y;
        /// </summary>
        public bool onlyYTurretRotation;
      /// <summary>
      /// The search rate in searches per second
      /// </summary>
      public float searchRate;
        /// <summary>
        /// The search rate in searches per second
        /// </summary>
        public float searchRate;
      /// <summary>
      /// Y rotation speed while the turret is idle in degrees per second
      /// </summary>
      public float idleRotationSpeed = 39f;
        /// <summary>
        /// Y rotation speed while the turret is idle in degrees per second
        /// </summary>
        public float idleRotationSpeed = 39f;
      /// <summary>
      /// The time it takes for the tower to correct its x rotation on idle in seconds
      /// </summary>
      public float idleCorrectionTime = 2.0f;
        /// <summary>
        /// The time it takes for the tower to correct its x rotation on idle in seconds
        /// </summary>
        public float idleCorrectionTime = 2.0f;
      /// <summary>
      /// The collider attached to the targetter
      /// </summary>
      public Collider attachedCollider;
        /// <summary>
        /// The collider attached to the targetter
        /// </summary>
        public Collider attachedCollider;
      /// <summary>
      /// How long the turret waits in its idle form before spinning in seconds
      /// </summary>
      public float idleWaitTime = 2.0f;
        /// <summary>
        /// How long the turret waits in its idle form before spinning in seconds
        /// </summary>
        public float idleWaitTime = 2.0f;
      /// <summary>
      /// The current targetables in the collider
      /// </summary>
      protected List<Targetable> m_TargetsInRange = new List<Targetable>();
        /// <summary>
        /// The current targetables in the collider
        /// </summary>
        protected List<Targetable> m_TargetsInRange = new List<Targetable>();
      /// <summary>
      /// The seconds until a search is allowed
      /// </summary>
      protected float m_SearchTimer = 0.0f;
        /// <summary>
        /// The seconds until a search is allowed
        /// </summary>
        protected float m_SearchTimer = 0.0f;
      /// <summary>
      /// The seconds until the tower starts spinning
      /// </summary>
      protected float m_WaitTimer = 0.0f;
        /// <summary>
        /// The seconds until the tower starts spinning
        /// </summary>
        protected float m_WaitTimer = 0.0f;
      /// <summary>
      /// The current targetable
      /// </summary>
      protected Targetable m_CurrrentTargetable;
        /// <summary>
        /// The current targetable
        /// </summary>
        protected Targetable m_CurrrentTargetable;
      /// <summary>
      /// Counter used for x rotation correction
      /// </summary>
      protected float m_XRotationCorrectionTime;
        /// <summary>
        /// Counter used for x rotation correction
        /// </summary>
        protected float m_XRotationCorrectionTime;
      /// <summary>
      /// If there was a targetable in the last frame
      /// </summary>
      protected bool m_HadTarget;
        /// <summary>
        /// If there was a targetable in the last frame
        /// </summary>
        protected bool m_HadTarget;
      /// <summary>
      /// How fast this turret is spinning
      /// </summary>
      protected float m_CurrentRotationSpeed;
        /// <summary>
        /// How fast this turret is spinning
        /// </summary>
        protected float m_CurrentRotationSpeed;
      /// <summary>
      /// returns the radius of the collider whether
      /// its a sphere or capsule
      /// </summary>
      public float effectRadius
      {
         get
         {
            var sphere = attachedCollider as SphereCollider;
            if (sphere != null)
            {
               return sphere.radius;
            }
            var capsule = attachedCollider as CapsuleCollider;
            if (capsule != null)
            {
               return capsule.radius;
            }
            return 0;
         }
      }
      /// <summary>
      /// The alignment of the affector
      /// </summary>
      public IAlignmentProvider alignment;
      /// <summary>
      /// Returns the current target
      /// </summary>
      public Targetable GetTarget( int waveline,bool noPoison = false )
      {
         switch( this.searchEnemyFunc)
        /// <summary>
        /// returns the radius of the collider whether
        /// its a sphere or capsule
        /// </summary>
        public float effectRadius
        {
            get
            {
            case EEnemySelFunc.Nearest:
            case EEnemySelFunc.MaxHp:
            case EEnemySelFunc.Random:
               {
                  if (bOpponent)
                var sphere = attachedCollider as SphereCollider;
                if (sphere != null)
                {
                    return sphere.radius;
                }
                var capsule = attachedCollider as CapsuleCollider;
                if (capsule != null)
                {
                    return capsule.radius;
                }
                return 0;
            }
        }
        /// <summary>
        /// The alignment of the affector
        /// </summary>
        public IAlignmentProvider alignment;
        /// <summary>
        /// Returns the current target
        /// </summary>
        public Targetable GetTarget(int waveline, bool noPoison = false)
        {
            switch (this.searchEnemyFunc)
            {
                case EEnemySelFunc.Nearest:
                case EEnemySelFunc.MaxHp:
                case EEnemySelFunc.Random:
                    {
                        if (bOpponent)
                        {
                     //return AgentInsManager.instance.oppoMinDisAgent;
                     if (waveline >= 0)
                        return AgentInsManager.instance.GetMinDisAgent(waveline, true,false,noPoison);
                     else
                     {
                        Debug.Log("当前Opponent传入的参数waveLine is:" + waveline);
                        return AgentInsManager.instance.MinDisAgent;
                     }
                  }
                        else
                        {
                     //return AgentInsManager.instance.MinDisAgent;
                     if(waveline>= 0)
                        return AgentInsManager.instance.GetMinDisAgent(waveline,false,false, noPoison );
                            //return AgentInsManager.instance.oppoMinDisAgent;
                            if (waveline >= 0)
                                return AgentInsManager.instance.GetMinDisAgent(waveline, true, false, noPoison);
                            else
                            {
                        Debug.Log("当前传入的参数waveLine is:" + waveline);
                        return AgentInsManager.instance.MinDisAgent;
                     }
                  }
               }
/*            case EEnemySelFunc.MaxHp:
                    {
                  return AgentInsManager.instance.getMaxHpAgent(bOpponent);
               }
            case EEnemySelFunc.Random:
               {
                  return AgentInsManager.instance.getRandomAgent(bOpponent);
               }*/
                                Debug.Log("当前Opponent传入的参数waveLine is:" + waveline);
                                return AgentInsManager.instance.MinDisAgent;
                            }
                        }
                        else
                        {
                            //return AgentInsManager.instance.MinDisAgent;
                            if (waveline >= 0)
                                return AgentInsManager.instance.GetMinDisAgent(waveline, false, false, noPoison);
                            else
                            {
                                Debug.Log("当前传入的参数waveLine is:" + waveline);
                                return AgentInsManager.instance.MinDisAgent;
                            }
                        }
                    }
                    /*            case EEnemySelFunc.MaxHp:
                                        {
                                            return AgentInsManager.instance.getMaxHpAgent(bOpponent);
                                        }
                                    case EEnemySelFunc.Random:
                                        {
                                            return AgentInsManager.instance.getRandomAgent(bOpponent);
                                        }*/
            }
         return null;
      }
            return null;
        }
      /// <summary>
      /// 是否是对手战斗方.
      /// </summary>
      public bool bOpponent { set; get; }
        /// <summary>
        /// 是否是对手战斗方.
        /// </summary>
        public bool bOpponent { set; get; }
      /// <summary>
      /// Clears the list of current targets and clears all events
      /// </summary>
      public void ResetTargetter()
      {
         m_TargetsInRange.Clear();
         m_CurrrentTargetable = null;
        /// <summary>
        /// Clears the list of current targets and clears all events
        /// </summary>
        public void ResetTargetter()
        {
            m_TargetsInRange.Clear();
            m_CurrrentTargetable = null;
         targetEntersRange = null;
         targetExitsRange = null;
         acquiredTarget = null;
         lostTarget = null;
            targetEntersRange = null;
            targetExitsRange = null;
            acquiredTarget = null;
            lostTarget = null;
         // Reset turret facing
         if (turret != null)
         {
            turret.localRotation = Quaternion.identity;
         }
      }
      /// <summary>
      /// Returns all the targets within the collider. This list must not be changed as it is the working
      /// list of the targetter. Changing it could break the targetter
      /// </summary>
      public List<Targetable> GetAllTargets()
      {
         switch( searchEnemyFunc)
            // Reset turret facing
            if (turret != null)
            {
            case EEnemySelFunc.MaxHp:
                turret.localRotation = Quaternion.identity;
            }
        }
        /// <summary>
        /// Returns all the targets within the collider. This list must not be changed as it is the working
        /// list of the targetter. Changing it could break the targetter
        /// </summary>
        public List<Targetable> GetAllTargets()
        {
            switch (searchEnemyFunc)
            {
                case EEnemySelFunc.MaxHp:
                    {
                  return AgentInsManager.instance.getAgentListMaxHp(bOpponent);
                        return AgentInsManager.instance.getAgentListMaxHp(bOpponent);
                    }
            case EEnemySelFunc.Random:
                case EEnemySelFunc.Random:
                    {
                  return AgentInsManager.instance.getAgentListRandom(bOpponent);
                        return AgentInsManager.instance.getAgentListRandom(bOpponent);
                    }
            }
         // 最后返回的就是距离计算的模式:
         if (this.bOpponent)
            return AgentInsManager.instance.OppoAgentInRange;
         else
            return AgentInsManager.instance.AgentInRange;
      }
            // 最后返回的就是距离计算的模式:
            if (this.bOpponent)
                return AgentInsManager.instance.OppoAgentInRange;
            else
                return AgentInsManager.instance.AgentInRange;
        }
      /// <summary>
      /// Checks if the targetable is a valid target
      /// </summary>
      /// <param name="targetable"></param>
      /// <returns>true if targetable is vaild, false if not</returns>
      protected virtual bool IsTargetableValid(Targetable targetable)
      {
         if (targetable == null)
         {
            return false;
         }
         IAlignmentProvider targetAlignment = targetable.configuration.alignmentProvider;
         bool canDamage = alignment == null || targetAlignment == null ||
                          alignment.CanHarm(targetAlignment);
         return canDamage;
      }
        /// <summary>
        /// Checks if the targetable is a valid target
        /// </summary>
        /// <param name="targetable"></param>
        /// <returns>true if targetable is vaild, false if not</returns>
        protected virtual bool IsTargetableValid(Targetable targetable)
        {
            if (targetable == null)
            {
                return false;
            }
      /// <summary>
      /// On exiting the trigger, a valid targetable is removed from the tracking list.
      /// </summary>
      /// <param name="other">The other collider in the collision</param>
      protected virtual void OnTriggerExit(Collider other)
      {
         var targetable = other.GetComponent<Targetable>();
         if (!IsTargetableValid(targetable))
         {
            return;
         }
         m_TargetsInRange.Remove(targetable);
         if (targetExitsRange != null)
         {
            targetExitsRange(targetable);
         }
         if (targetable == m_CurrrentTargetable)
         {
            OnTargetRemoved(targetable);
         }
         else
         {
            // Only need to remove if we're not our actual target, otherwise OnTargetRemoved will do the work above
            targetable.removed -= OnTargetRemoved;
         }
      }
      /// <summary>
      /// On entering the trigger, a valid targetable is added to the tracking list.
      /// </summary>
      /// <param name="other">The other collider in the collision</param>
      protected virtual void OnTriggerEnter(Collider other)
      {
         GameObject tobj = this.transform.parent.gameObject;
         Collider tcol = GetComponent<Collider>();
            IAlignmentProvider targetAlignment = targetable.configuration.alignmentProvider;
            bool canDamage = alignment == null || targetAlignment == null ||
                             alignment.CanHarm(targetAlignment);
         var targetable = other.GetComponent<Targetable>();
         if (!IsTargetableValid(targetable))
         {
            return;
         }
         targetable.removed += OnTargetRemoved;
         m_TargetsInRange.Add(targetable);
         if (targetEntersRange != null)
         {
            targetEntersRange(targetable);
         }
      }
            return canDamage;
        }
      /// <summary>
      /// Returns the nearest targetable within the currently tracked targetables
      /// </summary>
      /// <returns>The nearest targetable if there is one, null otherwise</returns>
      protected virtual Targetable GetNearestTargetable()
      {
        /// <summary>
        /// On exiting the trigger, a valid targetable is removed from the tracking list.
        /// </summary>
        /// <param name="other">The other collider in the collision</param>
        protected virtual void OnTriggerExit(Collider other)
        {
            var targetable = other.GetComponent<Targetable>();
            if (!IsTargetableValid(targetable))
            {
                return;
            }
            m_TargetsInRange.Remove(targetable);
            if (targetExitsRange != null)
            {
                targetExitsRange(targetable);
            }
            if (targetable == m_CurrrentTargetable)
            {
                OnTargetRemoved(targetable);
            }
            else
            {
                // Only need to remove if we're not our actual target, otherwise OnTargetRemoved will do the work above
                targetable.removed -= OnTargetRemoved;
            }
        }
        /// <summary>
        /// On entering the trigger, a valid targetable is added to the tracking list.
        /// </summary>
        /// <param name="other">The other collider in the collision</param>
        protected virtual void OnTriggerEnter(Collider other)
        {
            GameObject tobj = this.transform.parent.gameObject;
            Collider tcol = GetComponent<Collider>();
            var targetable = other.GetComponent<Targetable>();
            if (!IsTargetableValid(targetable))
            {
                return;
            }
            targetable.removed += OnTargetRemoved;
            m_TargetsInRange.Add(targetable);
            if (targetEntersRange != null)
            {
                targetEntersRange(targetable);
            }
        }
        /// <summary>
        /// Returns the nearest targetable within the currently tracked targetables
        /// </summary>
        /// <returns>The nearest targetable if there is one, null otherwise</returns>
        protected virtual Targetable GetNearestTargetable()
        {
            // 使用统一管理的管理器接口.
            if (this.bOpponent)
                return AgentInsManager.instance.oppoMinDisAgent;
         else
            return AgentInsManager.instance.MinDisAgent;
      }
            else
                return AgentInsManager.instance.MinDisAgent;
        }
      /// <summary>
      /// Starts the search timer
      /// </summary>
      protected virtual void Start()
      {
         m_SearchTimer = searchRate;
         m_WaitTimer = idleWaitTime;
        /// <summary>
        /// Starts the search timer
        /// </summary>
        protected virtual void Start()
        {
            m_SearchTimer = searchRate;
            m_WaitTimer = idleWaitTime;
      }
        }
      /// <summary>
      /// Checks if any targets are destroyed and aquires a new targetable if appropriate
      /// </summary>
      protected virtual void Update()
      {
         m_SearchTimer -= Time.deltaTime;
        /// <summary>
        /// Checks if any targets are destroyed and aquires a new targetable if appropriate
        /// </summary>
        protected virtual void Update()
        {
            m_SearchTimer -= Time.deltaTime;
         if (m_SearchTimer <= 0.0f && m_CurrrentTargetable == null && m_TargetsInRange.Count > 0)
         {
            m_CurrrentTargetable = GetNearestTargetable();
            if (m_CurrrentTargetable != null)
            {
               if (acquiredTarget != null)
               {
                  acquiredTarget(m_CurrrentTargetable);
               }
               m_SearchTimer = searchRate;
            }
         }
            if (m_SearchTimer <= 0.0f && m_CurrrentTargetable == null && m_TargetsInRange.Count > 0)
            {
                m_CurrrentTargetable = GetNearestTargetable();
                if (m_CurrrentTargetable != null)
                {
                    if (acquiredTarget != null)
                    {
                        acquiredTarget(m_CurrrentTargetable);
                    }
                    m_SearchTimer = searchRate;
                }
            }
         AimTurret();
            AimTurret();
         m_HadTarget = m_CurrrentTargetable != null;
      }
            m_HadTarget = m_CurrrentTargetable != null;
        }
      /// <summary>
      /// Fired by the agents died event or when the current target moves out of range,
      /// Fires the lostTarget event.
      /// </summary>
      void OnTargetRemoved(DamageableBehaviour target)
      {
         target.removed -= OnTargetRemoved;
         if (m_CurrrentTargetable != null && target.configuration == m_CurrrentTargetable.configuration)
         {
            if (lostTarget != null)
            {
               lostTarget();
            }
            m_HadTarget = false;
            m_TargetsInRange.Remove(m_CurrrentTargetable);
            m_CurrrentTargetable = null;
            m_XRotationCorrectionTime = 0.0f;
         }
         else //wasnt the current target, find and remove from targets list
         {
            for (int i = 0; i < m_TargetsInRange.Count; i++)
            {
               if (m_TargetsInRange[i].configuration == target.configuration)
               {
                  m_TargetsInRange.RemoveAt(i);
                  break;
               }
            }
         }
      }
        /// <summary>
        /// Fired by the agents died event or when the current target moves out of range,
        /// Fires the lostTarget event.
        /// </summary>
        void OnTargetRemoved(DamageableBehaviour target)
        {
            target.removed -= OnTargetRemoved;
            if (m_CurrrentTargetable != null && target.configuration == m_CurrrentTargetable.configuration)
            {
                if (lostTarget != null)
                {
                    lostTarget();
                }
                m_HadTarget = false;
                m_TargetsInRange.Remove(m_CurrrentTargetable);
                m_CurrrentTargetable = null;
                m_XRotationCorrectionTime = 0.0f;
            }
            else //wasnt the current target, find and remove from targets list
            {
                for (int i = 0; i < m_TargetsInRange.Count; i++)
                {
                    if (m_TargetsInRange[i].configuration == target.configuration)
                    {
                        m_TargetsInRange.RemoveAt(i);
                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Aims the turret at the current target
        /// 将炮塔对准当前目标
        /// </summary>
        protected virtual void AimTurret()
      {
         if (turret == null)
         {
            return;
         }
        {
            if (turret == null)
            {
                return;
            }
         if (m_CurrrentTargetable == null) // do idle rotation
         {
            if (m_WaitTimer > 0)
            {
               m_WaitTimer -= Time.deltaTime;
               if (m_WaitTimer <= 0)
               {
                  m_CurrentRotationSpeed = (Random.value * 2 - 1) * idleRotationSpeed;
               }
            }
            else
            {
               Vector3 euler = turret.rotation.eulerAngles;
               euler.x = Mathf.Lerp(Wrap180(euler.x), 0, m_XRotationCorrectionTime);
               m_XRotationCorrectionTime = Mathf.Clamp01((m_XRotationCorrectionTime + Time.deltaTime) / idleCorrectionTime);
               euler.y += m_CurrentRotationSpeed * Time.deltaTime;
            if (m_CurrrentTargetable == null) // do idle rotation
            {
                if (m_WaitTimer > 0)
                {
                    m_WaitTimer -= Time.deltaTime;
                    if (m_WaitTimer <= 0)
                    {
                        m_CurrentRotationSpeed = (Random.value * 2 - 1) * idleRotationSpeed;
                    }
                }
                else
                {
                    Vector3 euler = turret.rotation.eulerAngles;
                    euler.x = Mathf.Lerp(Wrap180(euler.x), 0, m_XRotationCorrectionTime);
                    m_XRotationCorrectionTime = Mathf.Clamp01((m_XRotationCorrectionTime + Time.deltaTime) / idleCorrectionTime);
                    euler.y += m_CurrentRotationSpeed * Time.deltaTime;
               turret.eulerAngles = euler;
            }
         }
         else
         {
            m_WaitTimer = idleWaitTime;
                    turret.eulerAngles = euler;
                }
            }
            else
            {
                m_WaitTimer = idleWaitTime;
            Vector3 targetPosition = m_CurrrentTargetable.position;
            if (onlyYTurretRotation)
            {
               targetPosition.y = turret.position.y;
            }
            Vector3 direction = targetPosition - turret.position;
            Quaternion look = Quaternion.LookRotation(direction, Vector3.up);
            Vector3 lookEuler = look.eulerAngles;
            // We need to convert the rotation to a -180/180 wrap so that we can clamp the angle with a min/max
            float x = Wrap180(lookEuler.x);
            lookEuler.x = Mathf.Clamp(x, turretXRotationRange.x, turretXRotationRange.y);
            look.eulerAngles = lookEuler;
            turret.rotation = look;
         }
      }
                Vector3 targetPosition = m_CurrrentTargetable.position;
                if (onlyYTurretRotation)
                {
                    targetPosition.y = turret.position.y;
                }
                Vector3 direction = targetPosition - turret.position;
                Quaternion look = Quaternion.LookRotation(direction, Vector3.up);
                Vector3 lookEuler = look.eulerAngles;
                // We need to convert the rotation to a -180/180 wrap so that we can clamp the angle with a min/max
                float x = Wrap180(lookEuler.x);
                lookEuler.x = Mathf.Clamp(x, turretXRotationRange.x, turretXRotationRange.y);
                look.eulerAngles = lookEuler;
                turret.rotation = look;
            }
        }
      /// <summary>
      /// A simply function to convert an angle to a -180/180 wrap
      /// </summary>
      static float Wrap180(float angle)
      {
         angle %= 360;
         if (angle < -180)
         {
            angle += 360;
         }
         else if (angle > 180)
         {
            angle -= 360;
         }
         return angle;
      }
   }
        /// <summary>
        /// A simply function to convert an angle to a -180/180 wrap
        /// </summary>
        static float Wrap180(float angle)
        {
            angle %= 360;
            if (angle < -180)
            {
                angle += 360;
            }
            else if (angle > 180)
            {
                angle -= 360;
            }
            return angle;
        }
    }
}