渲染和逻辑分离
This commit is contained in:
@@ -355,7 +355,7 @@ GameObject:
|
|||||||
- component: {fileID: 524034410}
|
- component: {fileID: 524034410}
|
||||||
- component: {fileID: 524034409}
|
- component: {fileID: 524034409}
|
||||||
- component: {fileID: 524034408}
|
- component: {fileID: 524034408}
|
||||||
m_Layer: 0
|
m_Layer: 6
|
||||||
m_Name: Plane
|
m_Name: Plane
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -468,6 +468,7 @@ GameObject:
|
|||||||
- component: {fileID: 678145338}
|
- component: {fileID: 678145338}
|
||||||
- component: {fileID: 678145337}
|
- component: {fileID: 678145337}
|
||||||
- component: {fileID: 678145336}
|
- component: {fileID: 678145336}
|
||||||
|
- component: {fileID: 678145340}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: RopeGenerator
|
m_Name: RopeGenerator
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -491,7 +492,7 @@ MonoBehaviour:
|
|||||||
enabled: 1
|
enabled: 1
|
||||||
ropeStartPoint: {x: -3, y: 4, z: 0}
|
ropeStartPoint: {x: -3, y: 4, z: 0}
|
||||||
ropeEndPoint: {x: 3, y: 4, z: 0}
|
ropeEndPoint: {x: 3, y: 4, z: 0}
|
||||||
ropeResolution: 0.895
|
ropeResolution: 0.5
|
||||||
ropeRadius: 0.1
|
ropeRadius: 0.1
|
||||||
gravity: {x: 0, y: -9.81, z: 0}
|
gravity: {x: 0, y: -9.81, z: 0}
|
||||||
airFriction: 0.1
|
airFriction: 0.1
|
||||||
@@ -505,7 +506,7 @@ MonoBehaviour:
|
|||||||
enableCollisions: 1
|
enableCollisions: 1
|
||||||
collisionLayer:
|
collisionLayer:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 4294967295
|
m_Bits: 64
|
||||||
enableSelfCollision: 0
|
enableSelfCollision: 0
|
||||||
initialPinPoints:
|
initialPinPoints:
|
||||||
- nodeIndex: 0
|
- nodeIndex: 0
|
||||||
@@ -514,8 +515,7 @@ MonoBehaviour:
|
|||||||
- nodeIndex: -1
|
- nodeIndex: -1
|
||||||
targetTransform: {fileID: 415232843}
|
targetTransform: {fileID: 415232843}
|
||||||
localPos: {x: 0, y: 0, z: 0}
|
localPos: {x: 0, y: 0, z: 0}
|
||||||
meshRadialSegments: 8
|
ropeRendererComponent: {fileID: 0}
|
||||||
meshTextureTiling: 1
|
|
||||||
--- !u!23 &678145337
|
--- !u!23 &678145337
|
||||||
MeshRenderer:
|
MeshRenderer:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -588,6 +588,20 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &678145340
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 678145335}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: daa54ae922807734c889e8404bfd7b2e, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::AdvancedRope.RopeMeshRenderer
|
||||||
|
radialSegments: 8
|
||||||
|
textureTiling: 1
|
||||||
--- !u!1 &956472882
|
--- !u!1 &956472882
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
10
Assets/AdvancedRope/Scripts/IRopeRenderer.cs
Normal file
10
Assets/AdvancedRope/Scripts/IRopeRenderer.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace AdvancedRope
|
||||||
|
{
|
||||||
|
public interface IRopeRenderer
|
||||||
|
{
|
||||||
|
void Bind(VerletRopeGenerator ropeGenerator);
|
||||||
|
void Rebuild();
|
||||||
|
void Render();
|
||||||
|
void Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/AdvancedRope/Scripts/IRopeRenderer.cs.meta
Normal file
2
Assets/AdvancedRope/Scripts/IRopeRenderer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5fb18fbc92c2ecf49a7fcbb8fce7df36
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
namespace AdvancedRope
|
namespace AdvancedRope
|
||||||
{
|
{
|
||||||
public class RopeMesh
|
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
|
||||||
|
public class RopeMeshRenderer : MonoBehaviour, IRopeRenderer
|
||||||
{
|
{
|
||||||
private readonly int _radialSegments;
|
[SerializeField] private int radialSegments = 8;
|
||||||
private readonly float _textureTiling;
|
[SerializeField] private float textureTiling = 1f;
|
||||||
|
|
||||||
private VerletRopeGenerator _verletRopeGenerator;
|
private VerletRopeGenerator _ropeGenerator;
|
||||||
private MeshFilter _meshFilter;
|
private MeshFilter _meshFilter;
|
||||||
private Mesh _mesh;
|
private Mesh _mesh;
|
||||||
private Vector3[] _vertices;
|
private Vector3[] _vertices;
|
||||||
@@ -15,47 +16,76 @@ namespace AdvancedRope
|
|||||||
private Vector2[] _uvs;
|
private Vector2[] _uvs;
|
||||||
private float _radius;
|
private float _radius;
|
||||||
|
|
||||||
public RopeMesh(MeshFilter meshFilter, VerletRopeGenerator verletRopeGenerator, int radialSegments = 8, float textureTiling = 1f)
|
private void Awake()
|
||||||
{
|
{
|
||||||
_verletRopeGenerator = verletRopeGenerator;
|
_meshFilter = GetComponent<MeshFilter>();
|
||||||
_meshFilter = meshFilter;
|
|
||||||
_radialSegments = radialSegments;
|
|
||||||
_textureTiling = textureTiling;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetupRopeMesh()
|
public void Bind(VerletRopeGenerator ropeGenerator)
|
||||||
{
|
{
|
||||||
_mesh = new Mesh
|
_ropeGenerator = ropeGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rebuild()
|
||||||
|
{
|
||||||
|
SetupRopeMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
UpdateMeshVertices();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
ClearMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupRopeMesh()
|
||||||
|
{
|
||||||
|
if (_ropeGenerator == null) return;
|
||||||
|
if (!_meshFilter) _meshFilter = GetComponent<MeshFilter>();
|
||||||
|
|
||||||
|
var segmentCount = Mathf.Max(3, radialSegments);
|
||||||
|
|
||||||
|
if (_mesh == null)
|
||||||
{
|
{
|
||||||
name = "ProceduralRope"
|
_mesh = new Mesh
|
||||||
};
|
{
|
||||||
_mesh.MarkDynamic();
|
name = "ProceduralRope"
|
||||||
|
};
|
||||||
|
_mesh.MarkDynamic();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mesh.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
_meshFilter.sharedMesh = _mesh;
|
_meshFilter.sharedMesh = _mesh;
|
||||||
|
|
||||||
_radius = _verletRopeGenerator.ropeRadius;
|
_radius = _ropeGenerator.ropeRadius;
|
||||||
var nodes = _verletRopeGenerator.Nodes;
|
var nodes = _ropeGenerator.Nodes;
|
||||||
var vertexCount = nodes.Count * (_radialSegments + 1) + 2;
|
var vertexCount = nodes.Count * (segmentCount + 1) + 2;
|
||||||
var triCount = (nodes.Count - 1) * _radialSegments * 6;
|
var triCount = (nodes.Count - 1) * segmentCount * 6;
|
||||||
var capTrisCount = (_radialSegments * 3) * 2;
|
var capTrisCount = (segmentCount * 3) * 2;
|
||||||
|
|
||||||
_vertices = new Vector3[vertexCount];
|
_vertices = new Vector3[vertexCount];
|
||||||
_uvs = new Vector2[vertexCount];
|
_uvs = new Vector2[vertexCount];
|
||||||
_triangles = new int[triCount + capTrisCount];
|
_triangles = new int[triCount + capTrisCount];
|
||||||
|
|
||||||
var triIndex = 0;
|
var triIndex = 0;
|
||||||
|
|
||||||
for (var i = 0; i < nodes.Count - 1; i++)
|
for (var i = 0; i < nodes.Count - 1; i++)
|
||||||
{
|
{
|
||||||
for (var j = 0; j < _radialSegments; j++)
|
for (var j = 0; j < segmentCount; j++)
|
||||||
{
|
{
|
||||||
var current = i * (_radialSegments + 1) + j;
|
var current = i * (segmentCount + 1) + j;
|
||||||
var next = current + _radialSegments + 1;
|
var next = current + segmentCount + 1;
|
||||||
|
|
||||||
_triangles[triIndex++] = current;
|
_triangles[triIndex++] = current;
|
||||||
_triangles[triIndex++] = current + 1;
|
_triangles[triIndex++] = current + 1;
|
||||||
_triangles[triIndex++] = next;
|
_triangles[triIndex++] = next;
|
||||||
|
|
||||||
_triangles[triIndex++] = next;
|
_triangles[triIndex++] = next;
|
||||||
_triangles[triIndex++] = current + 1;
|
_triangles[triIndex++] = current + 1;
|
||||||
_triangles[triIndex++] = next + 1;
|
_triangles[triIndex++] = next + 1;
|
||||||
@@ -64,21 +94,21 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
var startCenterIndex = vertexCount - 2;
|
var startCenterIndex = vertexCount - 2;
|
||||||
var endCenterIndex = vertexCount - 1;
|
var endCenterIndex = vertexCount - 1;
|
||||||
|
|
||||||
// Start Cap
|
// Start Cap
|
||||||
for (var j = 0; j < _radialSegments; j++)
|
for (var j = 0; j < segmentCount; j++)
|
||||||
{
|
{
|
||||||
var current = j;
|
var current = j;
|
||||||
var next = j + 1;
|
var next = j + 1;
|
||||||
|
|
||||||
_triangles[triIndex++] = startCenterIndex;
|
_triangles[triIndex++] = startCenterIndex;
|
||||||
_triangles[triIndex++] = next;
|
_triangles[triIndex++] = next;
|
||||||
_triangles[triIndex++] = current;
|
_triangles[triIndex++] = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End Cap
|
// End Cap
|
||||||
var lastRingStart = (nodes.Count - 1) * (_radialSegments + 1);
|
var lastRingStart = (nodes.Count - 1) * (segmentCount + 1);
|
||||||
for (var j = 0; j < _radialSegments; j++)
|
for (var j = 0; j < segmentCount; j++)
|
||||||
{
|
{
|
||||||
var current = lastRingStart + j;
|
var current = lastRingStart + j;
|
||||||
var next = lastRingStart + j + 1;
|
var next = lastRingStart + j + 1;
|
||||||
@@ -90,22 +120,36 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
_mesh.vertices = _vertices;
|
_mesh.vertices = _vertices;
|
||||||
_mesh.triangles = _triangles;
|
_mesh.triangles = _triangles;
|
||||||
|
_mesh.uv = _uvs;
|
||||||
|
_mesh.RecalculateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateMesh()
|
private void ClearMesh()
|
||||||
{
|
{
|
||||||
UpdateMeshVertices();
|
if (_meshFilter)
|
||||||
|
_meshFilter.sharedMesh = null;
|
||||||
|
|
||||||
|
if (_mesh != null)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying)
|
||||||
|
Destroy(_mesh);
|
||||||
|
else
|
||||||
|
DestroyImmediate(_mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mesh = null;
|
||||||
}
|
}
|
||||||
public void ClearMesh()
|
|
||||||
{
|
|
||||||
_meshFilter.sharedMesh = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateMeshVertices()
|
private void UpdateMeshVertices()
|
||||||
{
|
{
|
||||||
var verletTransform = _verletRopeGenerator.transform;
|
if (_ropeGenerator == null || _mesh == null) return;
|
||||||
var nodes = _verletRopeGenerator.Nodes;
|
|
||||||
if(nodes.Count < 2) return;
|
var segmentCount = Mathf.Max(3, radialSegments);
|
||||||
|
var uvTiling = Mathf.Max(0.01f, textureTiling);
|
||||||
|
|
||||||
|
var verletTransform = _ropeGenerator.transform;
|
||||||
|
var nodes = _ropeGenerator.Nodes;
|
||||||
|
if (nodes.Count < 2) return;
|
||||||
|
|
||||||
var lastRotation = Quaternion.identity;
|
var lastRotation = Quaternion.identity;
|
||||||
var lastForward = Vector3.zero;
|
var lastForward = Vector3.zero;
|
||||||
@@ -117,9 +161,9 @@ namespace AdvancedRope
|
|||||||
forward = (nodes[i + 1].position - nodes[i].position).normalized;
|
forward = (nodes[i + 1].position - nodes[i].position).normalized;
|
||||||
else
|
else
|
||||||
forward = (nodes[i].position - nodes[i - 1].position).normalized;
|
forward = (nodes[i].position - nodes[i - 1].position).normalized;
|
||||||
|
|
||||||
if (forward == Vector3.zero) forward = Vector3.up;
|
if (forward == Vector3.zero) forward = Vector3.up;
|
||||||
|
|
||||||
Quaternion rotation;
|
Quaternion rotation;
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
@@ -127,16 +171,16 @@ namespace AdvancedRope
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var swing = Quaternion.FromToRotation(lastForward, forward);
|
var swing = Quaternion.FromToRotation(lastForward, forward);
|
||||||
|
|
||||||
rotation = swing * lastRotation;
|
rotation = swing * lastRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastRotation = rotation;
|
lastRotation = rotation;
|
||||||
lastForward = forward;
|
lastForward = forward;
|
||||||
|
|
||||||
for (var j = 0; j <= _radialSegments; j++)
|
for (var j = 0; j <= segmentCount; j++)
|
||||||
{
|
{
|
||||||
var u = (float)j / _radialSegments;
|
var u = (float)j / segmentCount;
|
||||||
var angle = u * Mathf.PI * 2;
|
var angle = u * Mathf.PI * 2;
|
||||||
|
|
||||||
var x = Mathf.Cos(angle) * _radius;
|
var x = Mathf.Cos(angle) * _radius;
|
||||||
@@ -146,24 +190,24 @@ namespace AdvancedRope
|
|||||||
var worldPos = nodes[i].position + (rotation * localPos);
|
var worldPos = nodes[i].position + (rotation * localPos);
|
||||||
|
|
||||||
var meshLocalPos = verletTransform.InverseTransformPoint(worldPos);
|
var meshLocalPos = verletTransform.InverseTransformPoint(worldPos);
|
||||||
|
|
||||||
var vertIndex = i * (_radialSegments + 1) + j;
|
var vertIndex = i * (segmentCount + 1) + j;
|
||||||
_vertices[vertIndex] = meshLocalPos;
|
_vertices[vertIndex] = meshLocalPos;
|
||||||
|
|
||||||
var v = (float)i / (nodes.Count - 1) * _textureTiling;
|
var v = (float)i / (nodes.Count - 1) * uvTiling;
|
||||||
_uvs[vertIndex] = new Vector2(u, v);
|
_uvs[vertIndex] = new Vector2(u, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var startCenterIndex = _vertices.Length - 2;
|
var startCenterIndex = _vertices.Length - 2;
|
||||||
var endCenterIndex = _vertices.Length - 1;
|
var endCenterIndex = _vertices.Length - 1;
|
||||||
|
|
||||||
_vertices[startCenterIndex] = verletTransform.InverseTransformPoint(nodes[0].position);
|
_vertices[startCenterIndex] = verletTransform.InverseTransformPoint(nodes[0].position);
|
||||||
_uvs[startCenterIndex] = new Vector2(0.5f, 0f);
|
_uvs[startCenterIndex] = new Vector2(0.5f, 0f);
|
||||||
|
|
||||||
_vertices[endCenterIndex] = verletTransform.InverseTransformPoint(nodes[^1].position);
|
_vertices[endCenterIndex] = verletTransform.InverseTransformPoint(nodes[^1].position);
|
||||||
_uvs[endCenterIndex] = new Vector2(0.5f, 1f * _textureTiling);
|
_uvs[endCenterIndex] = new Vector2(0.5f, uvTiling);
|
||||||
|
|
||||||
_mesh.vertices = _vertices;
|
_mesh.vertices = _vertices;
|
||||||
_mesh.uv = _uvs;
|
_mesh.uv = _uvs;
|
||||||
|
|
||||||
@@ -7,7 +7,6 @@ namespace AdvancedRope
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用于生成并模拟基于 Verlet 积分绳索的基类。
|
/// 用于生成并模拟基于 Verlet 积分绳索的基类。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
|
|
||||||
public class VerletRopeGenerator : MonoBehaviour
|
public class VerletRopeGenerator : MonoBehaviour
|
||||||
{
|
{
|
||||||
#region --- 数据结构 ---
|
#region --- 数据结构 ---
|
||||||
@@ -24,7 +23,7 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
public RopeNode connectedNode;
|
public RopeNode connectedNode;
|
||||||
public Vector3 connectionOffset;
|
public Vector3 connectionOffset;
|
||||||
|
|
||||||
public Transform pinnedTransform;
|
public Transform pinnedTransform;
|
||||||
public Vector3 pinLocalPos;
|
public Vector3 pinLocalPos;
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ namespace AdvancedRope
|
|||||||
pinnedTransform = t;
|
pinnedTransform = t;
|
||||||
pinLocalPos = offset;
|
pinLocalPos = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将该节点连接到另一条绳索节点,可选偏移量。
|
/// 将该节点连接到另一条绳索节点,可选偏移量。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -59,7 +58,7 @@ namespace AdvancedRope
|
|||||||
isPinned = false;
|
isPinned = false;
|
||||||
pinnedTransform = null;
|
pinnedTransform = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 取消该节点的固定状态。
|
/// 取消该节点的固定状态。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -82,8 +81,7 @@ namespace AdvancedRope
|
|||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public struct PinConfiguration
|
public struct PinConfiguration
|
||||||
{
|
{
|
||||||
[Tooltip("要固定的节点索引(0 = 起点,-1 = 末尾)")]
|
[Tooltip("要固定的节点索引(0 = 起点,-1 = 末尾)")] public int nodeIndex;
|
||||||
public int nodeIndex;
|
|
||||||
public Transform targetTransform;
|
public Transform targetTransform;
|
||||||
public Vector3 localPos;
|
public Vector3 localPos;
|
||||||
}
|
}
|
||||||
@@ -95,33 +93,29 @@ namespace AdvancedRope
|
|||||||
//[Header("通用设置")]
|
//[Header("通用设置")]
|
||||||
public bool initializeOnStart = false;
|
public bool initializeOnStart = false;
|
||||||
public bool enabled = true;
|
public bool enabled = true;
|
||||||
[Tooltip("绳索起点")]
|
[Tooltip("绳索起点")] public Vector3 ropeStartPoint;
|
||||||
public Vector3 ropeStartPoint;
|
[Tooltip("绳索终点(可选,若为空则绳索自由下垂)")] public Vector3 ropeEndPoint;
|
||||||
[Tooltip("绳索终点(可选,若为空则绳索自由下垂)")]
|
|
||||||
public Vector3 ropeEndPoint;
|
|
||||||
|
|
||||||
//[Header("分辨率与尺寸")]
|
//[Header("分辨率与尺寸")]
|
||||||
[Range(0.01f, 1f)]
|
[Range(0.01f, 1f)] [Tooltip("0.1 = 稀疏,1.0 = 按半径可达到的最大密度")]
|
||||||
[Tooltip("0.1 = 稀疏,1.0 = 按半径可达到的最大密度")]
|
|
||||||
public float ropeResolution = 0.5f;
|
public float ropeResolution = 0.5f;
|
||||||
|
|
||||||
public float ropeRadius = 0.1f;
|
public float ropeRadius = 0.1f;
|
||||||
|
|
||||||
//[Header("物理参数")]
|
//[Header("物理参数")]
|
||||||
public Vector3 gravity = new Vector3(0, -9.81f, 0);
|
public Vector3 gravity = new Vector3(0, -9.81f, 0);
|
||||||
[Range(0f, 1f)] public float airFriction = 0.1f;
|
[Range(0f, 1f)] public float airFriction = 0.1f;
|
||||||
[Range(0f, 1f)] public float stiffness = 1.0f;
|
[Range(0f, 1f)] public float stiffness = 1.0f;
|
||||||
[Tooltip("约束求解迭代次数。值越高越硬,但开销更大")]
|
[Tooltip("约束求解迭代次数。值越高越硬,但开销更大")] public int constraintIterations = 8;
|
||||||
public int constraintIterations = 8;
|
[Tooltip("绳索与刚体碰撞时施加给刚体的力")] public float collisionPushPower = 5f;
|
||||||
[Tooltip("绳索与刚体碰撞时施加给刚体的力")]
|
|
||||||
public float collisionPushPower = 5f;
|
|
||||||
[Range(0f, 1f)] public float contactFriction = 0.5f;
|
[Range(0f, 1f)] public float contactFriction = 0.5f;
|
||||||
|
|
||||||
|
|
||||||
//[Header("耦合设置")]
|
//[Header("耦合设置")]
|
||||||
public Rigidbody connectedBodyStart;
|
public Rigidbody connectedBodyStart;
|
||||||
public Rigidbody connectedBodyEnd;
|
public Rigidbody connectedBodyEnd;
|
||||||
public float rigidbodyInteractionForce = 10f;
|
public float rigidbodyInteractionForce = 10f;
|
||||||
|
|
||||||
//[Header("碰撞设置")]
|
//[Header("碰撞设置")]
|
||||||
public bool enableCollisions = true;
|
public bool enableCollisions = true;
|
||||||
public LayerMask collisionLayer;
|
public LayerMask collisionLayer;
|
||||||
@@ -129,65 +123,82 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
//[Header("初始设置")]
|
//[Header("初始设置")]
|
||||||
public List<PinConfiguration> initialPinPoints;
|
public List<PinConfiguration> initialPinPoints;
|
||||||
|
|
||||||
//[Header("网格设置")]
|
|
||||||
public int meshRadialSegments = 8;
|
|
||||||
public float meshTextureTiling = 1f;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region --- 公开属性 ---
|
#region --- 公开属性 ---
|
||||||
|
|
||||||
public List<RopeNode> Nodes { get; private set; } = new List<RopeNode>();
|
public List<RopeNode> Nodes { get; private set; } = new List<RopeNode>();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region --- 私有变量 ---
|
#region --- 私有变量 ---
|
||||||
|
|
||||||
private float _segmentLength;
|
private float _segmentLength;
|
||||||
private float _preferredSegmentLength;
|
private float _preferredSegmentLength;
|
||||||
private Collider[] _collisionBuffer = new Collider[10];
|
private Collider[] _collisionBuffer = new Collider[10];
|
||||||
private List<Collider> _ignoreColliders = new List<Collider>();
|
private List<Collider> _ignoreColliders = new List<Collider>();
|
||||||
|
|
||||||
private RopeMesh _ropeMesh;
|
private IRopeRenderer _ropeRenderer;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region --- 初始化 ---
|
#region --- 初始化 ---
|
||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
if(enabled)
|
if (enabled)
|
||||||
VerletPhysics.RegisterRopeGenerator(this);
|
VerletPhysics.RegisterRopeGenerator(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
VerletPhysics.UnregisterRopeGenerator(this);
|
VerletPhysics.UnregisterRopeGenerator(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
ResolveRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
if(initializeOnStart && enabled)
|
if (initializeOnStart && enabled)
|
||||||
InitializeRope();
|
InitializeRope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ResolveRenderer()
|
||||||
|
{
|
||||||
|
if (_ropeRenderer != null) return;
|
||||||
|
var behaviours = GetComponents<MonoBehaviour>();
|
||||||
|
foreach (var behaviour in behaviours)
|
||||||
|
{
|
||||||
|
if (behaviour is not IRopeRenderer ropeRenderer) continue;
|
||||||
|
_ropeRenderer = ropeRenderer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ropeRenderer == null)
|
||||||
|
Debug.LogWarning("未找到 IRopeRenderer 实现,绳索将不会渲染。请在预制体上绑定渲染组件。", this);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据参数从零创建绳索。
|
/// 根据参数从零创建绳索。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void InitializeRope()
|
public void InitializeRope()
|
||||||
{
|
{
|
||||||
_ropeMesh?.ClearMesh();
|
ResolveRenderer();
|
||||||
_ropeMesh = new RopeMesh(GetComponent<MeshFilter>(), this, meshRadialSegments, meshTextureTiling);
|
_ropeRenderer?.Clear();
|
||||||
|
_ropeRenderer?.Bind(this);
|
||||||
Nodes.Clear();
|
Nodes.Clear();
|
||||||
_ignoreColliders.Clear();
|
_ignoreColliders.Clear();
|
||||||
|
|
||||||
var startPos = ropeStartPoint;
|
var startPos = ropeStartPoint;
|
||||||
var endPos = ropeEndPoint;
|
var endPos = ropeEndPoint;
|
||||||
|
|
||||||
if(Vector3.Distance(startPos, endPos) < 0.01f)
|
if (Vector3.Distance(startPos, endPos) < 0.01f)
|
||||||
endPos = startPos + Vector3.forward * 5f;
|
endPos = startPos + Vector3.forward * 5f;
|
||||||
|
|
||||||
var totalDistance = Vector3.Distance(startPos, endPos);
|
var totalDistance = Vector3.Distance(startPos, endPos);
|
||||||
|
|
||||||
var diameter = ropeRadius * 2f;
|
var diameter = ropeRadius * 2f;
|
||||||
@@ -195,39 +206,39 @@ namespace AdvancedRope
|
|||||||
var minNodes = 2;
|
var minNodes = 2;
|
||||||
|
|
||||||
var nodeCount = Mathf.RoundToInt(Mathf.Lerp(minNodes, maxNodes, ropeResolution));
|
var nodeCount = Mathf.RoundToInt(Mathf.Lerp(minNodes, maxNodes, ropeResolution));
|
||||||
|
|
||||||
_segmentLength = totalDistance / (nodeCount - 1);
|
_segmentLength = totalDistance / (nodeCount - 1);
|
||||||
_preferredSegmentLength = _segmentLength;
|
_preferredSegmentLength = _segmentLength;
|
||||||
|
|
||||||
var direction = (endPos - startPos).normalized;
|
var direction = (endPos - startPos).normalized;
|
||||||
for (var i = 0; i < nodeCount; i++)
|
for (var i = 0; i < nodeCount; i++)
|
||||||
{
|
{
|
||||||
var pos = startPos + direction * (_segmentLength * i);
|
var pos = startPos + direction * (_segmentLength * i);
|
||||||
Nodes.Add(new RopeNode(pos));
|
Nodes.Add(new RopeNode(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用初始固定点配置
|
// 应用初始固定点配置
|
||||||
ApplyInitialPins();
|
ApplyInitialPins();
|
||||||
|
|
||||||
if(connectedBodyStart)
|
if (connectedBodyStart)
|
||||||
_ignoreColliders.AddRange(connectedBodyStart.GetComponentsInChildren<Collider>());
|
_ignoreColliders.AddRange(connectedBodyStart.GetComponentsInChildren<Collider>());
|
||||||
if(connectedBodyEnd)
|
if (connectedBodyEnd)
|
||||||
_ignoreColliders.AddRange(connectedBodyEnd.GetComponentsInChildren<Collider>());
|
_ignoreColliders.AddRange(connectedBodyEnd.GetComponentsInChildren<Collider>());
|
||||||
|
|
||||||
_ropeMesh.SetupRopeMesh();
|
_ropeRenderer?.Rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyInitialPins()
|
private void ApplyInitialPins()
|
||||||
{
|
{
|
||||||
if(initialPinPoints == null) return;
|
if (initialPinPoints == null) return;
|
||||||
|
|
||||||
foreach (var pin in initialPinPoints)
|
foreach (var pin in initialPinPoints)
|
||||||
{
|
{
|
||||||
var index = pin.nodeIndex;
|
var index = pin.nodeIndex;
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
index = Nodes.Count + index;
|
index = Nodes.Count + index;
|
||||||
|
|
||||||
if(index >= 0 && index < Nodes.Count && pin.targetTransform)
|
if (index >= 0 && index < Nodes.Count && pin.targetTransform)
|
||||||
PinNode(index, pin.targetTransform, pin.localPos);
|
PinNode(index, pin.targetTransform, pin.localPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,15 +249,15 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
private void LateUpdate()
|
private void LateUpdate()
|
||||||
{
|
{
|
||||||
if(!enabled) return;
|
if (!enabled) return;
|
||||||
_ropeMesh.UpdateMesh();
|
_ropeRenderer?.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FixedUpdate()
|
private void FixedUpdate()
|
||||||
{
|
{
|
||||||
if(!enabled) return;
|
if (!enabled) return;
|
||||||
if(Nodes.Count < 2) return;
|
if (Nodes.Count < 2) return;
|
||||||
|
|
||||||
SimulateVerlet();
|
SimulateVerlet();
|
||||||
|
|
||||||
|
|
||||||
@@ -256,9 +267,9 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
ApplyInterRopeConnection();
|
ApplyInterRopeConnection();
|
||||||
// 如需强制同步连接节点,可启用下一行
|
// 如需强制同步连接节点,可启用下一行
|
||||||
|
|
||||||
// 每次迭代都检查碰撞
|
// 每次迭代都检查碰撞
|
||||||
if(enableCollisions)
|
if (enableCollisions)
|
||||||
ResolveCollisions();
|
ResolveCollisions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,15 +284,15 @@ namespace AdvancedRope
|
|||||||
for (var i = 0; i < Nodes.Count; i++)
|
for (var i = 0; i < Nodes.Count; i++)
|
||||||
{
|
{
|
||||||
var myNode = Nodes[i];
|
var myNode = Nodes[i];
|
||||||
if(myNode.connectedNode == null) continue;
|
if (myNode.connectedNode == null) continue;
|
||||||
|
|
||||||
var otherNode = myNode.connectedNode;
|
var otherNode = myNode.connectedNode;
|
||||||
|
|
||||||
var currentVector = myNode.position - otherNode.position;
|
var currentVector = myNode.position - otherNode.position;
|
||||||
var targetVector = myNode.connectionOffset;
|
var targetVector = myNode.connectionOffset;
|
||||||
|
|
||||||
var diff = currentVector - targetVector;
|
var diff = currentVector - targetVector;
|
||||||
|
|
||||||
var moveFactorA = 0.5f;
|
var moveFactorA = 0.5f;
|
||||||
var moveFactorB = 0.5f;
|
var moveFactorB = 0.5f;
|
||||||
|
|
||||||
@@ -295,14 +306,14 @@ namespace AdvancedRope
|
|||||||
moveFactorA = 1f;
|
moveFactorA = 1f;
|
||||||
moveFactorB = 0f;
|
moveFactorB = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(moveFactorA > 0f)
|
if (moveFactorA > 0f)
|
||||||
myNode.position -= diff * moveFactorA;
|
myNode.position -= diff * moveFactorA;
|
||||||
if(moveFactorB > 0f)
|
if (moveFactorB > 0f)
|
||||||
otherNode.position += diff * moveFactorB;
|
otherNode.position += diff * moveFactorB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在约束迭代过程中防止连接节点发生漂移。
|
/// 在约束迭代过程中防止连接节点发生漂移。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -313,7 +324,7 @@ namespace AdvancedRope
|
|||||||
node.position = node.connectedNode.position + node.connectionOffset;
|
node.position = node.connectedNode.position + node.connectionOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 基础 Verlet 积分:x(t+dt) = 2x(t) - x(t-dt) + a*dt*dt
|
/// 基础 Verlet 积分:x(t+dt) = 2x(t) - x(t-dt) + a*dt*dt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -331,12 +342,13 @@ namespace AdvancedRope
|
|||||||
node.position = node.pinnedTransform.TransformPoint(node.pinLocalPos);
|
node.position = node.pinnedTransform.TransformPoint(node.pinLocalPos);
|
||||||
node.prevPosition = node.position;
|
node.prevPosition = node.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var velocity = (node.position - node.prevPosition) * (1f - airFriction);
|
var velocity = (node.position - node.prevPosition) * (1f - airFriction);
|
||||||
var newPos = node.position + velocity + gravityStep;
|
var newPos = node.position + velocity + gravityStep;
|
||||||
|
|
||||||
node.prevPosition = node.position;
|
node.prevPosition = node.position;
|
||||||
node.position = newPos;
|
node.position = newPos;
|
||||||
}
|
}
|
||||||
@@ -348,17 +360,17 @@ namespace AdvancedRope
|
|||||||
{
|
{
|
||||||
var nodeA = Nodes[i];
|
var nodeA = Nodes[i];
|
||||||
var nodeB = Nodes[i + 1];
|
var nodeB = Nodes[i + 1];
|
||||||
|
|
||||||
var diff = nodeA.position - nodeB.position;
|
var diff = nodeA.position - nodeB.position;
|
||||||
var dist = diff.magnitude;
|
var dist = diff.magnitude;
|
||||||
if (dist < 0.0001f) continue;
|
if (dist < 0.0001f) continue;
|
||||||
|
|
||||||
var differance = (dist - _segmentLength) / dist;
|
var differance = (dist - _segmentLength) / dist;
|
||||||
var translate = diff * (0.5f * differance * stiffness);
|
var translate = diff * (0.5f * differance * stiffness);
|
||||||
|
|
||||||
if (!nodeA.isPinned)
|
if (!nodeA.isPinned)
|
||||||
nodeA.position -= translate;
|
nodeA.position -= translate;
|
||||||
|
|
||||||
if (!nodeB.isPinned)
|
if (!nodeB.isPinned)
|
||||||
nodeB.position += translate;
|
nodeB.position += translate;
|
||||||
}
|
}
|
||||||
@@ -373,7 +385,7 @@ namespace AdvancedRope
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void ApplyCouplingForces()
|
private void ApplyCouplingForces()
|
||||||
{
|
{
|
||||||
if(connectedBodyStart && Nodes.Count > 0)
|
if (connectedBodyStart && Nodes.Count > 0)
|
||||||
{
|
{
|
||||||
var firstNode = Nodes[0];
|
var firstNode = Nodes[0];
|
||||||
|
|
||||||
@@ -388,12 +400,12 @@ namespace AdvancedRope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(connectedBodyEnd && Nodes.Count > 0)
|
if (connectedBodyEnd && Nodes.Count > 0)
|
||||||
{
|
{
|
||||||
var lastNode = Nodes[^1];
|
var lastNode = Nodes[^1];
|
||||||
var prevNode = Nodes[^2];
|
var prevNode = Nodes[^2];
|
||||||
|
|
||||||
var pullDir = (prevNode.position - lastNode.position).normalized;
|
var pullDir = (prevNode.position - lastNode.position).normalized;
|
||||||
var dist = Vector3.Distance(prevNode.position, lastNode.position);
|
var dist = Vector3.Distance(prevNode.position, lastNode.position);
|
||||||
|
|
||||||
@@ -416,7 +428,7 @@ namespace AdvancedRope
|
|||||||
{
|
{
|
||||||
for (var i = 0; i < Nodes.Count; i++)
|
for (var i = 0; i < Nodes.Count; i++)
|
||||||
{
|
{
|
||||||
if(Nodes[i].isPinned) continue;
|
if (Nodes[i].isPinned) continue;
|
||||||
|
|
||||||
HandleTerrainCollision(Nodes[i]);
|
HandleTerrainCollision(Nodes[i]);
|
||||||
|
|
||||||
@@ -431,17 +443,18 @@ namespace AdvancedRope
|
|||||||
{
|
{
|
||||||
var terrain = UnityEngine.Terrain.activeTerrain;
|
var terrain = UnityEngine.Terrain.activeTerrain;
|
||||||
if (!terrain) return;
|
if (!terrain) return;
|
||||||
|
|
||||||
var terrainHeight = terrain.SampleHeight(node.position) + terrain.transform.position.y;
|
var terrainHeight = terrain.SampleHeight(node.position) + terrain.transform.position.y;
|
||||||
if (node.position.y < terrainHeight + ropeRadius)
|
if (node.position.y < terrainHeight + ropeRadius)
|
||||||
{
|
{
|
||||||
node.position = new Vector3(node.position.x, terrainHeight + ropeRadius, node.position.z);
|
node.position = new Vector3(node.position.x, terrainHeight + ropeRadius, node.position.z);
|
||||||
|
|
||||||
// 地面摩擦
|
// 地面摩擦
|
||||||
var vel = node.position - node.prevPosition;
|
var vel = node.position - node.prevPosition;
|
||||||
node.prevPosition = node.position - (vel * 0.5f);
|
node.prevPosition = node.position - (vel * 0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleStandardCollisions(RopeNode node)
|
private void HandleStandardCollisions(RopeNode node)
|
||||||
{
|
{
|
||||||
var count = Physics.OverlapSphereNonAlloc(node.position, ropeRadius, _collisionBuffer, collisionLayer);
|
var count = Physics.OverlapSphereNonAlloc(node.position, ropeRadius, _collisionBuffer, collisionLayer);
|
||||||
@@ -449,7 +462,7 @@ namespace AdvancedRope
|
|||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var col = _collisionBuffer[i];
|
var col = _collisionBuffer[i];
|
||||||
if(col is TerrainCollider) continue;
|
if (col is TerrainCollider) continue;
|
||||||
if (_ignoreColliders.Contains(col)) continue;
|
if (_ignoreColliders.Contains(col)) continue;
|
||||||
|
|
||||||
if (col is MeshCollider { convex: false } meshCol)
|
if (col is MeshCollider { convex: false } meshCol)
|
||||||
@@ -457,40 +470,40 @@ namespace AdvancedRope
|
|||||||
HandleNonConvexCollision(node, meshCol);
|
HandleNonConvexCollision(node, meshCol);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var closestPoint = col.ClosestPoint(node.position);
|
var closestPoint = col.ClosestPoint(node.position);
|
||||||
var distToSurface = Vector3.Distance(node.position, closestPoint);
|
var distToSurface = Vector3.Distance(node.position, closestPoint);
|
||||||
|
|
||||||
if (distToSurface < ropeRadius)
|
if (distToSurface < ropeRadius)
|
||||||
{
|
{
|
||||||
var pushDir = (node.position - closestPoint).normalized;
|
var pushDir = (node.position - closestPoint).normalized;
|
||||||
|
|
||||||
if(pushDir == Vector3.zero)
|
if (pushDir == Vector3.zero)
|
||||||
pushDir = (node.position - node.prevPosition).normalized;
|
pushDir = (node.position - node.prevPosition).normalized;
|
||||||
|
|
||||||
var penetrationDepth = ropeRadius - distToSurface;
|
var penetrationDepth = ropeRadius - distToSurface;
|
||||||
var pushVector = pushDir * penetrationDepth;
|
var pushVector = pushDir * penetrationDepth;
|
||||||
|
|
||||||
if(!node.isPinned)
|
if (!node.isPinned)
|
||||||
node.position += pushVector;
|
node.position += pushVector;
|
||||||
|
|
||||||
// 对刚体施加反作用力
|
// 对刚体施加反作用力
|
||||||
var targetRb = col.attachedRigidbody;
|
var targetRb = col.attachedRigidbody;
|
||||||
if (targetRb && !targetRb.isKinematic)
|
if (targetRb && !targetRb.isKinematic)
|
||||||
{
|
{
|
||||||
var scale = collisionPushPower / constraintIterations;
|
var scale = collisionPushPower / constraintIterations;
|
||||||
var reactionForce = -pushDir * (penetrationDepth * scale);
|
var reactionForce = -pushDir * (penetrationDepth * scale);
|
||||||
|
|
||||||
targetRb.AddForceAtPosition(reactionForce, node.position, ForceMode.Impulse);
|
targetRb.AddForceAtPosition(reactionForce, node.position, ForceMode.Impulse);
|
||||||
|
|
||||||
// 摩擦效果
|
// 摩擦效果
|
||||||
|
|
||||||
var nodeVel = (node.position - node.prevPosition) / Time.fixedDeltaTime;
|
var nodeVel = (node.position - node.prevPosition) / Time.fixedDeltaTime;
|
||||||
var rbVel = targetRb.GetPointVelocity(node.position);
|
var rbVel = targetRb.GetPointVelocity(node.position);
|
||||||
var relativeVel = rbVel - nodeVel;
|
var relativeVel = rbVel - nodeVel;
|
||||||
|
|
||||||
var tangentVel = Vector3.ProjectOnPlane(relativeVel, pushDir);
|
var tangentVel = Vector3.ProjectOnPlane(relativeVel, pushDir);
|
||||||
|
|
||||||
var normalForceMag = reactionForce.magnitude;
|
var normalForceMag = reactionForce.magnitude;
|
||||||
var frictionMag = normalForceMag * contactFriction;
|
var frictionMag = normalForceMag * contactFriction;
|
||||||
|
|
||||||
@@ -498,7 +511,7 @@ namespace AdvancedRope
|
|||||||
{
|
{
|
||||||
var frictionDir = -tangentVel.normalized;
|
var frictionDir = -tangentVel.normalized;
|
||||||
var frictionForce = frictionDir * frictionMag;
|
var frictionForce = frictionDir * frictionMag;
|
||||||
|
|
||||||
targetRb.AddForceAtPosition(frictionForce, node.position, ForceMode.Impulse);
|
targetRb.AddForceAtPosition(frictionForce, node.position, ForceMode.Impulse);
|
||||||
|
|
||||||
if (!node.isPinned)
|
if (!node.isPinned)
|
||||||
@@ -511,28 +524,30 @@ namespace AdvancedRope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleNonConvexCollision(RopeNode node, MeshCollider meshCol)
|
private void HandleNonConvexCollision(RopeNode node, MeshCollider meshCol)
|
||||||
{
|
{
|
||||||
var travelDir = node.position - node.prevPosition;
|
var travelDir = node.position - node.prevPosition;
|
||||||
var travelDist = travelDir.magnitude;
|
var travelDist = travelDir.magnitude;
|
||||||
|
|
||||||
if (travelDist < 0.0001f) return;
|
if (travelDist < 0.0001f) return;
|
||||||
|
|
||||||
if (Physics.SphereCast(node.prevPosition, ropeRadius, travelDir.normalized, out var hit,
|
if (Physics.SphereCast(node.prevPosition, ropeRadius, travelDir.normalized, out var hit,
|
||||||
travelDist + ropeRadius, collisionLayer))
|
travelDist + ropeRadius, collisionLayer))
|
||||||
{
|
{
|
||||||
if(hit.collider == meshCol)
|
if (hit.collider == meshCol)
|
||||||
node.position = hit.point + hit.normal * (ropeRadius * 1.1f);
|
node.position = hit.point + hit.normal * (ropeRadius * 1.1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSelfCollision(int currentIndex)
|
private void HandleSelfCollision(int currentIndex)
|
||||||
{
|
{
|
||||||
var currentNode = Nodes[currentIndex];
|
var currentNode = Nodes[currentIndex];
|
||||||
|
|
||||||
for (var i = 0; i < Nodes.Count; i++)
|
for (var i = 0; i < Nodes.Count; i++)
|
||||||
{
|
{
|
||||||
if(Mathf.Abs(currentIndex - i) <= 1) continue;
|
if (Mathf.Abs(currentIndex - i) <= 1) continue;
|
||||||
|
|
||||||
var otherNode = Nodes[i];
|
var otherNode = Nodes[i];
|
||||||
var distSqr = (currentNode.position - otherNode.position).sqrMagnitude;
|
var distSqr = (currentNode.position - otherNode.position).sqrMagnitude;
|
||||||
var minSpacing = ropeRadius * 2f;
|
var minSpacing = ropeRadius * 2f;
|
||||||
@@ -542,10 +557,10 @@ namespace AdvancedRope
|
|||||||
var dist = Mathf.Sqrt(distSqr);
|
var dist = Mathf.Sqrt(distSqr);
|
||||||
var pushDir = (currentNode.position - otherNode.position).normalized;
|
var pushDir = (currentNode.position - otherNode.position).normalized;
|
||||||
var pushAmount = (minSpacing - dist) * 0.5f;
|
var pushAmount = (minSpacing - dist) * 0.5f;
|
||||||
|
|
||||||
if(!currentNode.isPinned)
|
if (!currentNode.isPinned)
|
||||||
currentNode.position += pushDir * pushAmount;
|
currentNode.position += pushDir * pushAmount;
|
||||||
if(!otherNode.isPinned)
|
if (!otherNode.isPinned)
|
||||||
otherNode.position -= pushDir * pushAmount;
|
otherNode.position -= pushDir * pushAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,10 +572,10 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
public void Enable()
|
public void Enable()
|
||||||
{
|
{
|
||||||
if(enabled) return;
|
if (enabled) return;
|
||||||
if(gameObject.activeInHierarchy)
|
if (gameObject.activeInHierarchy)
|
||||||
VerletPhysics.RegisterRopeGenerator(this);
|
VerletPhysics.RegisterRopeGenerator(this);
|
||||||
|
|
||||||
InitializeRope();
|
InitializeRope();
|
||||||
enabled = true;
|
enabled = true;
|
||||||
}
|
}
|
||||||
@@ -568,7 +583,7 @@ namespace AdvancedRope
|
|||||||
public void Disable()
|
public void Disable()
|
||||||
{
|
{
|
||||||
VerletPhysics.UnregisterRopeGenerator(this);
|
VerletPhysics.UnregisterRopeGenerator(this);
|
||||||
_ropeMesh?.ClearMesh();
|
_ropeRenderer?.Clear();
|
||||||
enabled = false;
|
enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,7 +593,7 @@ namespace AdvancedRope
|
|||||||
public void IncreaseLength()
|
public void IncreaseLength()
|
||||||
{
|
{
|
||||||
if (TryInsertSegmentAtStart())
|
if (TryInsertSegmentAtStart())
|
||||||
_ropeMesh?.SetupRopeMesh();
|
_ropeRenderer?.Rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -587,7 +602,7 @@ namespace AdvancedRope
|
|||||||
public void DecreaseLength()
|
public void DecreaseLength()
|
||||||
{
|
{
|
||||||
if (TryRemoveFreeSegmentNode())
|
if (TryRemoveFreeSegmentNode())
|
||||||
_ropeMesh?.SetupRopeMesh();
|
_ropeRenderer?.Rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -620,7 +635,8 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
targetLength = Mathf.Max(0.001f, targetLength);
|
targetLength = Mathf.Max(0.001f, targetLength);
|
||||||
|
|
||||||
var preferredSegmentLength = Mathf.Max(0.001f, _preferredSegmentLength > 0f ? _preferredSegmentLength : _segmentLength);
|
var preferredSegmentLength = Mathf.Max(0.001f,
|
||||||
|
_preferredSegmentLength > 0f ? _preferredSegmentLength : _segmentLength);
|
||||||
var desiredSegmentCount = Mathf.Max(1, Mathf.RoundToInt(targetLength / preferredSegmentLength));
|
var desiredSegmentCount = Mathf.Max(1, Mathf.RoundToInt(targetLength / preferredSegmentLength));
|
||||||
|
|
||||||
var topologyChanged = false;
|
var topologyChanged = false;
|
||||||
@@ -642,7 +658,7 @@ namespace AdvancedRope
|
|||||||
_segmentLength = targetLength / Mathf.Max(1, Nodes.Count - 1);
|
_segmentLength = targetLength / Mathf.Max(1, Nodes.Count - 1);
|
||||||
|
|
||||||
if (topologyChanged)
|
if (topologyChanged)
|
||||||
_ropeMesh?.SetupRopeMesh();
|
_ropeRenderer?.Rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -675,7 +691,7 @@ namespace AdvancedRope
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将指定索引节点固定到目标变换组件,可选本地偏移。
|
/// 将指定索引节点固定到目标变换组件,可选本地偏移。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -684,32 +700,32 @@ namespace AdvancedRope
|
|||||||
/// <param name="localOffset"></param>
|
/// <param name="localOffset"></param>
|
||||||
public void PinNode(int index, Transform target, Vector3 localOffset = default)
|
public void PinNode(int index, Transform target, Vector3 localOffset = default)
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if (index < 0)
|
||||||
index = Nodes.Count + index;
|
index = Nodes.Count + index;
|
||||||
if (index >= 0 && index < Nodes.Count)
|
if (index >= 0 && index < Nodes.Count)
|
||||||
{
|
{
|
||||||
Nodes[index].PinTo(target, localOffset);
|
Nodes[index].PinTo(target, localOffset);
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
ropeStartPoint = target.position;
|
ropeStartPoint = target.position;
|
||||||
if(index == Nodes.Count -1)
|
if (index == Nodes.Count - 1)
|
||||||
ropeEndPoint = target.position;
|
ropeEndPoint = target.position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解除指定索引节点的固定。
|
/// 解除指定索引节点的固定。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">0 表示起点,-1 表示末尾节点</param>
|
/// <param name="index">0 表示起点,-1 表示末尾节点</param>
|
||||||
public void UnpinNode(int index)
|
public void UnpinNode(int index)
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if (index < 0)
|
||||||
index = Nodes.Count + index;
|
index = Nodes.Count + index;
|
||||||
if (index >= 0 && index < Nodes.Count)
|
if (index >= 0 && index < Nodes.Count)
|
||||||
{
|
{
|
||||||
Nodes[index].Unpin();
|
Nodes[index].Unpin();
|
||||||
if(index == 0)
|
if (index == 0)
|
||||||
ropeStartPoint = transform.position;
|
ropeStartPoint = transform.position;
|
||||||
if(index == Nodes.Count -1)
|
if (index == Nodes.Count - 1)
|
||||||
ropeEndPoint = transform.position + Vector3.forward * 5f;
|
ropeEndPoint = transform.position + Vector3.forward * 5f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -722,30 +738,31 @@ namespace AdvancedRope
|
|||||||
/// <param name="time"></param>
|
/// <param name="time"></param>
|
||||||
public void ConnectStartRigidbody(Rigidbody connectedRb, Vector3 localPos, float time)
|
public void ConnectStartRigidbody(Rigidbody connectedRb, Vector3 localPos, float time)
|
||||||
{
|
{
|
||||||
StopCoroutine(RigibBodyConnectionRoute(connectedRb,localPos, time));
|
StopCoroutine(RigibBodyConnectionRoute(connectedRb, localPos, time));
|
||||||
if(connectedBodyStart)
|
if (connectedBodyStart)
|
||||||
{
|
{
|
||||||
_ignoreColliders.RemoveAll(c =>
|
_ignoreColliders.RemoveAll(c =>
|
||||||
c && (c.gameObject == connectedBodyStart.gameObject) ||
|
c && (c.gameObject == connectedBodyStart.gameObject) ||
|
||||||
c.transform.IsChildOf(connectedBodyStart.transform));
|
c.transform.IsChildOf(connectedBodyStart.transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
UnpinNode(0);
|
UnpinNode(0);
|
||||||
connectedBodyStart = null;
|
connectedBodyStart = null;
|
||||||
|
|
||||||
if(Nodes.Count == 0) return;
|
if (Nodes.Count == 0) return;
|
||||||
|
|
||||||
Nodes[0].isPinned = true;
|
Nodes[0].isPinned = true;
|
||||||
|
|
||||||
StartCoroutine(RigibBodyConnectionRoute(connectedRb, localPos, time));
|
StartCoroutine(RigibBodyConnectionRoute(connectedRb, localPos, time));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerator RigibBodyConnectionRoute(Rigidbody connectedRb, Vector3 localPos, float time)
|
private IEnumerator RigibBodyConnectionRoute(Rigidbody connectedRb, Vector3 localPos, float time)
|
||||||
{
|
{
|
||||||
var dist = Vector3.Distance(Nodes[0].position, connectedRb.position + connectedRb.transform.TransformVector(localPos));
|
var dist = Vector3.Distance(Nodes[0].position,
|
||||||
|
connectedRb.position + connectedRb.transform.TransformVector(localPos));
|
||||||
var deltaTime = Time.fixedDeltaTime;
|
var deltaTime = Time.fixedDeltaTime;
|
||||||
var deltaDist = dist;
|
var deltaDist = dist;
|
||||||
if(time > 0.01f)
|
if (time > 0.01f)
|
||||||
deltaDist = deltaTime * (dist / time);
|
deltaDist = deltaTime * (dist / time);
|
||||||
var wait = new WaitForFixedUpdate();
|
var wait = new WaitForFixedUpdate();
|
||||||
while (dist > 0.01f)
|
while (dist > 0.01f)
|
||||||
@@ -757,6 +774,7 @@ namespace AdvancedRope
|
|||||||
connectedRb.position + connectedRb.transform.TransformVector(localPos));
|
connectedRb.position + connectedRb.transform.TransformVector(localPos));
|
||||||
yield return wait;
|
yield return wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedBodyStart = connectedRb;
|
connectedBodyStart = connectedRb;
|
||||||
_ignoreColliders.AddRange(connectedRb.GetComponentsInChildren<Collider>());
|
_ignoreColliders.AddRange(connectedRb.GetComponentsInChildren<Collider>());
|
||||||
PinNode(0, connectedBodyStart.transform, localPos);
|
PinNode(0, connectedBodyStart.transform, localPos);
|
||||||
@@ -772,14 +790,15 @@ namespace AdvancedRope
|
|||||||
if (connectedBodyStart)
|
if (connectedBodyStart)
|
||||||
{
|
{
|
||||||
_ignoreColliders.RemoveAll(c =>
|
_ignoreColliders.RemoveAll(c =>
|
||||||
c&& (c.gameObject == connectedBodyStart.gameObject) ||
|
c && (c.gameObject == connectedBodyStart.gameObject) ||
|
||||||
c.transform.IsChildOf(connectedBodyStart.transform));
|
c.transform.IsChildOf(connectedBodyStart.transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedBodyStart = connectedRb;
|
connectedBodyStart = connectedRb;
|
||||||
_ignoreColliders.AddRange(connectedRb.GetComponentsInChildren<Collider>());
|
_ignoreColliders.AddRange(connectedRb.GetComponentsInChildren<Collider>());
|
||||||
PinNode(0, connectedBodyStart.transform, localPos);
|
PinNode(0, connectedBodyStart.transform, localPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将绳索末端连接到指定刚体。
|
/// 将绳索末端连接到指定刚体。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -790,103 +809,108 @@ namespace AdvancedRope
|
|||||||
if (connectedBodyEnd)
|
if (connectedBodyEnd)
|
||||||
{
|
{
|
||||||
_ignoreColliders.RemoveAll(c =>
|
_ignoreColliders.RemoveAll(c =>
|
||||||
c && (c.gameObject == connectedBodyEnd.gameObject) ||
|
c && (c.gameObject == connectedBodyEnd.gameObject) ||
|
||||||
c.transform.IsChildOf(connectedBodyEnd.transform));
|
c.transform.IsChildOf(connectedBodyEnd.transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedBodyEnd = connectedRb;
|
connectedBodyEnd = connectedRb;
|
||||||
_ignoreColliders.AddRange(connectedRb.GetComponentsInChildren<Collider>());
|
_ignoreColliders.AddRange(connectedRb.GetComponentsInChildren<Collider>());
|
||||||
PinNode(-1, connectedBodyEnd.transform, localPos);
|
PinNode(-1, connectedBodyEnd.transform, localPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 断开绳索起点与刚体的连接。
|
/// 断开绳索起点与刚体的连接。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void DisconnectStartRigidbody()
|
public void DisconnectStartRigidbody()
|
||||||
{
|
{
|
||||||
if (!connectedBodyStart) return;
|
if (!connectedBodyStart) return;
|
||||||
|
|
||||||
_ignoreColliders.RemoveAll(c =>
|
_ignoreColliders.RemoveAll(c =>
|
||||||
c != null && (c.gameObject == connectedBodyStart.gameObject) ||
|
c != null && (c.gameObject == connectedBodyStart.gameObject) ||
|
||||||
c.transform.IsChildOf(connectedBodyStart.transform));
|
c.transform.IsChildOf(connectedBodyStart.transform));
|
||||||
|
|
||||||
connectedBodyStart = null;
|
connectedBodyStart = null;
|
||||||
UnpinNode(0);
|
UnpinNode(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 断开绳索末端与刚体的连接。
|
/// 断开绳索末端与刚体的连接。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void DisconnectEndRigidbody()
|
public void DisconnectEndRigidbody()
|
||||||
{
|
{
|
||||||
if (!connectedBodyEnd) return;
|
if (!connectedBodyEnd) return;
|
||||||
|
|
||||||
_ignoreColliders.RemoveAll(c =>
|
_ignoreColliders.RemoveAll(c =>
|
||||||
c != null && (c.gameObject == connectedBodyEnd.gameObject) ||
|
c != null && (c.gameObject == connectedBodyEnd.gameObject) ||
|
||||||
c.transform.IsChildOf(connectedBodyEnd.transform));
|
c.transform.IsChildOf(connectedBodyEnd.transform));
|
||||||
|
|
||||||
connectedBodyEnd = null;
|
connectedBodyEnd = null;
|
||||||
UnpinNode(-1);
|
UnpinNode(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将当前绳索末端连接到目标绳索的某个节点。
|
/// 将当前绳索末端连接到目标绳索的某个节点。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetRope"></param>
|
/// <param name="targetRope"></param>
|
||||||
/// <param name="targetNodeIndex"></param>
|
/// <param name="targetNodeIndex"></param>
|
||||||
/// <param name="connectionOffset"></param>
|
/// <param name="connectionOffset"></param>
|
||||||
public void ConnectEndToOtherRope(VerletRopeGenerator targetRope, int targetNodeIndex, Vector3 connectionOffset = default)
|
public void ConnectEndToOtherRope(VerletRopeGenerator targetRope, int targetNodeIndex,
|
||||||
|
Vector3 connectionOffset = default)
|
||||||
{
|
{
|
||||||
ConnectToOtherRope(-1, targetRope, targetNodeIndex, connectionOffset);
|
ConnectToOtherRope(-1, targetRope, targetNodeIndex, connectionOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将当前绳索起点连接到目标绳索的某个节点。
|
/// 将当前绳索起点连接到目标绳索的某个节点。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetRope"></param>
|
/// <param name="targetRope"></param>
|
||||||
/// <param name="targetNodeIndex"></param>
|
/// <param name="targetNodeIndex"></param>
|
||||||
/// <param name="connectionOffset"></param>
|
/// <param name="connectionOffset"></param>
|
||||||
public void ConnectStartToOtherRope(VerletRopeGenerator targetRope, int targetNodeIndex, Vector3 connectionOffset = default)
|
public void ConnectStartToOtherRope(VerletRopeGenerator targetRope, int targetNodeIndex,
|
||||||
|
Vector3 connectionOffset = default)
|
||||||
{
|
{
|
||||||
ConnectToOtherRope(0, targetRope, targetNodeIndex, connectionOffset);
|
ConnectToOtherRope(0, targetRope, targetNodeIndex, connectionOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将当前绳索的某个节点连接到目标绳索的某个节点。
|
/// 将当前绳索的某个节点连接到目标绳索的某个节点。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="myNodeIndex">0 表示起点,-1 表示末尾节点</param>
|
/// <param name="myNodeIndex">0 表示起点,-1 表示末尾节点</param>
|
||||||
/// <param name="targetRope"></param>
|
/// <param name="targetRope"></param>
|
||||||
/// <param name="targetNodeIndex">0 表示起点,-1 表示末尾节点</param>
|
/// <param name="targetNodeIndex">0 表示起点,-1 表示末尾节点</param>
|
||||||
public void ConnectToOtherRope(int myNodeIndex, VerletRopeGenerator targetRope, int targetNodeIndex, Vector3 connectionOffset = default)
|
public void ConnectToOtherRope(int myNodeIndex, VerletRopeGenerator targetRope, int targetNodeIndex,
|
||||||
|
Vector3 connectionOffset = default)
|
||||||
{
|
{
|
||||||
if(myNodeIndex < 0)
|
if (myNodeIndex < 0)
|
||||||
myNodeIndex = Nodes.Count + myNodeIndex;
|
myNodeIndex = Nodes.Count + myNodeIndex;
|
||||||
if(targetNodeIndex < 0)
|
if (targetNodeIndex < 0)
|
||||||
targetNodeIndex = targetRope.Nodes.Count + targetNodeIndex;
|
targetNodeIndex = targetRope.Nodes.Count + targetNodeIndex;
|
||||||
if(myNodeIndex < 0 || myNodeIndex >= Nodes.Count) return;
|
if (myNodeIndex < 0 || myNodeIndex >= Nodes.Count) return;
|
||||||
if(!targetRope || targetNodeIndex < 0 || targetNodeIndex >= targetRope.Nodes.Count) return;
|
if (!targetRope || targetNodeIndex < 0 || targetNodeIndex >= targetRope.Nodes.Count) return;
|
||||||
|
|
||||||
var myNode = Nodes[myNodeIndex];
|
var myNode = Nodes[myNodeIndex];
|
||||||
var targetNode = targetRope.Nodes[targetNodeIndex];
|
var targetNode = targetRope.Nodes[targetNodeIndex];
|
||||||
|
|
||||||
if(targetNodeIndex == 0)
|
if (targetNodeIndex == 0)
|
||||||
targetRope.DisconnectStartRigidbody();
|
targetRope.DisconnectStartRigidbody();
|
||||||
if(targetNodeIndex == targetRope.Nodes.Count - 1)
|
if (targetNodeIndex == targetRope.Nodes.Count - 1)
|
||||||
targetRope.DisconnectEndRigidbody();
|
targetRope.DisconnectEndRigidbody();
|
||||||
|
|
||||||
myNode.ConnectToNode(targetNode, connectionOffset);
|
myNode.ConnectToNode(targetNode, connectionOffset);
|
||||||
targetNode.ConnectToNode(myNode, -connectionOffset);
|
targetNode.ConnectToNode(myNode, -connectionOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 断开该节点与其他绳索节点的连接。
|
/// 断开该节点与其他绳索节点的连接。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="myNodeIndex">0 表示起点,-1 表示末尾节点</param>
|
/// <param name="myNodeIndex">0 表示起点,-1 表示末尾节点</param>
|
||||||
public void DisconnectFromOtherRope(int myNodeIndex)
|
public void DisconnectFromOtherRope(int myNodeIndex)
|
||||||
{
|
{
|
||||||
if(myNodeIndex < 0)
|
if (myNodeIndex < 0)
|
||||||
myNodeIndex = Nodes.Count + myNodeIndex;
|
myNodeIndex = Nodes.Count + myNodeIndex;
|
||||||
if(myNodeIndex < 0 || myNodeIndex >= Nodes.Count) return;
|
if (myNodeIndex < 0 || myNodeIndex >= Nodes.Count) return;
|
||||||
|
|
||||||
var myNode = Nodes[myNodeIndex];
|
var myNode = Nodes[myNodeIndex];
|
||||||
var targetNode = myNode.connectedNode;
|
var targetNode = myNode.connectedNode;
|
||||||
myNode.ConnectToNode(null);
|
myNode.ConnectToNode(null);
|
||||||
@@ -910,7 +934,7 @@ namespace AdvancedRope
|
|||||||
{
|
{
|
||||||
var p1 = Nodes[i].position;
|
var p1 = Nodes[i].position;
|
||||||
var p2 = Nodes[i + 1].position;
|
var p2 = Nodes[i + 1].position;
|
||||||
|
|
||||||
var distSqr = SqrDistanceRaySegment(ray, p1, p2, out var rayParam, out var segmentParam);
|
var distSqr = SqrDistanceRaySegment(ray, p1, p2, out var rayParam, out var segmentParam);
|
||||||
|
|
||||||
if (distSqr <= (ropeRadius * ropeRadius) && rayParam < maxDistance && rayParam > 0)
|
if (distSqr <= (ropeRadius * ropeRadius) && rayParam < maxDistance && rayParam > 0)
|
||||||
@@ -918,7 +942,7 @@ namespace AdvancedRope
|
|||||||
if (rayParam < closestDistSqr)
|
if (rayParam < closestDistSqr)
|
||||||
{
|
{
|
||||||
closestDistSqr = rayParam;
|
closestDistSqr = rayParam;
|
||||||
|
|
||||||
hitInfo.DidHit = true;
|
hitInfo.DidHit = true;
|
||||||
hitInfo.Rope = this;
|
hitInfo.Rope = this;
|
||||||
hitInfo.NodeIndex = segmentParam < 0.5f ? i : i + 1;
|
hitInfo.NodeIndex = segmentParam < 0.5f ? i : i + 1;
|
||||||
@@ -926,27 +950,28 @@ namespace AdvancedRope
|
|||||||
hitInfo.Distance = rayParam;
|
hitInfo.Distance = rayParam;
|
||||||
hitInfo.SegmentRatio = segmentParam;
|
hitInfo.SegmentRatio = segmentParam;
|
||||||
hitInfo.Point = ray.GetPoint(rayParam);
|
hitInfo.Point = ray.GetPoint(rayParam);
|
||||||
|
|
||||||
hasHit = true;
|
hasHit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasHit;
|
return hasHit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float SqrDistanceRaySegment(Ray ray, Vector3 s1, Vector3 s2, out float rayParam, out float segmentParam)
|
private static float SqrDistanceRaySegment(Ray ray, Vector3 s1, Vector3 s2, out float rayParam,
|
||||||
|
out float segmentParam)
|
||||||
{
|
{
|
||||||
var u = ray.direction;
|
var u = ray.direction;
|
||||||
var v = s2 - s1;
|
var v = s2 - s1;
|
||||||
var w = ray.origin - s1;
|
var w = ray.origin - s1;
|
||||||
|
|
||||||
var a = Vector3.Dot(u, u);
|
var a = Vector3.Dot(u, u);
|
||||||
var b = Vector3.Dot(u, v);
|
var b = Vector3.Dot(u, v);
|
||||||
var c = Vector3.Dot(v, v);
|
var c = Vector3.Dot(v, v);
|
||||||
var d = Vector3.Dot(u, w);
|
var d = Vector3.Dot(u, w);
|
||||||
var e = Vector3.Dot(v, w);
|
var e = Vector3.Dot(v, w);
|
||||||
|
|
||||||
var denom = a * c - b * b;
|
var denom = a * c - b * b;
|
||||||
float sc, tc;
|
float sc, tc;
|
||||||
|
|
||||||
@@ -960,43 +985,44 @@ namespace AdvancedRope
|
|||||||
sc = (b * e - c * d) / denom;
|
sc = (b * e - c * d) / denom;
|
||||||
tc = (a * e - b * d) / denom;
|
tc = (a * e - b * d) / denom;
|
||||||
}
|
}
|
||||||
|
|
||||||
rayParam = sc;
|
rayParam = sc;
|
||||||
segmentParam = Mathf.Clamp01(tc);
|
segmentParam = Mathf.Clamp01(tc);
|
||||||
|
|
||||||
var closestPointOnSegment = s1 + v * segmentParam;
|
var closestPointOnSegment = s1 + v * segmentParam;
|
||||||
|
|
||||||
var delta = closestPointOnSegment - ray.origin;
|
var delta = closestPointOnSegment - ray.origin;
|
||||||
rayParam = Vector3.Dot(delta, ray.direction);
|
rayParam = Vector3.Dot(delta, ray.direction);
|
||||||
|
|
||||||
if(rayParam < 0f)
|
if (rayParam < 0f)
|
||||||
rayParam = 0f;
|
rayParam = 0f;
|
||||||
|
|
||||||
var closestPointOnRay = ray.GetPoint(rayParam);
|
var closestPointOnRay = ray.GetPoint(rayParam);
|
||||||
|
|
||||||
return (closestPointOnSegment - closestPointOnRay).sqrMagnitude;
|
return (closestPointOnSegment - closestPointOnRay).sqrMagnitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region --- 调试 / 场景可视化 ---
|
#region --- 调试 / 场景可视化 ---
|
||||||
|
|
||||||
private void OnDrawGizmos()
|
private void OnDrawGizmos()
|
||||||
{
|
{
|
||||||
if(Nodes == null || Nodes.Count == 0) return;
|
if (Nodes == null || Nodes.Count == 0) return;
|
||||||
|
|
||||||
Gizmos.color = Color.green;
|
Gizmos.color = Color.green;
|
||||||
for (var i = 0; i < Nodes.Count - 1; i++)
|
for (var i = 0; i < Nodes.Count - 1; i++)
|
||||||
Gizmos.DrawLine(Nodes[i].position, Nodes[i + 1].position);
|
Gizmos.DrawLine(Nodes[i].position, Nodes[i + 1].position);
|
||||||
|
|
||||||
Gizmos.color = Color.red;
|
Gizmos.color = Color.red;
|
||||||
foreach (var node in Nodes)
|
foreach (var node in Nodes)
|
||||||
if(node.isPinned)
|
if (node.isPinned)
|
||||||
Gizmos.DrawWireSphere(node.position, ropeRadius * 1.2f);
|
Gizmos.DrawWireSphere(node.position, ropeRadius * 1.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDrawGizmosSelected()
|
private void OnDrawGizmosSelected()
|
||||||
{
|
{
|
||||||
if(Application.isPlaying) return;
|
if (Application.isPlaying) return;
|
||||||
var startPos = ropeStartPoint;
|
var startPos = ropeStartPoint;
|
||||||
var endPos = ropeEndPoint;
|
var endPos = ropeEndPoint;
|
||||||
|
|
||||||
@@ -1025,7 +1051,7 @@ namespace AdvancedRope
|
|||||||
foreach (var pin in initialPinPoints)
|
foreach (var pin in initialPinPoints)
|
||||||
{
|
{
|
||||||
if (pin.targetTransform == null) continue;
|
if (pin.targetTransform == null) continue;
|
||||||
|
|
||||||
var idx = pin.nodeIndex;
|
var idx = pin.nodeIndex;
|
||||||
if (idx < 0) idx = nodeCount + idx;
|
if (idx < 0) idx = nodeCount + idx;
|
||||||
|
|
||||||
@@ -1067,7 +1093,7 @@ namespace AdvancedRope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1081,4 +1107,4 @@ namespace AdvancedRope
|
|||||||
public float Distance;
|
public float Distance;
|
||||||
public float SegmentRatio;
|
public float SegmentRatio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2
F2RopeLine2.sln.DotSettings.user
Normal file
2
F2RopeLine2.sln.DotSettings.user
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff713e1c3c5f545ce9b1df47230f4f85346ae00_003Fc9_003Ff4df80af_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||||
@@ -24,20 +24,20 @@ EditorUserSettings:
|
|||||||
value: 055550005c055e0e58085a2741720f4415154a7f7b2c22652e794461b7e5376a
|
value: 055550005c055e0e58085a2741720f4415154a7f7b2c22652e794461b7e5376a
|
||||||
flags: 0
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-4:
|
RecentlyUsedSceneGuid-4:
|
||||||
value: 00040c5204025e5d0f59547242710744454e4d7f28717363757c1f63e7b0633c
|
|
||||||
flags: 0
|
|
||||||
RecentlyUsedSceneGuid-5:
|
|
||||||
value: 0050525253025d0b5a0f542748710f4446151a727d2b74652e714862b4b6636d
|
value: 0050525253025d0b5a0f542748710f4446151a727d2b74652e714862b4b6636d
|
||||||
flags: 0
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-6:
|
RecentlyUsedSceneGuid-5:
|
||||||
value: 0206055103530a0908560e21497b0d4410161c2b2a712468757e4461b3e3306a
|
value: 0206055103530a0908560e21497b0d4410161c2b2a712468757e4461b3e3306a
|
||||||
flags: 0
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-7:
|
RecentlyUsedSceneGuid-6:
|
||||||
value: 5100040304005e5a5b0d5875482659441715412b757b74342b781b6ab7e1653b
|
value: 5100040304005e5a5b0d5875482659441715412b757b74342b781b6ab7e1653b
|
||||||
flags: 0
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-8:
|
RecentlyUsedSceneGuid-7:
|
||||||
value: 5005025453535f0f595d597b12225d44174f1a73757f22357f711c30b4e4666a
|
value: 5005025453535f0f595d597b12225d44174f1a73757f22357f711c30b4e4666a
|
||||||
flags: 0
|
flags: 0
|
||||||
|
RecentlyUsedSceneGuid-8:
|
||||||
|
value: 00040c5204025e5d0f59547242710744454e4d7f28717363757c1f63e7b0633c
|
||||||
|
flags: 0
|
||||||
vcSharedLogLevel:
|
vcSharedLogLevel:
|
||||||
value: 0d5e400f0650
|
value: 0d5e400f0650
|
||||||
flags: 0
|
flags: 0
|
||||||
|
|||||||
Reference in New Issue
Block a user