using System.Collections.Generic;
|
using vnxbqy.UI;
|
using UnityEngine;
|
using UnityEngine.AI;
|
using UnityEngine.Events;
|
|
public abstract class GActor
|
{
|
#region 角色基础模块(表现,基本朝向,坐标)
|
|
public Transform MP_Name { get; protected set; }
|
public Transform MP_Name1 { get; protected set; }
|
public Transform MP_Hit { get; protected set; }
|
public Transform MP_Down { get; protected set; }
|
public Transform MP_Stun { get; protected set; }
|
public Transform MP_Weapon { get; protected set; }
|
public Transform MP_FollowDown { get; protected set; }
|
|
public uint ClientInstID { get; protected set; }
|
public uint ServerInstID { get; protected set; }
|
|
protected Transform m_Root = null;
|
public Transform Root { get { return m_Root; } }
|
|
/// <summary>
|
/// 如果有,杀死这个npc的角色服务器实例ID
|
/// </summary>
|
private uint m_KillServerInstID;
|
public uint KillerServerInstID
|
{
|
get { return m_KillServerInstID; }
|
set { m_KillServerInstID = value; }
|
}
|
public byte killHurtType;
|
public int belongEventID = -1;
|
private Vector3 m_BornPos;
|
public virtual Vector3 BornPos
|
{
|
get { return m_BornPos; }
|
set { m_BornPos = value; }
|
}
|
|
private Vector3 m_Pos;
|
public Vector3 Pos
|
{
|
get { return m_Pos; }
|
set
|
{
|
#if UNITY_EDITOR
|
if (this is GA_NpcFunc)
|
{
|
string _content = string.Format("{0} / {1} => 设置了位置: {2}", ServerInstID, (this as GA_NpcFunc).NpcConfig.NPCID, value);
|
RuntimeLogUtility.AddLog(_content, ServerInstID, true);
|
if (value == Constants.Special_Hide_Position)
|
{
|
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
|
System.Diagnostics.StackFrame[] sfs = st.GetFrames();
|
for (int i = 0; i < sfs.Length && i < 2; ++i)
|
{
|
System.Reflection.MethodBase mb = sfs[i].GetMethod();
|
RuntimeLogUtility.AddLog(string.Format("{0} / {1} => └ {2}.{3}", ServerInstID, (this as GA_NpcFunc).NpcConfig.NPCID, mb.DeclaringType.FullName, mb.Name), ServerInstID, true);
|
}
|
}
|
}
|
#endif
|
m_Pos = value;
|
if (m_Root)
|
{
|
m_Root.position = m_Pos;
|
}
|
// #if UNITY_EDITOR
|
// if (this is GActorNpcFight)
|
// {
|
// if (ServerInstID == 4)
|
// {
|
// Debug.Log(ServerInstID + " => 坐标改变.....: " + value);
|
// }
|
// }
|
// #endif
|
}
|
}
|
|
private float m_EulerAngles;
|
public float EulerAngles
|
{
|
get { return m_EulerAngles; }
|
set
|
{
|
m_EulerAngles = value;
|
if (m_Root)
|
{
|
m_Root.eulerAngles = new Vector3(0, m_EulerAngles, 0);
|
m_Forward = m_Root.forward;
|
m_Rotation = Quaternion.LookRotation(m_Forward, Vector3.up);
|
}
|
}
|
}
|
|
private Vector3 m_Forward;
|
public Vector3 Forward
|
{
|
get
|
{
|
if (m_Forward == Vector3.zero)
|
{
|
m_Forward = m_Root.forward;
|
}
|
return m_Forward;
|
}
|
set
|
{
|
value.y = 0;
|
if (value != Vector3.zero)
|
{
|
m_Forward = value;
|
m_Rotation = Quaternion.LookRotation(m_Forward, Vector3.up);
|
if (m_Root)
|
{
|
m_Root.rotation = m_Rotation;
|
}
|
|
if (ServerInstID == PlayerDatas.Instance.PlayerId)
|
{
|
// System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
|
// System.Diagnostics.StackFrame[] sfs = st.GetFrames();
|
// string _content = "";
|
// for (int u = 0; u < sfs.Length; ++u)
|
// {
|
// System.Reflection.MethodBase mb = sfs[u].GetMethod();
|
// _content += string.Format("[CALL STACK][{0}]: {1}.{2}\r\n", u, mb.DeclaringType.FullName, mb.Name);
|
// }
|
// Debug.Log(_content);
|
|
// Debug.Log("转向了.......: " + m_Forward);
|
}
|
}
|
}
|
}
|
|
/// <summary>
|
/// 目标朝向
|
/// 主要与当前比较, 做转身的缓动处理的一个方向
|
/// </summary>
|
public Vector3 destForward;
|
|
private Quaternion m_Rotation;
|
public Quaternion Rotation
|
{
|
get { return m_Rotation; }
|
set
|
{
|
m_Rotation = value;
|
if (m_Root)
|
{
|
m_Root.rotation = m_Rotation;
|
}
|
m_Forward = m_Root.forward;
|
}
|
}
|
|
public GameObjType ActorType { get; protected set; }
|
|
public E_ActorGroup Group { get; protected set; }
|
public GActorInfo ActorInfo { get; protected set; }
|
|
private E_ActorState m_State;
|
public E_ActorState State
|
{
|
get { return m_State; }
|
set
|
{
|
m_State = value;
|
// if (ServerInstID == PlayerDatas.Instance.PlayerId)
|
// {
|
// Debug.Log("状态改变: " + value.ToString());
|
// }
|
}
|
}
|
|
public bool ShowOrHide { get; protected set; }
|
|
private bool m_NeedSyncGroundHeight = true;
|
public bool needSyncGroundHeight
|
{
|
get { return m_NeedSyncGroundHeight; }
|
set
|
{
|
m_NeedSyncGroundHeight = value;
|
}
|
}
|
protected Vector3 m_PrevPos;
|
|
public Vector3 PrevPos
|
{
|
get
|
{
|
return m_PrevPos;
|
}
|
set
|
{
|
if (m_PrevPos == value)
|
{
|
return;
|
}
|
m_PrevPos = value;
|
}
|
}
|
|
public abstract int NextAction { get; set; }
|
|
#endregion
|
|
#region 供外部调用基础接口(初始化, 卸载, 更新)
|
|
public void Init(uint serverInstID, uint clientInstID, E_ActorGroup group, GameNetPackBasic package)
|
{
|
ShowOrHide = true;
|
needSyncGroundHeight = true;
|
|
if (!m_Root)
|
{
|
m_Root = new GameObject().transform;
|
// 将被复用,不被销毁
|
Object.DontDestroyOnLoad(m_Root);
|
}
|
|
m_Root.localScale = Vector3.one;
|
Forward = m_Root.forward;
|
// 角色属性
|
if (ActorInfo == null)
|
{
|
ActorInfo = new GActorInfo();
|
}
|
|
ActorInfo.sid = serverInstID;
|
ActorInfo.ownerSID = 0;
|
ActorInfo.serverDie = false;
|
|
#if UNITY_EDITOR
|
m_Root.name = string.Format("{0}_{1}_{2}",
|
GetType().ToString(),
|
serverInstID,
|
clientInstID);
|
#endif
|
|
ServerInstID = serverInstID;
|
ClientInstID = clientInstID;
|
|
Group = group;
|
|
State = E_ActorState.Idle;
|
|
OnInit(package);
|
|
#if UNITY_EDITOR
|
if (ServerInstID == PlayerDatas.Instance.PlayerId)
|
{
|
SnxxzGame.Instance.AddOnDrawGizmosAction(OnDrawGizmos);
|
}
|
#endif
|
}
|
|
#if UNITY_EDITOR
|
private void OnDrawGizmos()
|
{
|
Gizmos.color = Color.red;
|
Gizmos.DrawLine(Pos + new Vector3(0, .5f, 0),
|
Pos + new Vector3(0, .5f, 0) + destForward * 10);
|
|
Gizmos.DrawSphere(Pos, .3f);
|
|
if (State == E_ActorState.AutoRun)
|
{
|
if (GetCorners() != null && GetCorners().Length > 0)
|
{
|
Gizmos.color = Color.blue;
|
for (int i = 0; i < GetCorners().Length; ++i)
|
{
|
Gizmos.DrawSphere(new Vector3(GetCorners()[i].x, Pos.y, GetCorners()[i].z), 0.1f);
|
if (i < GetCorners().Length - 1)
|
{
|
Gizmos.DrawLine(new Vector3(GetCorners()[i].x, Pos.y, GetCorners()[i].z),
|
new Vector3(GetCorners()[i + 1].x, Pos.y, GetCorners()[i + 1].z));
|
}
|
}
|
}
|
}
|
}
|
#endif
|
|
public void UnInit()
|
{
|
#if UNITY_EDITOR
|
if (ServerInstID == PlayerDatas.Instance.PlayerId)
|
{
|
SnxxzGame.Instance.RemoveOnDrawGizmosAction(OnDrawGizmos);
|
}
|
#endif
|
|
OnUnit();
|
|
belongEventID = -1;
|
State = E_ActorState.Idle;
|
DestPos = Vector3.zero;
|
ActorInfo.serverDie = true;
|
ActorInfo.Clear();
|
}
|
|
public abstract void Destroy();
|
|
public void Update()
|
{
|
UpdatePathFinding();
|
|
OnUpdate();
|
}
|
|
public void LateUpdate()
|
{
|
OnLateUpdate();
|
}
|
|
private Vector3 recordSyncHeightPos;
|
|
public void FixedUpdate()
|
{
|
if (recordSyncHeightPos != Pos)
|
{
|
if (needSyncGroundHeight)
|
{
|
float _groundHeight = Pos.y;
|
if (CollisionUtility.TryGetGroundHeight(Pos, out _groundHeight))
|
{
|
Vector3 _pos = Pos;
|
_pos.y = _groundHeight;
|
Pos = _pos;
|
}
|
}
|
|
recordSyncHeightPos = Pos;
|
}
|
|
if (MP_Down)
|
{
|
if (MP_FollowDown)
|
{
|
MP_FollowDown.position = MP_Down.position;
|
}
|
}
|
|
OnFixedUpdate();
|
}
|
|
#endregion
|
|
#region 寻路模块
|
|
private NavMeshPath m_Path = new NavMeshPath();
|
private float m_KeepDist;
|
private E_SearchType m_SearchType;
|
private int m_CornerIndex;
|
|
public Vector3 DestPos { get; private set; }
|
public GActor TargetActor { get; private set; }
|
public E_PathFindStatus PathFindStatus { get; private set; }
|
|
public event UnityAction OnPathFindStart;
|
public event UnityAction OnPathFinding;
|
public event UnityAction OnPathFindStop;
|
|
public Vector3[] GetCorners()
|
{
|
return m_Path.corners;
|
}
|
|
public void MoveToTarget(GActor target, float keepDist = 0, bool force = false)
|
{
|
if (PathFindStatus == E_PathFindStatus.Moving
|
&& m_SearchType == E_SearchType.Dynamic
|
&& TargetActor == target
|
&& !force)
|
{
|
return;
|
}
|
|
Vector3 _validPos = Pos;
|
if (TryGetValidPos(target.Pos, ref _validPos))
|
{
|
m_SearchType = E_SearchType.Dynamic;
|
TargetActor = target;
|
|
// if (this is GA_Hero)
|
// {
|
// Debug.Log("开始寻路至目标: " + target);
|
// }
|
// 这里记录坐标的意义
|
// 在可能发生坐标动态变化的目标用以与之前记录坐标比较
|
// 判断是否在寻路过程中需要重新执行寻路
|
DestPos = _validPos;
|
MoveTo(_validPos, keepDist);
|
}
|
}
|
|
public void MoveToPosition(Vector3 destPos, float keepDist = 0, bool force = false)
|
{
|
if (PathFindStatus == E_PathFindStatus.Moving
|
&& m_SearchType == E_SearchType.Static
|
&& MathUtility.DistanceSqrtXZ(DestPos, destPos) < 0.01f
|
&& !force)
|
{
|
Run();
|
return;
|
}
|
|
DestPos = destPos;
|
|
Vector3 _validPos = Pos;
|
if (TryGetValidPos(destPos, ref _validPos))
|
{
|
DestPos = _validPos;
|
m_SearchType = E_SearchType.Static;
|
MoveTo(_validPos, keepDist);
|
}
|
}
|
|
public void ClearPathFindTarget()
|
{
|
TargetActor = null;
|
DestPos = Vector3.zero;
|
}
|
|
public void StopPathFind()
|
{
|
PathFindStatus = E_PathFindStatus.Stop;
|
|
if (State != E_ActorState.AutoRun)
|
{
|
return;
|
}
|
|
// if (this is GActorNpcFight
|
// && (ServerInstID == 133 || ServerInstID == 134 || ServerInstID == 112))
|
// {
|
// Debug.LogFormat("{0} 停止寻路至点: ", ServerInstID);
|
// }
|
|
TargetActor = null;
|
DestPos = Vector3.zero;
|
m_CornerIndex = 0;
|
|
if (NextAction != GAStaticDefine.Act_Dead
|
&& NextAction != 200
|
&& !(this is GA_Player))
|
{
|
Idle();
|
}
|
|
// 如果停止的时候未被其他状态打断, 则切换状态为站立
|
if (State == E_ActorState.AutoRun)
|
{
|
State = E_ActorState.Idle;
|
|
GA_Hero _hero = this as GA_Hero;
|
if (_hero != null)
|
{
|
if (!UserInputHandler.isTouched
|
&& !_hero.IsRushing
|
&& !GA_Hero.s_MapSwitching)
|
{
|
_hero.PrevPos = Pos;
|
UserInputHandler.Send_CB409_tagCMPyMove(0);
|
}
|
}
|
}
|
|
if (OnPathFindStop != null)
|
{
|
OnPathFindStop();
|
}
|
|
// if (ServerInstID == PlayerDatas.Instance.PlayerId)
|
// {
|
// Debug.Log("停止了移动");
|
// }
|
}
|
|
private void MoveTo(Vector3 position, float keepDist = 0)
|
{
|
Vector3 _curPos = Pos;
|
_curPos.y = 0;
|
|
if (NavMesh.CalculatePath(_curPos, position, NavMesh.AllAreas, m_Path))
|
{
|
m_CornerIndex = 0;
|
m_KeepDist = keepDist;
|
PathFindStatus = E_PathFindStatus.Moving;
|
if (OnPathFindStart != null)
|
{
|
OnPathFindStart();
|
}
|
Run();
|
State = E_ActorState.AutoRun;
|
|
// if (this is GActorNpcFight
|
// && (ServerInstID == 133 || ServerInstID == 134 || ServerInstID == 112))
|
// {
|
// Debug.LogFormat("{0} 开始寻路至点: {1}", ServerInstID, position);
|
// }
|
}
|
}
|
|
public static bool TryGetValidPos(Vector3 position, ref Vector3 validPosition)
|
{
|
bool _result = false;
|
|
position.y = 0;
|
|
NavMeshHit _hit;
|
if (NavMesh.SamplePosition(position, out _hit, 5, NavMesh.AllAreas))
|
{
|
_result = true;
|
validPosition = _hit.position;
|
}
|
|
return _result;
|
}
|
|
private void UpdatePathFinding()
|
{
|
if (State == E_ActorState.AutoRun
|
&& PathFindStatus == E_PathFindStatus.Moving
|
&& m_Path.corners != null && m_Path.corners.Length > 0
|
&& !ActorInfo.serverDie
|
&& PlayerDatas.Instance.IsTickVaild())
|
{
|
if (m_CornerIndex > m_Path.corners.Length - 1)
|
{
|
StopPathFind();
|
return;
|
}
|
|
Vector3 _curCorner = Pos;
|
|
// 获得当前点, 一般的寻路都会产生2个点
|
// 第一个点为寻路起点, 所以这里如果是第一次取
|
// 则直接取第二个点
|
if (m_CornerIndex == 0 && m_Path.corners.Length > 1)
|
{
|
m_CornerIndex = 1;
|
}
|
|
if (m_Path.corners != null && m_CornerIndex < m_Path.corners.Length)
|
{
|
_curCorner = m_Path.corners[m_CornerIndex];
|
}
|
|
// 计算坐标差
|
Vector3 _curPos = Pos;
|
_curPos.y = 0;
|
_curCorner.y = 0;
|
|
_curCorner.y = Pos.y;
|
Vector3 _dir = MathUtility.ForwardXZ(_curCorner, Pos);
|
if (_dir != Vector3.zero)
|
{
|
Quaternion _quaternion = Quaternion.LookRotation(_dir, Vector3.up);
|
Rotation = Quaternion.RotateTowards(Rotation, _quaternion, GeneralDefine.RotateSpeed);
|
destForward = _dir;
|
}
|
float _deltaDis = ActorInfo.moveSpeed * Time.deltaTime;
|
if (_deltaDis > .5f)
|
{
|
_deltaDis = .5f;
|
}
|
|
Vector3 _detalMove = _deltaDis * _dir;
|
var _next = _curPos + _detalMove;
|
|
var _vec1 = _curCorner - _curPos;
|
var _vec3 = _curCorner - _next;
|
|
if (!MathUtility.IsSameDir(_vec1, _vec3) || _curPos == _next)
|
{
|
Pos += _detalMove;
|
if (m_CornerIndex < m_Path.corners.Length - 1)
|
{
|
m_CornerIndex += 1;
|
}
|
}
|
else
|
{
|
Pos += _detalMove;
|
}
|
|
if (OnPathFinding != null)
|
{
|
OnPathFinding();
|
}
|
|
float _chkDistSqrt = 0;
|
|
// 寻找动态目标的时候
|
// 判断目标是否坐标发生了需要重新寻路的变化
|
if (m_SearchType == E_SearchType.Dynamic)
|
{
|
_chkDistSqrt = MathUtility.DistanceSqrtXZ(TargetActor.Pos, DestPos);
|
if (_chkDistSqrt > 1)
|
{
|
MoveToTarget(TargetActor, m_KeepDist);
|
}
|
}
|
|
// 判断和终点是否已经达到保持值得
|
_chkDistSqrt = MathUtility.DistanceSqrtXZ(DestPos, Pos);
|
|
if ((_chkDistSqrt - m_KeepDist * m_KeepDist) <= .05f)
|
{
|
StopPathFind();
|
}
|
}
|
}
|
|
private enum E_SearchType
|
{
|
Dynamic,
|
Static,
|
}
|
|
public enum E_PathFindStatus
|
{
|
Moving,
|
Stop,
|
}
|
|
#endregion
|
|
#region 派生类需实现接口部分
|
|
protected abstract void OnInit(GameNetPackBasic package);
|
protected abstract void OnUnit();
|
protected abstract void OnUpdate();
|
protected abstract void OnLateUpdate();
|
protected abstract void OnFixedUpdate();
|
|
public abstract void RequestName();
|
public abstract void ReleaseName();
|
public abstract void RequestLifeBar();
|
public abstract void ReleaseLifeBar();
|
public abstract void RequestShadow();
|
public abstract void ReleaseShadow();
|
|
#endregion
|
|
#region 表现层动画行为的接口
|
|
public abstract void SetAnimatorSpeed(float speed);
|
public abstract void Play(int stateNameHash, float normalizeTime = 0);
|
public abstract void Idle();
|
public abstract void IdleImmediate();
|
public abstract void Run();
|
public abstract void RunImmediate();
|
public abstract void Die();
|
public abstract void Hurt();
|
public abstract void HurtDown();
|
public abstract bool IsIdle();
|
public abstract bool IsRun();
|
public abstract bool IsHurt();
|
public abstract bool IsStun();
|
public abstract bool HasState(int stateHash);
|
|
#endregion
|
|
protected List<SFXController> m_EffectList = new List<SFXController>();
|
|
public abstract void ShowOrHideModel(bool showOrHide);
|
public abstract void ShowEffect();
|
public virtual void HideEffect()
|
{
|
foreach (var _ctrl in m_EffectList)
|
{
|
SFXPlayUtility.Instance.Release(_ctrl);
|
}
|
m_EffectList.Clear();
|
}
|
|
protected Vector3 MP_Name_Pos;
|
|
protected void SetupBindNode(Transform parent)
|
{
|
// 绑定点确定
|
MP_Stun = parent.GetChildTransformDeeply("A_Stun");
|
if (!MP_Stun)
|
{
|
MP_Stun = parent;
|
}
|
|
MP_Name = parent.GetChildTransformDeeply("A_Name");
|
if (!MP_Name)
|
{
|
MP_Name = parent;
|
}
|
|
MP_Name1 = parent.GetChildTransformDeeply("A_Name2");
|
if (!MP_Name1)
|
{
|
MP_Name1 = parent;
|
}
|
|
MP_Hit = parent.GetChildTransformDeeply("A_Hit");
|
if (!MP_Hit)
|
{
|
MP_Hit = parent;
|
}
|
|
MP_Down = parent.GetChildTransformDeeply("A_Down");
|
if (!MP_Down)
|
{
|
MP_Down = parent;
|
}
|
|
if (!MP_FollowDown)
|
{
|
MP_FollowDown = new GameObject("Follow_Down").transform;
|
MP_FollowDown.SetParent(Root);
|
}
|
}
|
|
public static void ForceCrossFade(Animator animator, int name, float transitionDuration, int layer = 0, float normalizedTime = float.NegativeInfinity)
|
{
|
animator.Update(0);
|
if (animator.GetNextAnimatorStateInfo(layer).fullPathHash == 0)
|
{
|
animator.CrossFade(name, transitionDuration, layer, normalizedTime);
|
}
|
else
|
{
|
animator.Play(animator.GetNextAnimatorStateInfo(layer).fullPathHash, layer);
|
animator.Update(0);
|
animator.CrossFade(name, transitionDuration, layer, normalizedTime);
|
}
|
}
|
|
/// <summary>
|
/// 矫正服务端坐标至客户端坐标
|
/// </summary>
|
/// <param name="sPosX">服务端坐标X</param>
|
/// <param name="sPosY">服务端坐标Y</param>
|
public void AdjustPos(ushort sPosX, ushort sPosY, bool chkNavmesh = true)
|
{
|
sPosX -= (ushort)GA_Hero.MapOffset.x;
|
sPosY -= (ushort)GA_Hero.MapOffset.z;
|
|
Vector3 _clntPos = new Vector3(sPosX * .5f, 0, sPosY * .5f);
|
|
Vector3 _adjustPos = Vector3.zero;
|
|
if (!chkNavmesh)
|
{
|
_adjustPos = _clntPos;
|
}
|
else
|
{
|
if (!TryGetValidPos(_clntPos, ref _adjustPos))
|
{
|
Debug.LogWarningFormat("对象: {3} : 服务端给定坐标: {0} 映射至客户端坐标: {1} 为客户端无法矫正的障碍点...当前地图: {2}",
|
new Vector2(sPosX, sPosY), new Vector2(_clntPos.x, _clntPos.z), PlayerDatas.Instance.baseData.MapID, ServerInstID);
|
return;
|
}
|
}
|
|
float _groundHeight = Pos.y;
|
if (!CollisionUtility.TryGetGroundHeight(_adjustPos, out _groundHeight))
|
{
|
Debug.LogErrorFormat("对象: {3} : 服务端给定坐标: {0} 映射至客户端坐标: {1} 并且矫正后: {2}客户端无法从此点计算出地板高度...",
|
new Vector2(sPosX, sPosY), new Vector2(_clntPos.x, _clntPos.y), new Vector2(_adjustPos.x, _adjustPos.z), ServerInstID);
|
}
|
|
_adjustPos.y = _groundHeight;
|
Pos = _adjustPos;
|
}
|
|
#if UNITY_EDITOR
|
|
#region 测试用模块
|
private static int m_NewCount = 0;
|
~GActor()
|
{
|
m_NewCount -= 1;
|
}
|
#endregion
|
|
#endif
|
}
|