using System;
|
using ActionGameFramework.Health;
|
using Core.Health;
|
using Core.Utilities;
|
using DG.Tweening;
|
using KTGMGemClient;
|
using TowerDefense.Affectors;
|
using TowerDefense.Economy;
|
using TowerDefense.Level;
|
using TowerDefense.Nodes;
|
using TowerDefense.UI.HUD;
|
using UnityEngine;
|
using UnityEngine.AI;
|
|
namespace TowerDefense.Agents
|
{
|
// 预计算移动的方位.
|
public enum EAgentMovDir
|
{
|
Start,
|
XPositive,
|
XNegative,
|
ZPositive,
|
ZNegative,
|
End
|
}
|
|
|
/// <summary>
|
/// 每一个Agent发布到场景内时设置的数据
|
/// </summary>
|
public struct AgentSetData
|
{
|
public float speed;
|
public float hp;
|
}
|
|
/// <summary>
|
/// An agent will follow a path of nodes
|
/// NavMeshAgent和AttackAffector是必须得加入的EntityNode.
|
/// </summary>
|
[RequireComponent(typeof(NavMeshAgent))]
|
public abstract class Agent : Targetable
|
{
|
/// <summary>
|
/// A means of keeping track of the agent along its path
|
/// </summary>
|
public enum State
|
{
|
/// <summary>
|
/// When the agent is on a path that is not blocked
|
/// </summary>
|
OnCompletePath,
|
|
/// <summary>
|
/// When the agent is on a path is blocked
|
/// </summary>
|
OnPartialPath,
|
|
/// <summary>
|
/// When the agent has reached the end of a blocked path
|
/// </summary>
|
Attacking,
|
|
/// <summary>
|
/// For flying agents, when they move over obstacles
|
/// </summary>
|
PushingThrough,
|
|
/// <summary>
|
/// When the agent has completed their path
|
/// </summary>
|
PathComplete
|
}
|
|
/// <summary>
|
/// Event fired when agent reached its final node
|
/// </summary>
|
public event Action<Node> destinationReached;
|
|
/// <summary>
|
/// Position offset for an applied affect
|
/// 这个主要是用于AgentEffect相关的类,给当前的Agent加相关的效果,移动变慢,加速等等。
|
/// </summary>
|
public Vector3 appliedEffectOffset = Vector3.zero;
|
|
/// <summary>
|
/// Scale adjustment for an applied affect
|
/// 加强效果对应的Scale数据
|
/// </summary>
|
public float appliedEffectScale = 1;
|
|
/// <summary>
|
/// 当前的Agent对应的healthBar.
|
/// </summary>
|
public HealthVisualizer healthBar;
|
|
/// <summary>
|
/// 如果不为空,则播放当前的particle.
|
/// </summary>
|
public ParticleSystem spawnParticle;
|
|
public AgentSetData mAgentData;
|
|
/// <summary>
|
/// The NavMeshAgent component attached to this
|
/// 这个,Unity内比较核心的类
|
/// </summary>
|
protected NavMeshAgent m_NavMeshAgent;
|
|
/// <summary>
|
/// The Current node that the agent must navigate to
|
/// </summary>
|
protected Node m_CurrentNode;
|
|
/// <summary>
|
/// 修改当前的NavMeshAgent移动模式,修改为纯手动移动
|
/// </summary>
|
protected Node m_NextNode;
|
|
/// <summary>
|
/// 记录初始化的位置和Node,用于重设当前Agent的位置
|
/// </summary>
|
protected Node initNode;
|
protected Vector3 initPos;
|
|
/// <summary>
|
/// Reference to the level manager
|
/// </summary>
|
protected LevelManager m_LevelManager;
|
|
protected EndlessLevelManager endlessLevelManager;
|
|
/// <summary>
|
/// Stores the Destination to the next node so we don't need to get new random positions every time
|
/// </summary>
|
protected Node m_Destination;
|
/// <summary>
|
/// 每次移动从SrcPosition到m_Destination.
|
/// </summary>
|
protected Vector3 m_SrcPosition;
|
/// <summary>
|
/// 每次切换Node,都会重新计算移动方向,用于中间状态时,更快速的计算移动数据。
|
/// </summary>
|
protected EAgentMovDir moveDir;
|
|
/// <summary>
|
/// 移动速度,暂时从navMeshAgent中获取.
|
/// </summary>
|
protected float fMoveSpeed;
|
protected float MoveStopTime = 0.0f;
|
protected ParticleSystem MoveStopEffect = null;
|
|
/// <summary>
|
/// 距离目标的最终距离.
|
/// </summary>
|
protected float m_DisToDestination;
|
|
/// <summary>
|
/// 当前Agent的默认Scale数据
|
/// </summary>
|
protected Vector3 mDefaultScale;
|
|
/// <summary>
|
/// 当前小怪对应的掉落数据
|
/// </summary>
|
protected LootDrop mLootDrop;
|
|
/// <summary>
|
/// 每一个Node加一个超长距离值
|
/// </summary>
|
protected readonly float NODE_DIS = 1000f;
|
|
/// <summary>
|
/// 当前Agent的开始Yval,用于标注层级显示.
|
/// </summary>
|
protected float mStartYVal;
|
protected float mStartZVal;
|
|
/// <summary>
|
/// 动画器.
|
/// </summary>
|
public Animator ActionAnimator;
|
|
protected AnimationClip[] clips;
|
|
protected bool bInDeathAct = false;
|
|
/// <summary>
|
/// 原地罚站
|
/// </summary>
|
/// <param name="can"></param>
|
public bool CanMove { get; set; } = true;
|
|
/// <summary>
|
/// 分别对应三种不同的纹理.
|
/// </summary>
|
public Texture poisonTex;
|
|
public Texture frozenTex;
|
|
public Texture commonTex;
|
|
/// <summary>
|
/// 速度降低值.
|
/// </summary>
|
protected float speedSlowRate = 0.0f;
|
|
// 中毒相关的数据
|
protected int poisonTimes = 0;
|
protected float poisonHurt = 0.0f;
|
protected int poisonAttid = 0;
|
protected float timeToPoisonHurt = 0.0f;
|
|
/// <summary>
|
/// 是否处于 中毒状态
|
/// </summary>
|
protected bool isPoison;
|
|
/// <summary>
|
/// 是否处于减速状态
|
/// </summary>
|
protected bool isSlowDown;
|
|
private bool isFrost;
|
|
/// <summary>
|
/// 是否处于冰冻状态
|
/// </summary>
|
/// <value></value>
|
public bool IsFrost
|
{
|
get { return isFrost; }
|
set
|
{
|
if (!isFrost)
|
{
|
if (value)
|
{
|
EndlessGameUI.instance.FloatFrostWord(transform.position);
|
PlaySlowDownEffect();
|
CanMove = false;
|
}
|
}
|
else
|
{
|
if (!value)
|
{
|
StopSlowDownEffect();
|
CanMove = true;
|
}
|
}
|
|
isFrost = value;
|
}
|
}
|
|
/// <summary>
|
/// 冰冻剩余时间,< 0时解除冰冻,冰冻期间不会被再次冰冻
|
/// </summary>
|
public float FrostRemainTime { get; set; }
|
|
/// <summary>
|
/// 中毒粒子特效
|
/// </summary>
|
public ParticleSystem PoisonParticle;
|
|
/// <summary>
|
/// 中毒结束播放的粒子特效
|
/// </summary>
|
public ParticleSystem PoisonEndParticle;
|
|
/// <summary>
|
/// 减速粒子特效
|
/// </summary>
|
public ParticleSystem SlowDownParticle;
|
|
/// <summary>
|
/// 被火技能攻击特效
|
/// </summary>
|
public ParticleSystem FireSkillParticle;
|
|
/// <summary>
|
/// 降低移动速度.
|
/// </summary>
|
/// <param name="rate"></param>
|
public void addSpeedSlowRate(float rate)
|
{
|
speedSlowRate += rate;
|
if (speedSlowRate >= 0.6f)
|
speedSlowRate = 0.6f;
|
}
|
|
private bool _HasSlowDownText;
|
public bool HasSlowDownText
|
{
|
set
|
{
|
_HasSlowDownText = value;
|
}
|
get
|
{
|
return _HasSlowDownText;
|
}
|
}
|
|
/// <summary>
|
/// 怪物中毒.
|
/// </summary>
|
/// <param name="damage"></param>
|
public void poisonAgent(float damage, int attid)
|
{
|
if (poisonTimes >= 1) return;
|
|
if (!isPoison)
|
{
|
isPoison = true;
|
|
if (PoisonParticle != null)
|
PoisonParticle.Play();
|
}
|
|
poisonTimes++;
|
poisonAttid = attid;
|
poisonHurt = (float)Math.Floor(configuration.maxHealth / 20.0f);
|
timeToPoisonHurt = 1.0f;
|
}
|
|
public bool bInPoison { get { return poisonHurt > 0; } }
|
|
/// <summary>
|
/// 处理中毒相关的数据
|
/// </summary>
|
/// <param name="time"></param>
|
protected void updatePoison(float time)
|
{
|
timeToPoisonHurt -= time;
|
if (timeToPoisonHurt <= 0)
|
{
|
Vector3 backPos = transform.position;
|
TakeDamage(poisonHurt, transform.position, null, poisonAttid);
|
// if ((poisonHurt > 0) && (!opponentAgent))
|
// {
|
// if (GameUI.instanceExists)
|
// GameUI.instance.generateBloodText(backPos, poisonHurt, false, true);
|
// else if (EndlessGameUI.instanceExists)
|
// EndlessGameUI.instance.generateBloodText(backPos, poisonHurt, false, true);
|
// }
|
|
if (poisonHurt > 0)
|
timeToPoisonHurt = 1.0f;
|
}
|
}
|
|
/// <summary>
|
/// Gets the attached nav mesh agent velocity
|
/// </summary>
|
public override Vector3 velocity
|
{
|
get { return m_NavMeshAgent.velocity; }
|
}
|
|
public bool bInDeathState
|
{
|
get { return bInDeathAct; }
|
set { bInDeathAct = value; }
|
}
|
|
/// <summary>
|
/// 是否是可再生的怪物类型
|
/// </summary>
|
public bool bRespawn { get; set; }
|
|
/// <summary>
|
/// The current state of the agent along the path
|
/// </summary>
|
public State state { get; protected set; }
|
|
/// <summary>
|
/// 当前Agent对应到的兵线ID.
|
/// </summary>
|
public int waveLineID { get; set; }
|
|
public NavMeshAgent navMeshAgent
|
{
|
get { return m_NavMeshAgent; }
|
}
|
|
/// <summary>
|
/// 是否显示目标位置信息
|
/// </summary>
|
public bool bShowDebugNode = false;
|
|
/// <summary>
|
/// 持续寻路,起到寻路成功
|
/// </summary>
|
protected bool nodeMoveUpdate = false;
|
|
/// <summary>
|
/// 代理的类型
|
/// </summary>
|
public SpawnAgentType AgentType { get; set; } = SpawnAgentType.Normal;
|
|
/// <summary>
|
/// 无尽模式小怪对应 endless_enemy表中数据
|
/// </summary>
|
public endless_enemy EnemyData { get; set; }
|
|
/// <summary>
|
/// Accessor to <see cref="m_NavMeshAgent"/>
|
/// </summary>
|
public NavMeshAgent navMeshNavMeshAgent
|
{
|
get { return m_NavMeshAgent; }
|
set { m_NavMeshAgent = value; }
|
}
|
|
public float distanceToDest
|
{
|
get { return m_DisToDestination; }
|
}
|
|
/// <summary>
|
/// The area mask of the attached nav mesh agent
|
/// </summary>
|
public int navMeshMask
|
{
|
get { return m_NavMeshAgent.areaMask; }
|
}
|
|
/// <summary>
|
/// Gets this agent's original movement speed
|
/// </summary>
|
public float originalMovementSpeed { get; protected set; }
|
|
/// <summary>
|
/// 小怪的动作状态
|
/// </summary>
|
public AgentActionState ActionState { get; protected set; }
|
|
private string paramName = "AgentActionState";
|
|
/// <summary>
|
/// 外部赋值,唯一标识一个代理
|
/// </summary>
|
public int Id { get; set; }
|
|
private Tween repelTween;
|
|
/// <summary>
|
/// 击退距离
|
/// </summary>
|
protected float repelDistance { get; set; } = 7f;
|
|
/// <summary>
|
/// 击退时间间隔
|
/// </summary>
|
protected float repelTime { get; set; } = 0.5f;
|
|
/// <summary>
|
/// 是否受到木属性强化子弹攻击
|
/// </summary>
|
public bool IsEnhancedBulletAttack { get; set; }
|
|
/// <summary>
|
/// 木属性精灵瞄准小怪特效
|
/// </summary>
|
public ParticleSystem WoodAimEffect;
|
|
/// <summary>
|
/// 有几个木属性精灵瞄准了Agent
|
/// </summary>
|
public int WoodAimCount { get; set; }
|
|
/// <summary>
|
/// 血条的引用
|
/// </summary>
|
public AgentBlood BloodBar { get; set; }
|
|
/// <summary>
|
/// 更新怪物的移动速度。
|
/// </summary>
|
/// <param name="fscale"></param>
|
public void SetMoveSpeedScale(float fscale)
|
{
|
fMoveSpeed = fMoveSpeed * fscale;
|
}
|
|
/// <summary>
|
/// 动态设置Agent的血量数据.
|
/// </summary>
|
/// <param name="health"></param> 设置当前小怪的血量.
|
/// <param name="moveSpeed"></param> 设置当前小怪的移动速度
|
/// <param name="coinDrop"></param> 设置当前小怪的金币掉落数据
|
public void SetAgentData(float health, float moveSpeed = -1, int coinDrop = 0)
|
{
|
SpawnBlood();
|
BloodBar.SetCurrentBlood(1f, false);
|
configuration.maxHealth = health;
|
configuration.startingHealth = health;
|
configuration.SetMaxHealth(health);
|
configuration.Init();
|
configuration.SetHealth(health);
|
|
// 只有在速度大于零的情况下才会设置.
|
if (moveSpeed > 0)
|
fMoveSpeed = moveSpeed;
|
|
mLootDrop.lootDropped = coinDrop;
|
|
mAgentData.speed = fMoveSpeed;
|
mAgentData.hp = configuration.maxHealth;
|
|
//
|
mStartYVal = transform.position.y;
|
mStartZVal = transform.position.z;
|
|
speedSlowRate = 0.0f;
|
poisonHurt = 0;
|
poisonAttid = 0;
|
poisonTimes = 0;
|
timeToPoisonHurt = 0;
|
CanMove = true;
|
}
|
|
/// <summary>
|
/// 生成血条
|
/// </summary>
|
/// <param name="agent"></param>
|
private void SpawnBlood()
|
{
|
if (BloodBar == null)
|
{
|
GameObject prefab = Resources.Load<GameObject>("Prefabs/AgentBlood");
|
GameObject obj = Instantiate(prefab);
|
BloodBar = obj.GetComponent<AgentBlood>();
|
GameObject bloodUI = GameObject.Find("MainUI/BloodUI");
|
obj.transform.SetParent(bloodUI.transform, false);
|
AgentBlood agentBlood = obj.GetComponent<AgentBlood>();
|
agentBlood.Target = this;
|
|
configuration.died += agentBlood.OnDied;
|
configuration.healthChanged += agentBlood.OnHealthChanged;
|
}
|
BloodBar.Hide();
|
}
|
|
/// <summary>
|
/// Checks if the path is blocked
|
/// </summary>
|
/// <value>
|
/// The status of the agent's path
|
/// </value>
|
protected virtual bool isPathBlocked
|
{
|
get { return m_NavMeshAgent.pathStatus == NavMeshPathStatus.PathPartial; }
|
}
|
|
/// <summary>
|
/// Is the Agent close enough to its destination?
|
/// </summary>
|
protected virtual bool isAtDestination
|
{
|
get { return navMeshNavMeshAgent.remainingDistance <= navMeshNavMeshAgent.stoppingDistance; }
|
}
|
|
/// <summary>
|
/// 改变m_NextNode,因为要切换基地
|
/// </summary>
|
/// <param name="node"></param>
|
public void ChangeNextNode(Node node)
|
{
|
m_NextNode = node;
|
}
|
|
/// <summary>
|
/// Sets the node to navigate to
|
/// </summary>
|
/// <param name="node">The node that the agent will navigate to</param>
|
public virtual void SetNode(Node node, int lineid)
|
{
|
if (lineid >= 0)
|
waveLineID = lineid;
|
|
// 设置为空的时候,是为了不再每一帧更新Agent的位置信息
|
if (node == null)
|
{
|
m_CurrentNode = null;
|
m_NextNode = null;
|
return;
|
}
|
|
if (m_CurrentNode == null)
|
{
|
initNode = node;
|
}
|
|
m_CurrentNode = node;
|
|
// 需要设置移动的目标Node.
|
m_NextNode = m_CurrentNode.GetNextNode();
|
|
MoveToNode();
|
}
|
|
/// <summary>
|
/// 处理Agent的锁定Buf,播放特效,设置速度为零,并时间到达后重设数据。
|
/// </summary>
|
/// <param name="binfo"></param>
|
protected virtual void SetAgentStopBuff(buffinfo binfo)
|
{
|
MoveStopTime = binfo.last / 1000;
|
}
|
|
/// <summary>
|
/// 设置当前Agent对应的各种技能属性ID.
|
/// </summary>
|
/// <param name="effid"></param>
|
public void SetAgentBuffEffect(int buffid)
|
{
|
buffinfo bufdata = JsonDataCenter.GetBuffFromId(buffid);
|
if (bufdata == null) return;
|
|
if (bufdata.buff_func[0] == 3)
|
{
|
SetAgentStopBuff(bufdata);
|
}
|
else
|
return;
|
}
|
|
public override bool isAgent()
|
{
|
return true;
|
}
|
|
/// <summary>
|
/// Agent被击退
|
/// </summary>
|
public void AgentBeRepelled()
|
{
|
CanMove = false;
|
|
if (repelTween != null)
|
repelTween.Kill();
|
|
Node StartingNodeList = EndlessLevelManager.instance.StartingNodeList[waveLineID];
|
repelTween = transform.DOMoveZ(Mathf.Min(StartingNodeList.transform.position.z, transform.position.z + repelDistance), repelTime);
|
repelTween.onComplete = RepelCompleted;
|
}
|
|
private void RepelCompleted()
|
{
|
CanMove = true;
|
repelTween = null;
|
}
|
|
/// <summary>
|
/// 播放木属性瞄准特效
|
/// </summary>
|
public void PlayWoodAimEffect()
|
{
|
if (WoodAimEffect == null) return;
|
|
WoodAimEffect.Play();
|
}
|
|
/// <summary>
|
/// 停止木属性瞄准特效
|
/// </summary>
|
public void StopWoodAimEffect()
|
{
|
if (WoodAimEffect == null) return;
|
|
WoodAimEffect.Stop();
|
WoodAimEffect.Clear();
|
}
|
|
/// <summary>
|
/// Stops the navMeshAgent and attempts to return to pool
|
/// </summary>
|
public override void Remove()
|
{
|
GameObject prefab = Resources.Load<GameObject>("Prefabs/Endless/AgentDeathEffect");
|
if (prefab != null)
|
{
|
GameObject obj = Poolable.TryGetPoolable(prefab);
|
obj.transform.position = transform.position;
|
ParticleSystem ps = obj.transform.GetChild(0).GetComponent<ParticleSystem>();
|
ps?.Play();
|
}
|
|
if (BloodBar)
|
{
|
BloodBar.Target = null;
|
Destroy(BloodBar.gameObject);
|
}
|
// 统一管理器内删除当前的Agent:
|
AgentInsManager.instance.removeAgent(this);
|
if (EnemyData != null)
|
EventCenter.Ins.BroadCast((int)KTGMGemClient.EventType.EndlessAgentDead, EnemyData.point);
|
base.Remove();
|
|
if (m_LevelManager) m_LevelManager.DecrementNumberOfEnemies();
|
else endlessLevelManager.DecrementNumberOfEnemies();
|
if (m_NavMeshAgent.enabled)
|
{
|
m_NavMeshAgent.isStopped = true;
|
}
|
m_NavMeshAgent.enabled = false;
|
|
// 必须要重置数据,不然会有一系列的小Bug.
|
m_CurrentNode = null;
|
m_NextNode = null;
|
|
liveID = liveID + 1;
|
|
speedSlowRate = 0.0f;
|
poisonHurt = 0;
|
poisonAttid = 0;
|
poisonTimes = 0;
|
timeToPoisonHurt = 0;
|
IsFrost = false;
|
bInDeathAct = false;
|
ChangeState(AgentActionState.Move);
|
configuration.ClearShieldWall();
|
|
if (WoodAimCount > 0)
|
WoodAimCount = 0;
|
StopWoodAimEffect();
|
//SetTargetableMatColor(Color.white);
|
|
// 删除当前停止特效和状态.
|
if (MoveStopTime > 0)
|
MoveStopTime = 0.0f;
|
|
if (repelTween != null)
|
{
|
repelTween.Kill();
|
repelTween = null;
|
}
|
|
// 停止DoTween动画.
|
transform.DOKill();
|
Destroy(gameObject);
|
}
|
|
/// <summary>
|
/// Setup all the necessary parameters for this agent from configuration data
|
/// </summary>
|
public virtual void Initialize()
|
{
|
Id = GameUtils.GetId();
|
ResetPositionData();
|
LazyLoad();
|
configuration.SetHealth(configuration.maxHealth);
|
state = isPathBlocked ? State.OnPartialPath : State.OnCompletePath;
|
|
// River TEST CODE TO DELETE: 测试成功之后,是需要删除navMeshAgent组件的,根本不需要
|
m_NavMeshAgent.enabled = false;
|
|
if (m_LevelManager) m_LevelManager.IncrementNumberOfEnemies();
|
else endlessLevelManager.IncrementNumberOfEnemies();
|
|
|
//
|
// 设置一个DOTween队列,让场景内刷出来的Agent有一个变大淡出的效果,此时是无敌效果加持.
|
configuration.bInvincible = true;
|
mDefaultScale = transform.localScale;
|
Sequence agentTweenSeq = DOTween.Sequence();
|
var ss = mDefaultScale * 0.3f;
|
transform.localScale = transform.localScale * 0.3f;
|
Tweener agScale = transform.DOScale(mDefaultScale, 0.3f);
|
agentTweenSeq.Append(agScale);
|
agentTweenSeq.AppendCallback(beDamageStart);
|
|
nodeMoveUpdate = false;
|
|
// 获取移动速度
|
fMoveSpeed = m_NavMeshAgent.speed / 2.0f;
|
_HasSlowDownText = false;
|
}
|
|
protected void beDamageStart()
|
{
|
configuration.bInvincible = false;
|
}
|
|
/// <summary>
|
/// 把当前的Agent拉到起始位置
|
/// </summary>
|
public void ResetAgentToInitialPos()
|
{
|
if (!initNode) return;
|
|
Vector3 initPos = initNode.GetRandomPointInNodeArea();
|
transform.position = initPos;
|
ResetPositionData();
|
|
SetNode(initNode, -1);
|
|
// 更新AgentMgr,确保不再攻击回到原点的Agent.
|
AgentInsManager.instance.updateInsMgrPos(opponentAgent);
|
|
}
|
|
/// <summary>
|
/// 执行重设到开始位置的Agent特效,需要执行一系列的动作.
|
/// </summary>
|
public void execAgentPosResetAction()
|
{
|
// 无敌状态不增加当前的效果:
|
if (configuration.bInvincible) return;
|
|
// 只有处于非PosEffect状态才可以有这个状态.
|
var posResetEff = GetComponent<AgentResetPosEffect>();
|
if (posResetEff == null)
|
{
|
posResetEff = gameObject.AddComponent<AgentResetPosEffect>();
|
|
// 初始化当前的PosResetEffect
|
posResetEff.Initialize(3, AgentInsManager.instance.posResetFx);
|
}
|
}
|
|
/// <summary>
|
/// 预处理当前的Agent最大或最小可以到达的ZValue.
|
/// </summary>
|
protected void PreProcessZValue()
|
{
|
if (!m_CurrentNode) return;
|
Node nextNode = m_CurrentNode.GetNextNode();
|
if (!nextNode) return;
|
}
|
|
/// <summary>
|
/// Finds the next node in the path
|
/// </summary>
|
public virtual void MoveToNextNode(Node currentlyEnteredNode)
|
{
|
// Don't do anything if the calling node is the same as the m_CurrentNode
|
if (m_NextNode != currentlyEnteredNode)
|
{
|
return;
|
}
|
if (m_NextNode == null)
|
{
|
Debug.LogError("Cannot find current node");
|
return;
|
}
|
|
Node nextNode = m_NextNode.GetNextNode();
|
if (nextNode == null)
|
{
|
if (m_NavMeshAgent.enabled)
|
{
|
m_NavMeshAgent.isStopped = true;
|
}
|
|
// 清空当前的Node和NextNode.
|
m_CurrentNode = null;
|
HandleDestinationReached();
|
m_NextNode = null;
|
|
return;
|
}
|
|
Debug.Assert(nextNode != m_CurrentNode);
|
|
SetNode(m_NextNode, -1);
|
}
|
|
/// <summary>
|
/// Moves the agent to a position in the <see cref="Agent.m_NextNode" />
|
/// 预计算开始位置,结束位置,以及移动方向。
|
/// </summary>
|
public virtual void MoveToNode()
|
{
|
if ((!m_CurrentNode) || (!m_NextNode))
|
{
|
if (!m_CurrentNode)
|
Debug.Log("当前的Node值为空.");
|
else
|
Debug.Log("NextNode的值为空.");
|
|
return;
|
}
|
|
m_Destination = m_NextNode;
|
m_SrcPosition = m_CurrentNode.transform.position;
|
|
//
|
// 预处理,确定移动方向.
|
if (m_Destination.transform.position.x == m_SrcPosition.x)
|
{
|
if (m_Destination.transform.position.z > m_SrcPosition.z)
|
moveDir = EAgentMovDir.ZPositive;
|
else
|
moveDir = EAgentMovDir.ZNegative;
|
}
|
else
|
{
|
if (m_Destination.transform.position.z != m_SrcPosition.z)
|
{
|
Debug.Log("目标位置和开始位置的Z值不相等:" + m_Destination.transform.position.x + "," + m_Destination.transform.position.z + "," +
|
m_SrcPosition.x + "," + m_SrcPosition.z);
|
}
|
if (m_Destination.transform.position.x > m_SrcPosition.x)
|
moveDir = EAgentMovDir.XPositive;
|
else
|
moveDir = EAgentMovDir.XNegative;
|
}
|
}
|
|
/// <summary>
|
/// The logic for what happens when the destination is reached
|
/// </summary>
|
public virtual void HandleDestinationReached()
|
{
|
state = State.PathComplete;
|
if (destinationReached != null)
|
{
|
destinationReached(m_NextNode);
|
}
|
}
|
|
/// <summary>
|
/// Lazy Load, if necesaary and ensure the NavMeshAgent is disabled
|
/// </summary>
|
protected override void Awake()
|
{
|
//Debug.Log("哪里生成的?");
|
base.Awake();
|
LazyLoad();
|
m_NavMeshAgent.enabled = false;
|
MoveStopTime = 0.0f;
|
}
|
|
private void PlaySlowDownEffect()
|
{
|
if (SlowDownParticle != null)
|
SlowDownParticle.Play();
|
}
|
|
private void StopSlowDownEffect()
|
{
|
if (SlowDownParticle != null && !isSlowDown && !IsFrost)
|
{
|
SlowDownParticle.Stop();
|
SlowDownParticle.Clear();
|
}
|
}
|
[SerializeField]
|
private float destinationOffect = 0f;//怪物移动到塔位的偏移量
|
/// <summary>
|
/// 根据帧间的时间,来更新Agent的位置信息,其它信息直接删除.
|
/// 1:
|
/// </summary>
|
/// <param name="deltaTime"></param>
|
protected void updateAgentPos(float deltaTime)
|
{
|
if (!m_CurrentNode || !m_NextNode || fMoveSpeed <= 0.0001f) return;
|
|
float finalSpeed = fMoveSpeed * (1 - speedSlowRate);
|
|
if (speedSlowRate > 0 && !isSlowDown)
|
{
|
isSlowDown = true;
|
PlaySlowDownEffect();
|
}
|
else if (speedSlowRate == 0 && isSlowDown)
|
{
|
isSlowDown = false;
|
StopSlowDownEffect();
|
}
|
|
Vector3 curPos = m_NavMeshAgent.transform.position;
|
bool swithNode = false;
|
|
switch (moveDir)
|
{
|
case EAgentMovDir.ZPositive:
|
{
|
curPos.z += (deltaTime * finalSpeed);
|
if (curPos.z >= m_Destination.transform.position.z)
|
{
|
curPos.z = m_Destination.transform.position.z;
|
swithNode = true;
|
}
|
break;
|
}
|
case EAgentMovDir.ZNegative:
|
{
|
curPos.z -= (deltaTime * finalSpeed);
|
if (curPos.z <= m_Destination.transform.position.z + destinationOffect)
|
{
|
curPos.z = m_Destination.transform.position.z + destinationOffect;
|
swithNode = true;
|
}
|
break;
|
}
|
case EAgentMovDir.XPositive:
|
{
|
curPos.x += (deltaTime * finalSpeed);
|
if (curPos.x >= m_Destination.transform.position.x)
|
{
|
curPos.x = m_Destination.transform.position.x;
|
swithNode = true;
|
}
|
break;
|
}
|
case EAgentMovDir.XNegative:
|
{
|
curPos.x -= (deltaTime * finalSpeed);
|
if (curPos.x <= m_Destination.transform.position.x)
|
{
|
curPos.x = m_Destination.transform.position.x;
|
swithNode = true;
|
}
|
break;
|
}
|
}
|
|
// 根据是否到达一个结点来进行不同的处理
|
if (swithNode)
|
{
|
MoveToNextNode(m_NextNode);
|
}
|
else
|
{
|
// Y值插值.
|
float lerpVal = (curPos.z - mStartZVal) / (m_Destination.transform.position.z - mStartZVal);
|
if (!opponentAgent)
|
// curPos.y = mStartYVal + lerpVal * 60f;
|
curPos.y = mStartYVal;
|
m_NavMeshAgent.transform.position = curPos;
|
}
|
|
return;
|
}
|
|
/// <summary>
|
/// 更新动作信息.
|
/// </summary>
|
/// <returns></returns>
|
protected void UpdateAction()
|
{
|
if (ActionAnimator == null) return;
|
|
AnimatorStateInfo stateInfo = ActionAnimator.GetCurrentAnimatorStateInfo(0);
|
|
switch (ActionState)
|
{
|
case AgentActionState.Move:
|
break;
|
case AgentActionState.GetHit:
|
if (stateInfo.normalizedTime >= 1f)
|
ChangeState(AgentActionState.Move);
|
break;
|
case AgentActionState.Attack:
|
if (stateInfo.normalizedTime >= 1f)
|
{
|
if (isDead)
|
{
|
AgentInsManager.instance.removeAgent(this);
|
Remove();
|
}
|
}
|
break;
|
case AgentActionState.Death:
|
if (stateInfo.normalizedTime >= 1f)
|
Remove();
|
break;
|
}
|
}
|
|
private void ChangeState(AgentActionState state)
|
{
|
if (state == ActionState) return;
|
|
ActionState = state;
|
if (ActionAnimator != null)
|
ActionAnimator.SetInteger(paramName, (int)state);
|
}
|
|
/// <summary>
|
/// Updates the agent in its different states,
|
/// Reset destination when path is stale
|
/// River: 更新的时候,加入离最终目标的距离数据.
|
/// </summary>
|
protected virtual void LateUpdate()
|
{
|
UpdateAction();
|
HandleShieldWall();
|
HandleFrost();
|
|
// 处理死亡状态了,不必再移动:
|
if (bInDeathAct || !CanMove) return;
|
|
m_Destination = initNode.GetNextNode();
|
updateAgentPos(Time.deltaTime);
|
|
// 计算离目标的距离,非最终距离,只用于比较大小
|
m_DisToDestination = GetDisToDestination();
|
|
// 计算中毒相关.
|
if ((poisonTimes > 0) && (poisonHurt > 0))
|
updatePoison(Time.deltaTime);
|
}
|
|
private void HandleFrost()
|
{
|
if (IsFrost)
|
{
|
if (FrostRemainTime > 0f)
|
{
|
FrostRemainTime -= Time.deltaTime;
|
|
if (FrostRemainTime <= 0f)
|
IsFrost = false;
|
}
|
else
|
IsFrost = false;
|
}
|
}
|
|
protected virtual void Update()
|
{
|
|
}
|
|
/// <summary>
|
/// 处理魔法护盾血量值
|
/// </summary>
|
protected void HandleShieldWall()
|
{
|
if (configuration.IsExistShieldWall)
|
{
|
if (configuration.ShieldWallCurrentHealth <= 0.00001f)
|
configuration.IsExistShieldWall = false;
|
else if (configuration.ShieldWallEffectiveTime > 0.00001f)
|
{
|
if (configuration.ShieldWallRemainTime <= 0.00001f)
|
configuration.IsExistShieldWall = false;
|
else
|
configuration.ShieldWallRemainTime -= Time.deltaTime;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 限制当前Agent的移动位置,让Agent的移动看起来更帅一些
|
/// </summary>
|
protected void restrictAgentPos()
|
{
|
/* // 设置最大的Z值,确保按路线移动
|
Vector3 pos = m_NavMeshAgent.transform.position;
|
if (m_PositiveZ)
|
{
|
if (pos.z > m_ZMaxValue)
|
{
|
pos.z = m_ZMaxValue;
|
m_NavMeshAgent.transform.position = pos;
|
}
|
}
|
else
|
{
|
if (pos.z < m_ZMaxValue)
|
{
|
pos.z = m_ZMaxValue;
|
m_NavMeshAgent.transform.position = pos;
|
}
|
}
|
// 设置X值的限制,确保按路线移动
|
if(m_PositiveX)
|
{
|
if( pos.x > m_XMaxValue)
|
{
|
pos.x = m_XMaxValue;
|
m_NavMeshAgent.transform.position = pos;
|
}
|
}
|
else
|
{
|
if( pos.x < m_XMaxValue)
|
{
|
pos.x = m_XMaxValue;
|
m_NavMeshAgent.transform.position = pos;
|
}
|
}*/
|
}
|
|
/// <summary>
|
/// Set the NavMeshAgent's destination
|
/// </summary>
|
/// <param name="nextPoint">The position to navigate to</param>
|
protected virtual bool NavigateTo(Vector3 nextPoint)
|
{
|
// River mod: 完全自己控制移动 ,不需要navMeshAgent.
|
return true;
|
/*
|
LazyLoad();
|
bool res = false;
|
if (m_NavMeshAgent.isOnNavMesh)
|
{
|
nextPoint.y += 0.1f;
|
res = m_NavMeshAgent.SetDestination(nextPoint);
|
}
|
return res;*/
|
}
|
|
/// <summary>
|
/// 计算当前的Agent距离目标的最终距离,不是真实距离,是比较大小的数值.
|
/// </summary>
|
/// <returns></returns>
|
protected float GetDisToDestination()
|
{
|
if (!m_CurrentNode) return 0;
|
|
float tmpDis = 0;
|
Node tmpNode = m_CurrentNode.GetNextNode();
|
while (tmpNode.GetNextNode() != null)
|
{
|
tmpDis += NODE_DIS;
|
tmpNode = tmpNode.GetNextNode();
|
}
|
Vector3 tpos = position;
|
tpos.y = mStartYVal;
|
tmpDis += Vector3.Distance(tpos, m_Destination.transform.position);
|
|
return tmpDis;
|
}
|
|
protected ChangeMat changeMat;
|
/// <summary>
|
/// This is a lazy way of caching several components utilised by the Agent
|
/// </summary>
|
protected virtual void LazyLoad()
|
{
|
if (m_NavMeshAgent == null)
|
{
|
m_NavMeshAgent = GetComponent<NavMeshAgent>();
|
mLootDrop = GetComponent<LootDrop>();
|
originalMovementSpeed = m_NavMeshAgent.speed;
|
}
|
if (m_LevelManager == null)
|
{
|
m_LevelManager = LevelManager.instance;
|
}
|
if (!endlessLevelManager)
|
endlessLevelManager = EndlessLevelManager.instance;
|
}
|
|
/// <summary>
|
/// 播放受击动画.
|
/// </summary>
|
public void PlayOnHit()
|
{
|
ChangeState(AgentActionState.GetHit);
|
}
|
|
/// <summary>
|
/// 播放受击动画,直接从头开始播放
|
/// </summary>
|
public void PlayOnHitImmediately()
|
{
|
ChangeState(AgentActionState.GetHit);
|
if (ActionAnimator)
|
{
|
ActionAnimator.Play("GetHit", 0, 0);
|
ActionAnimator.Update(0);
|
}
|
}
|
|
/// <summary>
|
/// 播放火技能打击动画
|
/// </summary>
|
public void PlayFireSkillHit()
|
{
|
if (FireSkillParticle != null)
|
FireSkillParticle.Play();
|
}
|
|
public void PlayAttack()
|
{
|
AudioSourceManager.Ins.Play(AudioEnum.AttackTower);
|
ChangeState(AgentActionState.Attack);
|
}
|
|
public override void PlayDeath()
|
{
|
if (bInDeathAct) return;
|
|
if (isPoison)
|
{
|
isPoison = false;
|
// 移除Agent身上的中毒特效,并播放一个中毒效果消失的特效
|
if (PoisonParticle != null)
|
{
|
PoisonParticle.Stop();
|
PoisonParticle.Clear();
|
if (PoisonEndParticle != null)
|
PoisonEndParticle.Play();
|
}
|
}
|
|
if (isSlowDown)
|
{
|
isSlowDown = false;
|
|
if (SlowDownParticle != null)
|
{
|
SlowDownParticle.Stop();
|
SlowDownParticle.Clear();
|
}
|
}
|
|
bInDeathAct = true;
|
|
if (EnemyData != null && EndlessGameUI.instance.state != EndlessGameUI.State.GameOver)
|
{
|
// Debug.Log("小怪被杀死了,增加能量" + EnemyData.energy);
|
EventCenter.Ins.BroadCast((int)KTGMGemClient.EventType.EnergyUp, EnemyData.energy);
|
}
|
|
Remove();
|
}
|
|
/// <summary>
|
/// Move along the path, change to <see cref="Agent.State.OnPartialPath" />
|
/// </summary>
|
protected virtual void OnCompletePathUpdate()
|
{
|
if (isPathBlocked)
|
{
|
state = State.OnPartialPath;
|
}
|
}
|
|
/// <summary>
|
/// Peforms the relevant path update
|
/// </summary>
|
protected abstract void PathUpdate();
|
|
/// <summary>
|
/// The behaviour for when the agent has been blocked
|
/// </summary>
|
protected abstract void OnPartialPathUpdate();
|
|
|
#if UNITY_EDITOR
|
/// <summary>
|
/// Draw the agent's path
|
/// </summary>
|
protected virtual void OnDrawGizmosSelected()
|
{
|
if (m_NavMeshAgent != null)
|
{
|
Vector3[] pathPoints = m_NavMeshAgent.path.corners;
|
int count = pathPoints.Length;
|
for (int i = 0; i < count - 1; i++)
|
{
|
Vector3 from = pathPoints[i];
|
Vector3 to = pathPoints[i + 1];
|
Gizmos.DrawLine(from, to);
|
}
|
Gizmos.DrawWireSphere(m_NavMeshAgent.destination, 0.2f);
|
}
|
}
|
#endif
|
}
|
}
|