using System;
using System.Collections.Generic;
using System.Net;
using ActionGameFramework.Health;
using Core.Health;
using UnityEngine;
using UnityEngine.UIElements;
using Random = UnityEngine.Random;
namespace TowerDefense.Targetting
{
///
/// 选择目标Agent的方式
///
public enum EEnemySelFunc
{
NULL,
Nearest, // 离终点最近的.
Random, // 随机选择.
MaxHp, // 血量最高的.
END
}
///
/// 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变量,不能在类的外面执行,只在类的外面 += -= 操作
///
///
public class Targetter : MonoBehaviour
{
public static bool bSearchTarget = true;
///
/// Fires when a targetable enters the target collider
///
public event Action targetEntersRange;
///
/// Fires when a targetable exits the target collider
///
public event Action targetExitsRange;
///
/// Fires when an appropriate target is found
///
public event Action acquiredTarget;
///
/// Fires when the current target was lost
///
public event Action lostTarget;
///
/// The transform to point at the target
///
public Transform turret;
///
/// 搜索敌人的方式:默认是最近,加入随机和血量最多,后期再加其它的模式
///
public EEnemySelFunc searchEnemyFunc = EEnemySelFunc.Nearest;
///
/// The range of the turret's x rotation
///
public Vector2 turretXRotationRange = new Vector2(0, 359);
///
/// If m_Turret rotates freely or only on y;
///
public bool onlyYTurretRotation;
///
/// The search rate in searches per second
///
public float searchRate;
///
/// Y rotation speed while the turret is idle in degrees per second
///
public float idleRotationSpeed = 39f;
///
/// The time it takes for the tower to correct its x rotation on idle in seconds
///
public float idleCorrectionTime = 2.0f;
///
/// The collider attached to the targetter
///
public Collider attachedCollider;
///
/// How long the turret waits in its idle form before spinning in seconds
///
public float idleWaitTime = 2.0f;
///
/// The current targetables in the collider
///
protected List m_TargetsInRange = new List();
///
/// The seconds until a search is allowed
///
protected float m_SearchTimer = 0.0f;
///
/// The seconds until the tower starts spinning
///
protected float m_WaitTimer = 0.0f;
///
/// The current targetable
///
protected Targetable m_CurrrentTargetable;
///
/// Counter used for x rotation correction
///
protected float m_XRotationCorrectionTime;
///
/// If there was a targetable in the last frame
///
protected bool m_HadTarget;
///
/// How fast this turret is spinning
///
protected float m_CurrentRotationSpeed;
///
/// returns the radius of the collider whether
/// its a sphere or capsule
///
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;
}
}
///
/// The alignment of the affector
///
public IAlignmentProvider alignment;
///
/// Returns the current target
///
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);
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;
}
///
/// 是否是对手战斗方.
///
public bool bOpponent { set; get; }
///
/// Clears the list of current targets and clears all events
///
public void ResetTargetter()
{
m_TargetsInRange.Clear();
m_CurrrentTargetable = null;
targetEntersRange = null;
targetExitsRange = null;
acquiredTarget = null;
lostTarget = null;
// Reset turret facing
if (turret != null)
{
turret.localRotation = Quaternion.identity;
}
}
///
/// 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
///
public List GetAllTargets()
{
switch (searchEnemyFunc)
{
case EEnemySelFunc.MaxHp:
{
return AgentInsManager.instance.getAgentListMaxHp(bOpponent);
}
case EEnemySelFunc.Random:
{
return AgentInsManager.instance.getAgentListRandom(bOpponent);
}
}
// 最后返回的就是距离计算的模式:
if (this.bOpponent)
return AgentInsManager.instance.OppoAgentInRange;
else
return AgentInsManager.instance.AgentInRange;
}
///
/// Checks if the targetable is a valid target
///
///
/// true if targetable is vaild, false if not
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;
}
///
/// On exiting the trigger, a valid targetable is removed from the tracking list.
///
/// The other collider in the collision
protected virtual void OnTriggerExit(Collider other)
{
var targetable = other.GetComponent();
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;
}
}
///
/// On entering the trigger, a valid targetable is added to the tracking list.
///
/// The other collider in the collision
protected virtual void OnTriggerEnter(Collider other)
{
GameObject tobj = this.transform.parent.gameObject;
Collider tcol = GetComponent();
var targetable = other.GetComponent();
if (!IsTargetableValid(targetable))
{
return;
}
targetable.removed += OnTargetRemoved;
m_TargetsInRange.Add(targetable);
if (targetEntersRange != null)
{
targetEntersRange(targetable);
}
}
///
/// Returns the nearest targetable within the currently tracked targetables
///
/// The nearest targetable if there is one, null otherwise
protected virtual Targetable GetNearestTargetable()
{
// 使用统一管理的管理器接口.
if (this.bOpponent)
return AgentInsManager.instance.oppoMinDisAgent;
else
return AgentInsManager.instance.MinDisAgent;
}
///
/// Starts the search timer
///
protected virtual void Start()
{
m_SearchTimer = searchRate;
m_WaitTimer = idleWaitTime;
}
///
/// Checks if any targets are destroyed and aquires a new targetable if appropriate
///
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;
}
}
AimTurret();
m_HadTarget = m_CurrrentTargetable != null;
}
///
/// Fired by the agents died event or when the current target moves out of range,
/// Fires the lostTarget event.
///
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;
}
}
}
}
///
/// Aims the turret at the current target
/// 将炮塔对准当前目标
///
protected virtual void AimTurret()
{
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;
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;
}
}
///
/// A simply function to convert an angle to a -180/180 wrap
///
static float Wrap180(float angle)
{
angle %= 360;
if (angle < -180)
{
angle += 360;
}
else if (angle > 180)
{
angle -= 360;
}
return angle;
}
}
}