Files
JJBB/Assets/Project/Script/Obj/Other/NexusMover.cs
2024-08-23 15:49:34 +08:00

243 lines
8.6 KiB
C#

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<Vector3> _cornerList;
private readonly List<MoverData> _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<MoverData>(movers.Count);
for (var i = 0; i < movers.Count; i++)
_moverList.Add(new MoverData(movers[i]));
_cornerList = new List<Vector3>(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<Obj_Character_Mover>();
mover.Init(npcData);
}
return mover;
}
}
}