15-05-2026, 03:03 AM
Thanks so much for all the help it is much appreciated. Excellent support here for this asset, as always.
It took some time, but I think I understand what you were suggesting. I implemented it and it seems to be working well.
On the final element of the rope, before moving that last particle, I do the raycast check and if it hits something I reset length.
It took some time, but I think I understand what you were suggesting. I implemented it and it seems to be working well.
On the final element of the rope, before moving that last particle, I do the raycast check and if it hits something I reset length.
Code:
IEnumerator ExtendTetherCR()
{
yield return null; //skip a step. then go.
extending = true;
extended = false;
retracting = false;
retracted = false;
lastParticleCollided = false;
lastParticleAttached = false;
// Procedurally generate the rope path (just a short segment, as we will extend it over time):
int filter = ObiUtils.MakeFilter(collidesWithMask, collisionCategory);
blueprint.path.Clear();
blueprint.path.AddControlPoint(Vector3.zero, Vector3.zero, Vector3.zero, Vector3.up, sectionMass, sectionRotMass, 0.1f, filter, Color.white, "Start");
blueprint.path.AddControlPoint(localDirection * 0.1f, Vector3.zero, Vector3.zero, Vector3.up, sectionMass, sectionRotMass, 0.1f, filter, Color.white, "End");
blueprint.path.FlushEvents();
// Generate the particle representation of the rope (wait until it has finished):
yield return blueprint.Generate();
// Set the blueprint (this adds particles/constraints to the solver and starts simulating them).
rope.ropeBlueprint = blueprint;
yield return new WaitForFixedUpdate();
yield return null;
ObiParticleGroup startPG = rope.ropeBlueprint.groups[0];
shipPA.target = shipAttachment.transform;
shipPA.particleGroup = startPG;
shipPA.attachmentType = ObiParticleAttachment.AttachmentType.Dynamic;
cursor.cursorMu = 0.0f; //cursorMu controls where new particles will be added to or removed from. curosorMu=0 means start of rope, cursorMu=1 means end of rope
cursor.direction = true; //true means we extend from start to end.
Vector3 direction = tetherDirectionHandle.transform.localPosition.normalized;
while (true)
{
Vector3 origin = solver.transform.InverseTransformPoint(rope.transform.position);
float length = 0;
if (!lastParticleCollided)
{
//extend the rope in a straight line by moving the positions of the active particles.
for (int i = 0; i < rope.elements.Count; ++i)
{
int p1 = rope.elements[i].particle1;
int p2 = rope.elements[i].particle2;
solver.prevPositions[p1] = solver.positions[p1] = origin + direction * length;
length += rope.elements[i].restLength;
if(i == rope.elements.Count - 1)
{///before we place the last particle of the last element, check if there is anything in that path using a raycast.
Vector3 rayStart = solver.transform.TransformPoint(solver.positions[p2]);
Vector3 rayEnd = solver.transform.TransformPoint(origin + direction * length);
Vector3 rayDirection = rayEnd - rayStart;
RaycastHit2D hit = Physics2D.Raycast(rayStart, rayDirection, rayDirection.magnitude, collisionMask); //cast the ray and see if it hits anything in the collision mask.
if (hit.collider != null)
{
//Debug.Log("Hit something! hit.collider.name: " + hit.collider.name);
lastParticleCollided = true;
//length needs to be reset to be the distance from the rope's origin to the point the raycast hit something.
length = ( (Vector3)hit.point - rope.transform.position ).magnitude;
if (((1 << hit.collider.gameObject.layer) & attachmentMask) != 0)
{
//Debug.Log("Hit something we can attach to!");
lastParticleAttached = true;
//add a particle attachment, and set it's target to be the collider we hit in the raycast.
otherPA = rope.gameObject.AddComponent<ObiParticleAttachment>();
otherPA.target = hit.collider.transform;
}
}
}
//move the last particle of the rope into position (before attaching it, if applicable).
solver.prevPositions[p2] = solver.positions[p2] = origin + direction * length;
if(lastParticleAttached)
{
ObiParticleGroup endPG = rope.ropeBlueprint.groups[1];
otherPA.particleGroup = endPG;
otherPA.attachmentType = ObiParticleAttachment.AttachmentType.Dynamic;
}
}
}
float distanceLeft = tetherTargetLength - cursor.ChangeLength(tetherShootSpeed * Time.deltaTime);
if (distanceLeft < 0)
{
cursor.ChangeLength(distanceLeft);
break;
}
yield return null;
}
yield return new WaitForFixedUpdate();
yield return null;
extending = false;
extended = true;
retracting = false;
retracted = false;
}