Files
aibis-dream/Assets/Scripts/MiniGame/Gear/ShaftGraph.cs
T
2025-01-24 23:21:54 +08:00

302 lines
9.1 KiB
C#

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();
}
/// <summary>
/// 更新轴
/// </summary>
/// <param name="panelShafts"></param>
public void UpdatePanel(Shaft[] panelShafts)
{
_panelShafts = panelShafts;
Array.ForEach(panelShafts, shaft => shaft.InitShaft());
}
/// <summary>
/// 是否所有齿轮都在运行
/// </summary>
/// <returns></returns>
public bool AllRunning()
{
return _vertexArray.All(shaft => Mathf.Abs(shaft.speed) > 0.01f);
}
/// <summary>
/// 初始化速度
/// </summary>
/// <param name="initSpeed">初始速度</param>
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;
}
/// <summary>
/// 生成边邻接表
/// </summary>
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;
}
}
}
}
/// <summary>
/// 某个轴上齿轮发生变化时更新临接关系
/// </summary>
/// <param name="instanceID">轴ID</param>
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;
}
/// <summary>
/// 更新当前所有轴的速度
/// </summary>
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;
}
}
}
/// <summary>
/// 查询可吸附的轴
/// </summary>
/// <param name="pos">齿轮位置</param>
/// <param name="gearRadius">齿轮半径</param>
/// <returns>吸附轴</returns>
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;
}
/// <summary>
/// 判断齿轮能不能放进轴内
/// </summary>
/// <param name="sourceShaft">当前轴</param>
/// <param name="gearRadius">齿轮半径</param>
/// <returns>是否可用</returns>
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.05f)
{
isShaftAvailable = false;
break;
}
}
return isShaftAvailable;
}
}
}