using System; using System.Linq; using UnityEngine; namespace AibisDream { public class ShaftGraph { // 顶点数量 private readonly int _vertexSize; // 顶点Map private readonly Shaft[] _vertexArray; private Shaft[] _panelShafts; // 邻接表 private float[,] _edgeTable; // 遍历记录 private readonly bool[] _vertexRecordArray; // 动力轴序号 private readonly int _rootIdx; public ShaftGraph(Shaft[] shafts, Shaft[] panelShafts) { // 为轴赋值 _vertexSize = shafts.Length; _vertexRecordArray = new bool[_vertexSize]; _vertexArray = shafts; _panelShafts = panelShafts; foreach (var shaft in _vertexArray) { shaft.InitShaft(); } foreach (var shaft in _panelShafts) { shaft.InitShaft(); } // 寻找唯一的驱动轴序号 _rootIdx = FindDriverIdx(); // 生成边邻接表 GeneEdgeTable(); UpdateSpeed(); } /// /// 更新轴 /// /// public void UpdatePanel(Shaft[] panelShafts) { _panelShafts = panelShafts; Array.ForEach(panelShafts, shaft => shaft.InitShaft()); } /// /// 是否所有齿轮都在运行 /// /// public bool AllRunning() { return _vertexArray.All(shaft => Mathf.Abs(shaft.speed) > 0.01f); } /// /// 是否所有齿轮都是新的 /// /// public bool AllNewGear() { return _vertexArray.Where(shaft => shaft.shaftType != ShaftType.Driver).All(shaft => shaft.childGear != null && shaft.childGear.gearProp == "NewGear"); } /// /// 初始化速度 /// /// 初始速度 public void StartRotate(float initSpeed) { _vertexArray[_rootIdx].speed = initSpeed; UpdateSpeed(); } public void Accelerate(float originSpeed, float speed) { foreach (var shaft in _vertexArray) { if (shaft.HasChildGear()) shaft.UpdateSpeedScale(speed); } } private int FindDriverIdx() { for (int i = 0; i < _vertexSize; i++) { if (_vertexArray[i].shaftType == ShaftType.Driver) { return i; } } return -1; } /// /// 生成边邻接表 /// private void GeneEdgeTable() { _edgeTable = new float[_vertexSize, _vertexSize]; for (int i = 0; i < _vertexSize - 1; i++) { for (int j = i + 1; j < _vertexSize; j++) { // 判断两个Shaft是否临接 if (Shaft.IsShaftConnected(_vertexArray[i], _vertexArray[j])) { _edgeTable[i, j] = Shaft.CalcAngularVelocityRatio(_vertexArray[i], _vertexArray[j]); _edgeTable[j, i] = Shaft.CalcAngularVelocityRatio(_vertexArray[j], _vertexArray[i]); } else { _edgeTable[i, j] = 0; _edgeTable[j, i] = 0; } } } } /// /// 某个轴上齿轮发生变化时更新临接关系 /// /// 轴ID public void ChangeVertex(int instanceID) { // 找到对应idx int idx = TranInstanceIDToIdx(instanceID); if (idx == -1) return; // 更新该节点对应的邻接表 for (int i = 0; i < _vertexSize; i++) { // 遇到自身跳过 if (i == idx) { continue; } // 判断两个Shaft是否临接 if (Shaft.IsShaftConnected(_vertexArray[idx], _vertexArray[i])) { _edgeTable[idx, i] = Shaft.CalcAngularVelocityRatio(_vertexArray[idx], _vertexArray[i]); _edgeTable[i, idx] = Shaft.CalcAngularVelocityRatio(_vertexArray[i], _vertexArray[idx]); } else { _edgeTable[idx, i] = 0; _edgeTable[i, idx] = 0; } } UpdateSpeed(); } private int TranInstanceIDToIdx(int instanceID) { // 查找对应的序号 int idx = -1; for (int i = 0; i < _vertexSize; i++) { if (instanceID == _vertexArray[i].GetInstanceID()) { idx = i; break; } } return idx; } /// /// 更新当前所有轴的速度 /// private void UpdateSpeed() { // 清除遍历记录 Array.Clear(_vertexRecordArray, 0, _vertexSize); // 从驱动轴开始计算速度 _vertexRecordArray[_rootIdx] = true; UpdateShaftSpeedByIdx(_rootIdx); UpdateUnreachableShafts(); // 更新完速度后统一更新动画 foreach (var shaft in _vertexArray) { shaft.ChangeRotateState(); } } private void UpdateShaftSpeedByIdx(int idx) { // 已知当前齿轮速度 float currentSpeed = _vertexArray[idx].speed; for (int i = 0; i < _vertexSize; i++) { // 遍历临接齿轮 if (i != idx && _edgeTable[idx, i] > 0) { // 已遍历过就跳过 if (_vertexRecordArray[i]) { continue; } // 更新速度 _vertexArray[i].speed = -currentSpeed * _edgeTable[idx, i]; // 已遍历的轴加上标记 _vertexRecordArray[i] = true; // 如果当前齿轮未破碎,继续更新相邻齿轮 if (!_vertexArray[i].IsBroken) { UpdateShaftSpeedByIdx(i); } } } } private void UpdateUnreachableShafts() { // 从Driver开始相连的齿轮全部更新后,剩下的齿轮置空 for (int i = 0; i < _vertexSize; i++) { if (!_vertexRecordArray[i]) { _vertexArray[i].speed = 0; } } } /// /// 查询可吸附的轴 /// /// 齿轮位置 /// 齿轮半径 /// 吸附轴 public Shaft QuerySnapShaft(Vector3 pos, float gearRadius) { if (_vertexArray == null || _vertexArray.Length == 0) { return null; } // 查询距离最近的,可吸附的Shaft Shaft closestShaft = null; float minDistance = -1; var totalArray = _vertexArray.Concat(_panelShafts); foreach (var vertex in totalArray) { if (!vertex.HasChildGear()) { float tempDis = Vector3.Distance(vertex.transform.position, pos); if (minDistance < 0 || tempDis < minDistance) { closestShaft = vertex; minDistance = tempDis; } } } // 没查到就返回空 if (!closestShaft) { return null; } // 查到了就返回是否吸附 return minDistance < gearRadius ? closestShaft : null; } /// /// 判断齿轮能不能放进轴内 /// /// 当前轴 /// 齿轮半径 /// 是否可用 public bool CheckGearSuitable(Shaft sourceShaft, float gearRadius) { // 获取目标idx int idx = TranInstanceIDToIdx(sourceShaft.GetInstanceID()); bool isShaftAvailable = true; // 寻找所有临接轴 for (int i = 0; i < _vertexSize; i++) { if (i == idx) continue; // 不需要计算和自身的关系 float availableDis = Shaft.CalcShaftAvailableDis(sourceShaft.transform.position, _vertexArray[i]); if (availableDis < gearRadius - 0.2f) { isShaftAvailable = false; break; } } return isShaftAvailable; } } }