243 lines
8.6 KiB
C#
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;
|
|
}
|
|
}
|
|
} |