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