using System.Collections.Generic; using Games.LogicObj; using Games.Scene; using GCGame.Table; using Module.Log; using UnityEngine; using UnityEngine.AI; public class NexusMover { private const float _followTime = 0.1f; private readonly Obj_Character _carrier; private readonly List _cornerList; private readonly List _moverList; private readonly NavMeshPath _path; public NexusMover(int roleId, Obj_Character carrier) { _carrier = carrier; var movers = TableManager.GetClientPathMoverByID(roleId); var corners = TableManager.GetClientPathByID(roleId); if (movers == null) { LogModule.ErrorLog("Cannot find movers for roleId = " + roleId); } else if (corners == null || corners.Count < 2) { LogModule.ErrorLog("Cannot find path for roleId = " + roleId); } else { _path = new NavMeshPath(); _moverList = new List(movers.Count); for (var i = 0; i < movers.Count; i++) _moverList.Add(new MoverData(movers[i])); _cornerList = new List(corners.Count); for (var i = 0; i < corners.Count; i++) { var point = new Vector3(corners[i].PosX, 0f, corners[i].PosY) * 0.01f; point = ActiveScene.GetTerrainPosition(point); _cornerList.Add(point); } } } public bool CreateMover() { var result = _moverList != null; if (result) { var next = GetNextPoint(); for (var i = _moverList.Count - 1; i >= 0; i--) { var roleBase = TableManager.GetRoleBaseAttrByID(_moverList[i].data.RoleId); if (roleBase == null) { Debug.LogError("Cannot find role base by id " + _moverList[i].data.RoleId); _moverList.RemoveAt(i); } else { var position = GetWorldPos(_carrier.transform, next, _moverList[i].data); _moverList[i].CreateMover(roleBase, position); } } } return result; } public void DestroyMover() { for (var i = 0; i < _moverList.Count; i++) if (_moverList[i].mover != null) _moverList[i].mover.DestroyObj(); } public void SyncMovers() { var next = GetNextPoint(); // 获得每个物体的位置 for (var i = 0; i < _moverList.Count; i++) { var target = GetWorldPos(_carrier.transform, next, _moverList[i].data); float speed; if (NavMesh.CalculatePath(_moverList[i].mover.Position, target, NavMesh.AllAreas, _path)) { var distance = 0f; for (var j = 0; j < _path.corners.Length - 1; j++) distance += Vector3.Distance(_path.corners[j], _path.corners[j + 1]); speed = distance / _followTime; } else { speed = _carrier.MoveSpeed; } speed = Mathf.Clamp(speed, _moverList[i].data.MinSpeed * 0.01f, _moverList[i].data.MaxSpeed * 0.01f); _moverList[i].mover.BaseAttr.MoveSpeed = speed; _moverList[i].mover.MoveAsMainPlayer(target); } } private int FixIndex(int sourceIndex) { if (sourceIndex > _cornerList.Count - 1) sourceIndex -= _cornerList.Count; if (sourceIndex < 0) sourceIndex += _cornerList.Count; return sourceIndex; } public static Vector2 GetCornerPoint(Vector2 p0, Vector2 p1, Vector2 p2, float offset) { var forward0 = (p1 - p0).normalized; var right0 = new Vector2(forward0.y, -forward0.x); var forward1 = (p2 - p1).normalized; var right1 = new Vector2(forward1.y, -forward1.x); var off0 = p0 + right0 * offset; var off1 = p1 + right0 * offset; var off2 = p1 + right1 * offset; var off3 = p2 + right1 * offset; var y = ((off0.y - off1.y) * (off3.y - off2.y) * off0.x + (off3.y - off2.y) * (off1.x - off0.x) * off0.y + (off1.y - off0.y) * (off3.y - off2.y) * off2.x + (off2.x - off3.x) * (off1.y - off0.y) * off2.y) / ((off1.x - off0.x) * (off3.y - off2.y) + (off0.y - off1.y) * (off3.x - off2.x)); var x = off2.x + (off3.x - off2.x) * (y - off2.y) / (off3.y - off2.y); return new Vector2(x, y); } private int GetNextPoint() { var target = _carrier.ServerPos; var nextPoint = 0; var distance = (_cornerList[nextPoint] - target).sqrMagnitude; for (var i = 1; i < _cornerList.Count; i++) { var newDist = (_cornerList[i] - target).sqrMagnitude; if (newDist < distance) { nextPoint = i; distance = newDist; } } if ((_cornerList[nextPoint] - target).sqrMagnitude > 0.1f) LogModule.ErrorLog("Next Point has precision problem from " + target + " to " + _cornerList[nextPoint]); return nextPoint; } private Vector3 GetWorldPos(Transform root, int next, Tab_ClientPathMover data) { var distance = data.PosY * 0.01f; var current = root.position; var target = new Vector3(); while (distance > 0f) { var delta = (_cornerList[next] - current).magnitude; if (delta > distance) { // 试图解决转角平移导致误差的问题 // 计算平移后直线实际的开始和结束位置 var start = FixIndex(next - 1); var previous = FixIndex(start - 1); var nextEnd = FixIndex(next + 1); var offset = data.PosX * 0.01f; var curForward = (_cornerList[next].RemoveY() - _cornerList[start].RemoveY()).normalized; var curRight = new Vector2(curForward.y, -curForward.x); var curPoint = (current + distance * (_cornerList[next] - _cornerList[start]).normalized).RemoveY(); curPoint += curRight * offset; // 防止平行线导致NaN var preForward = _cornerList[start].RemoveY() - _cornerList[previous].RemoveY(); var nextForward = _cornerList[nextEnd].RemoveY() - _cornerList[next].RemoveY(); if (Vector2.Dot(curForward, preForward) < 0.9f) { var p0 = GetCornerPoint(_cornerList[previous].RemoveY(), _cornerList[start].RemoveY(), _cornerList[next].RemoveY(), offset); if (Vector2.Dot(curPoint - p0, curForward) < 0f) curPoint = p0; } if (Vector2.Dot(curForward, nextForward) < 0.9f) { var p1 = GetCornerPoint(_cornerList[start].RemoveY(), _cornerList[next].RemoveY(), _cornerList[nextEnd].RemoveY(), offset); if (Vector2.Dot(p1 - curPoint, curForward) < 0f) curPoint = p1; } target = ActiveScene.GetTerrainPosition(curPoint.InsertY()); break; } distance -= delta; current = _cornerList[next]; next = FixIndex(next + 1); } return target; } private class MoverData { public readonly Tab_ClientPathMover data; public Obj_Character_Mover mover; public MoverData(Tab_ClientPathMover data) { this.data = data; } public Obj_Character_Mover CreateMover(Tab_RoleBaseAttr roleBase, Vector3 position) { if (mover == null) { var npcData = new Obj_NPC_Init_Data { _InitCurHP = 1, _InitMaxHP = 1, m_RoleBaseID = data.RoleId, m_Force = 1, m_fX = position.x, m_fZ = position.z, m_MoveSpeed = roleBase.MoveSpeed * 0.01f }; var npcObj = ResourceManager.InstantiateResource("Prefab/Model/NPCRoot"); npcObj.name = "Mover_" + roleBase.Id; mover = npcObj.EnsureComponent(); mover.Init(npcData); } return mover; } } }