提交测试代码
This commit is contained in:
@@ -598,15 +598,10 @@ MonoBehaviour:
|
||||
solver: {fileID: 603012225}
|
||||
initialFirstSegmentLength: 1.2
|
||||
minFirstSegmentLength: 0.1
|
||||
maxFirstSegmentLength: 5
|
||||
maxFirstSegmentLength: 50
|
||||
lineAdjustSpeed: 1
|
||||
extendKey: 273
|
||||
retractKey: 274
|
||||
rebuildKey: 114
|
||||
showGui: 1
|
||||
showNodeDetails: 1
|
||||
showSegmentDetails: 1
|
||||
detailScroll: {x: 0, y: 0}
|
||||
--- !u!1 &832575517
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -960,7 +955,7 @@ GameObject:
|
||||
- component: {fileID: 1939106084}
|
||||
- component: {fileID: 1939106083}
|
||||
- component: {fileID: 1939106082}
|
||||
m_Layer: 0
|
||||
m_Layer: 6
|
||||
m_Name: Terrain
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
|
||||
@@ -17,12 +17,28 @@ namespace F2RopeLine2.FishingLine
|
||||
[SerializeField] private float damping = 0.98f;
|
||||
[Min(0f)]
|
||||
[SerializeField] private float gravityScale = 1f;
|
||||
[Min(0.001f)]
|
||||
[SerializeField] private float simulationStep = 0.0166667f;
|
||||
[Min(0.001f)]
|
||||
[SerializeField] private float maxDeltaTime = 0.0333333f;
|
||||
|
||||
[Header("Pin Follow")]
|
||||
[Min(0f)]
|
||||
[SerializeField] private float pinFollowStrength = 50f;
|
||||
[SerializeField] private bool writeBackVirtualNodeTransforms = true;
|
||||
|
||||
[Header("Stability")]
|
||||
[Min(1)]
|
||||
[SerializeField] private int maxSubStepsPerFrame = 2;
|
||||
[Min(0f)]
|
||||
[SerializeField] private float sleepVelocityThreshold = 0.001f;
|
||||
[Min(0f)]
|
||||
[SerializeField] private float sleepDistanceThreshold = 0.002f;
|
||||
[Min(1)]
|
||||
[SerializeField] private int stableFramesBeforeSleep = 4;
|
||||
[Min(0f)]
|
||||
[SerializeField] private float wakeDistanceThreshold = 0.001f;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool drawDebugSamples = false;
|
||||
[SerializeField] private Color debugSampleColor = new(1f, 0.2f, 0.2f, 1f);
|
||||
@@ -31,7 +47,13 @@ namespace F2RopeLine2.FishingLine
|
||||
|
||||
private readonly List<Vector3> positions = new();
|
||||
private readonly List<Vector3> previousPositions = new();
|
||||
private readonly List<FishingLineNode> sampledNodes = new();
|
||||
private readonly List<Vector3> lastPinnedNodePositions = new();
|
||||
private readonly List<float> lastRestLengths = new();
|
||||
private bool[] pinnedFlags = System.Array.Empty<bool>();
|
||||
private float accumulatedTime;
|
||||
private bool isSleeping;
|
||||
private int stableFrameCounter;
|
||||
|
||||
public int SampleCount => positions.Count;
|
||||
|
||||
@@ -83,27 +105,59 @@ namespace F2RopeLine2.FishingLine
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureBuffers(nodes, pinnedIndices);
|
||||
var topologyChanged = EnsureBuffers(nodes, pinnedIndices);
|
||||
if (topologyChanged)
|
||||
{
|
||||
WakeUp();
|
||||
}
|
||||
|
||||
if (ShouldWake(nodes, restLengths))
|
||||
{
|
||||
WakeUp();
|
||||
}
|
||||
|
||||
Simulate(nodes, restLengths, deltaTime);
|
||||
ApplyToRenderer(nodes);
|
||||
CacheFrameState(nodes, restLengths);
|
||||
}
|
||||
|
||||
private void EnsureBuffers(
|
||||
private bool EnsureBuffers(
|
||||
IReadOnlyList<FishingLineNode> nodes,
|
||||
IReadOnlyList<int> pinnedIndices)
|
||||
{
|
||||
if (positions.Count == nodes.Count)
|
||||
var topologyChanged = positions.Count != nodes.Count;
|
||||
var previousPositionMap = new Dictionary<FishingLineNode, Vector3>(sampledNodes.Count);
|
||||
var previousHistoryMap = new Dictionary<FishingLineNode, Vector3>(sampledNodes.Count);
|
||||
for (var i = 0; i < sampledNodes.Count; i++)
|
||||
{
|
||||
return;
|
||||
var sampledNode = sampledNodes[i];
|
||||
if (sampledNode == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
previousPositionMap[sampledNode] = positions[i];
|
||||
previousHistoryMap[sampledNode] = previousPositions[i];
|
||||
}
|
||||
|
||||
positions.Clear();
|
||||
previousPositions.Clear();
|
||||
sampledNodes.Clear();
|
||||
pinnedFlags = new bool[nodes.Count];
|
||||
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var position = nodes[i].Position;
|
||||
var node = nodes[i];
|
||||
sampledNodes.Add(node);
|
||||
|
||||
if (node != null && previousPositionMap.TryGetValue(node, out var preservedPosition))
|
||||
{
|
||||
positions.Add(preservedPosition);
|
||||
previousPositions.Add(previousHistoryMap[node]);
|
||||
continue;
|
||||
}
|
||||
|
||||
var position = node != null ? node.Position : Vector3.zero;
|
||||
positions.Add(position);
|
||||
previousPositions.Add(position);
|
||||
}
|
||||
@@ -116,12 +170,41 @@ namespace F2RopeLine2.FishingLine
|
||||
pinnedFlags[pinnedIndex] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return topologyChanged;
|
||||
}
|
||||
|
||||
private void Simulate(IReadOnlyList<FishingLineNode> nodes, IReadOnlyList<float> restLengths, float deltaTime)
|
||||
{
|
||||
var simulationDelta = Mathf.Max(0.0001f, deltaTime);
|
||||
var gravity = Physics.gravity * gravityScale * simulationDelta * simulationDelta;
|
||||
if (isSleeping)
|
||||
{
|
||||
PinLogicalNodes(nodes, simulationStep);
|
||||
return;
|
||||
}
|
||||
|
||||
var clampedDelta = Mathf.Clamp(deltaTime, 0f, maxDeltaTime);
|
||||
accumulatedTime = Mathf.Min(accumulatedTime + clampedDelta, simulationStep * maxSubStepsPerFrame);
|
||||
|
||||
var subStepCount = 0;
|
||||
while (accumulatedTime >= simulationStep && subStepCount < maxSubStepsPerFrame)
|
||||
{
|
||||
SimulateStep(nodes, restLengths, simulationStep);
|
||||
accumulatedTime -= simulationStep;
|
||||
subStepCount++;
|
||||
}
|
||||
|
||||
if (subStepCount == 0)
|
||||
{
|
||||
PinLogicalNodes(nodes, simulationStep);
|
||||
ApplySleep(nodes);
|
||||
}
|
||||
|
||||
EvaluateSleepState(nodes, restLengths);
|
||||
}
|
||||
|
||||
private void SimulateStep(IReadOnlyList<FishingLineNode> nodes, IReadOnlyList<float> restLengths, float stepDelta)
|
||||
{
|
||||
var gravity = Physics.gravity * gravityScale * stepDelta * stepDelta;
|
||||
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
@@ -141,7 +224,7 @@ namespace F2RopeLine2.FishingLine
|
||||
|
||||
for (var iteration = 0; iteration < solverIterations; iteration++)
|
||||
{
|
||||
PinLogicalNodes(nodes, simulationDelta);
|
||||
PinLogicalNodes(nodes, stepDelta);
|
||||
|
||||
for (var segmentIndex = 0; segmentIndex < restLengths.Count; segmentIndex++)
|
||||
{
|
||||
@@ -149,7 +232,8 @@ namespace F2RopeLine2.FishingLine
|
||||
}
|
||||
}
|
||||
|
||||
PinLogicalNodes(nodes, simulationDelta);
|
||||
PinLogicalNodes(nodes, stepDelta);
|
||||
ApplySleep(nodes);
|
||||
}
|
||||
|
||||
private void PinLogicalNodes(IReadOnlyList<FishingLineNode> nodes, float deltaTime)
|
||||
@@ -211,6 +295,136 @@ namespace F2RopeLine2.FishingLine
|
||||
positions[segmentIndex + 1] -= correction;
|
||||
}
|
||||
|
||||
private void ApplySleep(IReadOnlyList<FishingLineNode> nodes)
|
||||
{
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (pinnedFlags[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var current = positions[i];
|
||||
var previous = previousPositions[i];
|
||||
var velocityMagnitude = (current - previous).magnitude;
|
||||
var authoredDistance = Vector3.Distance(current, nodes[i].Position);
|
||||
|
||||
if (velocityMagnitude <= sleepVelocityThreshold && authoredDistance <= sleepDistanceThreshold)
|
||||
{
|
||||
previousPositions[i] = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateSleepState(IReadOnlyList<FishingLineNode> nodes, IReadOnlyList<float> restLengths)
|
||||
{
|
||||
var isStable = true;
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (pinnedFlags[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var velocityMagnitude = (positions[i] - previousPositions[i]).magnitude;
|
||||
if (velocityMagnitude > sleepVelocityThreshold)
|
||||
{
|
||||
isStable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isStable)
|
||||
{
|
||||
for (var i = 0; i < restLengths.Count; i++)
|
||||
{
|
||||
var error = Mathf.Abs(Vector3.Distance(positions[i], positions[i + 1]) - restLengths[i]);
|
||||
if (error > sleepDistanceThreshold)
|
||||
{
|
||||
isStable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isStable)
|
||||
{
|
||||
stableFrameCounter = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
stableFrameCounter++;
|
||||
if (stableFrameCounter < stableFramesBeforeSleep)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isSleeping = true;
|
||||
accumulatedTime = 0f;
|
||||
for (var i = 0; i < positions.Count; i++)
|
||||
{
|
||||
previousPositions[i] = positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldWake(IReadOnlyList<FishingLineNode> nodes, IReadOnlyList<float> restLengths)
|
||||
{
|
||||
if (!isSleeping)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastPinnedNodePositions.Count != nodes.Count || lastRestLengths.Count != restLengths.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (!pinnedFlags[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Vector3.Distance(nodes[i].Position, lastPinnedNodePositions[i]) > wakeDistanceThreshold)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < restLengths.Count; i++)
|
||||
{
|
||||
if (Mathf.Abs(restLengths[i] - lastRestLengths[i]) > wakeDistanceThreshold)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CacheFrameState(IReadOnlyList<FishingLineNode> nodes, IReadOnlyList<float> restLengths)
|
||||
{
|
||||
lastPinnedNodePositions.Clear();
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
lastPinnedNodePositions.Add(nodes[i] != null ? nodes[i].Position : Vector3.zero);
|
||||
}
|
||||
|
||||
lastRestLengths.Clear();
|
||||
for (var i = 0; i < restLengths.Count; i++)
|
||||
{
|
||||
lastRestLengths.Add(restLengths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void WakeUp()
|
||||
{
|
||||
isSleeping = false;
|
||||
stableFrameCounter = 0;
|
||||
accumulatedTime = 0f;
|
||||
}
|
||||
|
||||
private void ApplyToRenderer(IReadOnlyList<FishingLineNode> nodes)
|
||||
{
|
||||
lineRenderer.positionCount = positions.Count;
|
||||
|
||||
@@ -46,7 +46,8 @@ namespace F2RopeLine2.FishingLine
|
||||
private readonly List<FishingLineNode> orderedNodes = new();
|
||||
private readonly List<float> restLengths = new();
|
||||
private readonly List<int> pinnedIndices = new();
|
||||
private readonly List<FishingLineNode> runtimeVirtualNodes = new();
|
||||
private readonly List<FishingLineNode> firstSegmentVirtualPool = new();
|
||||
private readonly List<FishingLineNode> otherSegmentVirtualPool = new();
|
||||
private readonly List<ConfigurableJoint> runtimeJoints = new();
|
||||
|
||||
private Transform virtualNodeRoot;
|
||||
@@ -68,16 +69,24 @@ namespace F2RopeLine2.FishingLine
|
||||
|
||||
public int LogicalNodeCount => logicalNodes?.Length ?? 0;
|
||||
|
||||
public int RuntimeVirtualNodeCount => runtimeVirtualNodes.Count;
|
||||
public int RuntimeVirtualNodeCount => firstSegmentVirtualPool.Count + otherSegmentVirtualPool.Count;
|
||||
|
||||
public int ActiveRuntimeVirtualNodeCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var activeCount = 0;
|
||||
for (var i = 0; i < runtimeVirtualNodes.Count; i++)
|
||||
for (var i = 0; i < firstSegmentVirtualPool.Count; i++)
|
||||
{
|
||||
if (runtimeVirtualNodes[i] != null && runtimeVirtualNodes[i].gameObject.activeSelf)
|
||||
if (firstSegmentVirtualPool[i] != null && firstSegmentVirtualPool[i].gameObject.activeSelf)
|
||||
{
|
||||
activeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < otherSegmentVirtualPool.Count; i++)
|
||||
{
|
||||
if (otherSegmentVirtualPool[i] != null && otherSegmentVirtualPool[i].gameObject.activeSelf)
|
||||
{
|
||||
activeCount++;
|
||||
}
|
||||
@@ -350,29 +359,39 @@ namespace F2RopeLine2.FishingLine
|
||||
return;
|
||||
}
|
||||
|
||||
var expectedVirtualCount = 0;
|
||||
var segmentLayouts = new SegmentLayout[logicalNodes.Length - 1];
|
||||
var firstSegmentVirtualCount = 0;
|
||||
var otherSegmentVirtualCount = 0;
|
||||
for (var segmentIndex = 0; segmentIndex < segmentLayouts.Length; segmentIndex++)
|
||||
{
|
||||
segmentLayouts[segmentIndex] = BuildSegmentLayout(segmentIndex);
|
||||
expectedVirtualCount += segmentLayouts[segmentIndex].VirtualNodeCount;
|
||||
if (segmentIndex == 0)
|
||||
{
|
||||
firstSegmentVirtualCount = segmentLayouts[segmentIndex].VirtualNodeCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
otherSegmentVirtualCount += segmentLayouts[segmentIndex].VirtualNodeCount;
|
||||
}
|
||||
}
|
||||
|
||||
ResizeRuntimeVirtualNodePool(expectedVirtualCount);
|
||||
EnsureFirstSegmentVirtualPool(firstSegmentVirtualCount);
|
||||
EnsureOtherSegmentVirtualPool(otherSegmentVirtualCount);
|
||||
|
||||
orderedNodes.Add(logicalNodes[0]);
|
||||
pinnedIndices.Add(0);
|
||||
|
||||
var virtualNodeCursor = 0;
|
||||
var otherVirtualCursor = 0;
|
||||
for (var segmentIndex = 0; segmentIndex < segmentLayouts.Length; segmentIndex++)
|
||||
{
|
||||
var fromNode = logicalNodes[segmentIndex];
|
||||
var toNode = logicalNodes[segmentIndex + 1];
|
||||
var layout = segmentLayouts[segmentIndex];
|
||||
var segmentVirtualNodes = GetSegmentVirtualNodes(segmentIndex, layout.VirtualNodeCount, ref otherVirtualCursor);
|
||||
|
||||
for (var virtualIndex = 0; virtualIndex < layout.VirtualNodeCount; virtualIndex++)
|
||||
for (var virtualIndex = 0; virtualIndex < segmentVirtualNodes.Count; virtualIndex++)
|
||||
{
|
||||
var virtualNode = runtimeVirtualNodes[virtualNodeCursor++];
|
||||
var virtualNode = segmentVirtualNodes[virtualIndex];
|
||||
virtualNode.SetRuntimeVirtual(true, orderedNodes.Count);
|
||||
virtualNode.name = $"VirtualNode_{segmentIndex}_{virtualIndex}";
|
||||
virtualNode.transform.SetParent(virtualNodeRoot, false);
|
||||
@@ -389,10 +408,11 @@ namespace F2RopeLine2.FishingLine
|
||||
orderedNodes.Add(toNode);
|
||||
pinnedIndices.Add(orderedNodes.Count - 1);
|
||||
|
||||
PlaceVirtualNodesBetween(fromNode, toNode, layout, segmentIndex);
|
||||
PlaceVirtualNodesBetween(fromNode, toNode, layout, segmentVirtualNodes);
|
||||
}
|
||||
|
||||
DisableUnusedVirtualNodes(expectedVirtualCount);
|
||||
DisableUnusedFirstSegmentVirtualNodes(firstSegmentVirtualCount);
|
||||
DisableUnusedOtherSegmentVirtualNodes(otherSegmentVirtualCount);
|
||||
UpdateJointLimitsFromConfig();
|
||||
chainDirty = false;
|
||||
}
|
||||
@@ -464,7 +484,7 @@ namespace F2RopeLine2.FishingLine
|
||||
FishingLineNode fromNode,
|
||||
FishingLineNode toNode,
|
||||
SegmentLayout layout,
|
||||
int segmentIndex)
|
||||
IReadOnlyList<FishingLineNode> segmentVirtualNodes)
|
||||
{
|
||||
if (layout.VirtualNodeCount == 0)
|
||||
{
|
||||
@@ -476,55 +496,100 @@ namespace F2RopeLine2.FishingLine
|
||||
var normalizedDirection = distance > 0.0001f ? direction / distance : Vector3.down;
|
||||
|
||||
var accumulatedDistance = 0f;
|
||||
var runtimeBaseIndex = GetVirtualBaseIndexForSegment(segmentIndex);
|
||||
for (var i = 0; i < layout.VirtualNodeCount; i++)
|
||||
{
|
||||
accumulatedDistance += layout.GapLengths[i];
|
||||
var position = fromNode.Position + normalizedDirection * accumulatedDistance;
|
||||
var virtualNode = runtimeVirtualNodes[runtimeBaseIndex + i];
|
||||
virtualNode.SetVisualPosition(position);
|
||||
var virtualNode = segmentVirtualNodes[i];
|
||||
var wasActive = virtualNode.gameObject.activeSelf;
|
||||
if (!Application.isPlaying || !wasActive)
|
||||
{
|
||||
virtualNode.SetVisualPosition(position);
|
||||
}
|
||||
|
||||
virtualNode.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetVirtualBaseIndexForSegment(int segmentIndex)
|
||||
private List<FishingLineNode> GetSegmentVirtualNodes(int segmentIndex, int virtualCount, ref int otherVirtualCursor)
|
||||
{
|
||||
var index = 0;
|
||||
for (var i = 0; i < segmentIndex; i++)
|
||||
var result = new List<FishingLineNode>(virtualCount);
|
||||
if (virtualCount <= 0)
|
||||
{
|
||||
index += BuildSegmentLayout(i).VirtualNodeCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
return index;
|
||||
if (segmentIndex == 0)
|
||||
{
|
||||
for (var i = 0; i < virtualCount; i++)
|
||||
{
|
||||
result.Add(firstSegmentVirtualPool[virtualCount - 1 - i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (var i = 0; i < virtualCount; i++)
|
||||
{
|
||||
result.Add(otherSegmentVirtualPool[otherVirtualCursor + i]);
|
||||
}
|
||||
|
||||
otherVirtualCursor += virtualCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ResizeRuntimeVirtualNodePool(int targetCount)
|
||||
private void EnsureFirstSegmentVirtualPool(int targetCount)
|
||||
{
|
||||
EnsureVirtualNodeRoot();
|
||||
|
||||
while (runtimeVirtualNodes.Count < targetCount)
|
||||
while (firstSegmentVirtualPool.Count < targetCount)
|
||||
{
|
||||
var node = CreateRuntimeVirtualNode(runtimeVirtualNodes.Count);
|
||||
runtimeVirtualNodes.Add(node);
|
||||
var node = CreateRuntimeVirtualNode($"FirstSegmentVirtualNode_{firstSegmentVirtualPool.Count}");
|
||||
firstSegmentVirtualPool.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
private FishingLineNode CreateRuntimeVirtualNode(int index)
|
||||
private void EnsureOtherSegmentVirtualPool(int targetCount)
|
||||
{
|
||||
var runtimeObject = new GameObject($"VirtualNode_{index}");
|
||||
EnsureVirtualNodeRoot();
|
||||
|
||||
while (otherSegmentVirtualPool.Count < targetCount)
|
||||
{
|
||||
var node = CreateRuntimeVirtualNode($"OtherSegmentVirtualNode_{otherSegmentVirtualPool.Count}");
|
||||
otherSegmentVirtualPool.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
private FishingLineNode CreateRuntimeVirtualNode(string nodeName)
|
||||
{
|
||||
var runtimeObject = new GameObject(nodeName);
|
||||
runtimeObject.transform.SetParent(virtualNodeRoot, false);
|
||||
|
||||
var node = runtimeObject.AddComponent<FishingLineNode>();
|
||||
node.SetRuntimeVirtual(true, index);
|
||||
node.SetRuntimeVirtual(true, -1);
|
||||
runtimeObject.hideFlags = HideFlags.DontSave;
|
||||
runtimeObject.SetActive(false);
|
||||
return node;
|
||||
}
|
||||
|
||||
private void DisableUnusedVirtualNodes(int usedCount)
|
||||
private void DisableUnusedFirstSegmentVirtualNodes(int usedCount)
|
||||
{
|
||||
for (var i = 0; i < runtimeVirtualNodes.Count; i++)
|
||||
for (var i = 0; i < firstSegmentVirtualPool.Count; i++)
|
||||
{
|
||||
var node = runtimeVirtualNodes[i];
|
||||
var node = firstSegmentVirtualPool[i];
|
||||
var active = i < usedCount;
|
||||
if (node != null)
|
||||
{
|
||||
node.gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableUnusedOtherSegmentVirtualNodes(int usedCount)
|
||||
{
|
||||
for (var i = 0; i < otherSegmentVirtualPool.Count; i++)
|
||||
{
|
||||
var node = otherSegmentVirtualPool[i];
|
||||
var active = i < usedCount;
|
||||
if (node != null)
|
||||
{
|
||||
@@ -535,17 +600,24 @@ namespace F2RopeLine2.FishingLine
|
||||
|
||||
private void DestroyAllRuntimeVirtualNodes()
|
||||
{
|
||||
for (var i = runtimeVirtualNodes.Count - 1; i >= 0; i--)
|
||||
for (var i = firstSegmentVirtualPool.Count - 1; i >= 0; i--)
|
||||
{
|
||||
DestroyNodeObject(runtimeVirtualNodes[i]);
|
||||
DestroyNodeObject(firstSegmentVirtualPool[i]);
|
||||
}
|
||||
|
||||
runtimeVirtualNodes.Clear();
|
||||
for (var i = otherSegmentVirtualPool.Count - 1; i >= 0; i--)
|
||||
{
|
||||
DestroyNodeObject(otherSegmentVirtualPool[i]);
|
||||
}
|
||||
|
||||
firstSegmentVirtualPool.Clear();
|
||||
otherSegmentVirtualPool.Clear();
|
||||
}
|
||||
|
||||
private void CleanupDeadReferences()
|
||||
{
|
||||
runtimeVirtualNodes.RemoveAll(node => node == null);
|
||||
firstSegmentVirtualPool.RemoveAll(node => node == null);
|
||||
otherSegmentVirtualPool.RemoveAll(node => node == null);
|
||||
runtimeJoints.RemoveAll(joint => joint == null);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,6 @@ namespace F2RopeLine2.FishingLine
|
||||
[Header("Input")]
|
||||
[SerializeField] private KeyCode extendKey = KeyCode.UpArrow;
|
||||
[SerializeField] private KeyCode retractKey = KeyCode.DownArrow;
|
||||
[SerializeField] private KeyCode rebuildKey = KeyCode.R;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showGui = true;
|
||||
[SerializeField] private bool showNodeDetails = true;
|
||||
[SerializeField] private bool showSegmentDetails = true;
|
||||
[SerializeField] private Vector2 detailScroll = Vector2.zero;
|
||||
|
||||
private float targetFirstSegmentLength;
|
||||
|
||||
@@ -75,96 +68,6 @@ namespace F2RopeLine2.FishingLine
|
||||
solver.SetFirstSegmentLength(targetFirstSegmentLength);
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(rebuildKey))
|
||||
{
|
||||
solver.RebuildNow();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!showGui || solver == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.BeginArea(new Rect(16f, 16f, 280f, 180f), GUI.skin.box);
|
||||
GUILayout.Label("Fishing Line Test");
|
||||
GUILayout.Label($"First Segment Length: {solver.FirstSegmentLength:F2} m");
|
||||
GUILayout.Label($"Total Line Length: {solver.TotalLineLength:F2} m");
|
||||
GUILayout.Label($"Chain: {(solver.IsChainDirty ? "Dirty" : "Ready")}");
|
||||
GUILayout.Label($"Logical/Virtual/Ordered: {solver.LogicalNodeCount}/{solver.ActiveRuntimeVirtualNodeCount}/{solver.OrderedNodeCount}");
|
||||
GUILayout.Label($"Keys: {extendKey} / {retractKey} / {rebuildKey}");
|
||||
|
||||
var nextLength = GUILayout.HorizontalSlider(
|
||||
targetFirstSegmentLength,
|
||||
minFirstSegmentLength,
|
||||
maxFirstSegmentLength);
|
||||
|
||||
if (!Mathf.Approximately(nextLength, targetFirstSegmentLength))
|
||||
{
|
||||
targetFirstSegmentLength = nextLength;
|
||||
solver.SetFirstSegmentLength(targetFirstSegmentLength);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Rebuild Line"))
|
||||
{
|
||||
solver.RebuildNow();
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Retract"))
|
||||
{
|
||||
targetFirstSegmentLength = Mathf.Max(minFirstSegmentLength, targetFirstSegmentLength - 0.1f);
|
||||
solver.SetFirstSegmentLength(targetFirstSegmentLength);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Extend"))
|
||||
{
|
||||
targetFirstSegmentLength = Mathf.Min(maxFirstSegmentLength, targetFirstSegmentLength + 0.1f);
|
||||
solver.SetFirstSegmentLength(targetFirstSegmentLength);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
|
||||
GUILayout.BeginArea(new Rect(16f, 204f, 420f, 320f), GUI.skin.box);
|
||||
GUILayout.Label("Fishing Line Debug");
|
||||
if (solver.TryGetComponent<FishingLineRenderer>(out var renderer))
|
||||
{
|
||||
GUILayout.Label($"Render Samples: {renderer.SampleCount}");
|
||||
GUILayout.Label($"Rendered Length: {renderer.CurrentRenderedLength:F2} m");
|
||||
}
|
||||
|
||||
detailScroll = GUILayout.BeginScrollView(detailScroll, GUILayout.Width(404f), GUILayout.Height(250f));
|
||||
|
||||
if (showNodeDetails)
|
||||
{
|
||||
GUILayout.Label("Nodes");
|
||||
for (var i = 0; i < solver.OrderedNodeCount; i++)
|
||||
{
|
||||
if (!solver.TryGetOrderedNode(i, out var node))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GUILayout.Label($"#{i} {node.GetDebugSummary()}");
|
||||
}
|
||||
}
|
||||
|
||||
if (showSegmentDetails)
|
||||
{
|
||||
GUILayout.Label("Segments");
|
||||
for (var i = 0; i < solver.SegmentCount; i++)
|
||||
{
|
||||
var rest = solver.RestLengths[i];
|
||||
var actual = solver.GetActualDistance(i);
|
||||
GUILayout.Label($"S{i} rest:{rest:F3}m actual:{actual:F3}m diff:{(actual - rest):F3}m");
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ PlayerSettings:
|
||||
androidApplicationEntry: 2
|
||||
defaultIsNativeResolution: 1
|
||||
macRetinaSupport: 1
|
||||
runInBackground: 0
|
||||
runInBackground: 1
|
||||
muteOtherAudioSources: 0
|
||||
Prepare IOS For Recording: 0
|
||||
Force IOS Speakers When Recording: 0
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!78 &1
|
||||
TagManager:
|
||||
serializedVersion: 2
|
||||
serializedVersion: 3
|
||||
tags: []
|
||||
layers:
|
||||
- Default
|
||||
@@ -11,7 +11,7 @@ TagManager:
|
||||
-
|
||||
- Water
|
||||
- UI
|
||||
-
|
||||
- Terrain
|
||||
-
|
||||
-
|
||||
-
|
||||
@@ -50,27 +50,4 @@ TagManager:
|
||||
- Light Layer 5
|
||||
- Light Layer 6
|
||||
- Light Layer 7
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
m_MigratedRenderPipelines: []
|
||||
|
||||
Reference in New Issue
Block a user