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; } } /// /// 如果有,杀死这个npc的角色服务器实例ID /// 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); } } } } /// /// 目标朝向 /// 主要与当前比较, 做转身的缓动处理的一个方向 /// 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); //是主角冲锋 GA_Hero _hero = this as GA_Hero; if (_hero != null && MapTransferUtility.tagetNPCforRush != 0) { // 判断是否需要冲锋 if (_hero.IsNeedRush(_chkDistSqrt)) { _hero.StartRush(); } } 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 m_EffectList = new List(); 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); } } /// /// 矫正服务端坐标至客户端坐标 /// /// 服务端坐标X /// 服务端坐标Y 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 }