Hello,
I am trying to figure out a way to have multiple pin points on my rope so I can move them like a snake.
I am unable to figure out what may cause this bug, my rope is floating on the non-kinematic end. For context, the selected object is a kinematic pin on my rope, the other golden balls are non-kinematic obi rigid bodies.
I am providing my solver settings alongside my result in the attachment.
Here's the code snippet I used to create the rope:
(It is messy but I use it for having pre-existing control points in the scene, and creating a rope blueprint and a rope out of these existing control points called "snake pieces")
I am trying to figure out a way to have multiple pin points on my rope so I can move them like a snake.
I am unable to figure out what may cause this bug, my rope is floating on the non-kinematic end. For context, the selected object is a kinematic pin on my rope, the other golden balls are non-kinematic obi rigid bodies.
I am providing my solver settings alongside my result in the attachment.
Here's the code snippet I used to create the rope:
(It is messy but I use it for having pre-existing control points in the scene, and creating a rope blueprint and a rope out of these existing control points called "snake pieces")
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using _Game.Scripts.GameplayElements.Snakes;
using Obi;
using UnityEngine;
namespace _Game.Scripts.Gameplay.Demo
{
public class DemoSnake : MonoBehaviour
{
[SerializeField] private List<SnakePiece> snakePieces;
[SerializeField] private ObiSolver solver;
[SerializeField] private Material ropeMaterial;
[SerializeField] private ObiRopeSection ropeSection;
[SerializeField] private ObiCollisionMaterial collisionMaterial;
[SerializeField] private float ropeThickness;
[SerializeField] private ObiRope rope;
private ObiCollider[] Colliders => snakePieces.Select(o => o.GetComponent<ObiCollider>()).ToArray();
private void Start()
{
CreateSnake();
}
private void CreateSnake()
{
rope = CreateRopeProcedurally(this, Colliders, 0);
PinRope(rope, Colliders);
}
public void PinRope(ObiRope rope, params ObiCollider[] colliders)
{
PinRopeEnds(rope, colliders);
for (int i = 1; i < colliders.Length-1; i++)
{
var obiCollider = colliders[i];
var attachment = rope.gameObject.AddComponent<ObiParticleAttachment>();
attachment.attachmentType = ObiParticleAttachment.AttachmentType.Dynamic;
attachment.target = obiCollider.transform;
attachment.particleGroup = rope.ropeBlueprint.groups[i];
attachment.compliance = 0F;
}
}
public void PinRopeEnds(ObiRope rope, params ObiCollider[] colliders)
{
var colliderStart = colliders[0];
var colliderEnd = colliders[^1];
var startIndex = rope.ropeBlueprint.groups.FindIndex(g => g.name == "start");
var endIndex = rope.ropeBlueprint.groups.FindIndex(g => g.name == "end");
var attachmentStart = rope.gameObject.AddComponent<ObiParticleAttachment>();
attachmentStart.attachmentType = ObiParticleAttachment.AttachmentType.Dynamic;
attachmentStart.target = colliderStart.transform;
attachmentStart.particleGroup = rope.ropeBlueprint.groups[startIndex];
var attachmentEnd = rope.gameObject.AddComponent<ObiParticleAttachment>();
attachmentEnd.attachmentType = ObiParticleAttachment.AttachmentType.Dynamic;
attachmentEnd.target = colliderEnd.transform;
attachmentEnd.particleGroup = rope.ropeBlueprint.groups[endIndex];
}
public ObiRope CreateRopeProcedurally(DemoSnake snake, ObiCollider[] controlPoints, int index = 0)
{
var ropeObject = new GameObject($"Rope_{index}");
ropeObject.transform.parent = solver.transform;
var rope = ropeObject.AddComponent<ObiRope>();
var blueprint = CreateBlueprint(snake, controlPoints,out var length);
rope.ropeBlueprint = blueprint;
rope.distanceConstraintsEnabled = true;
rope.stretchingScale = 0.5f;
rope.surfaceCollisions = false;
rope.collisionMaterial = collisionMaterial;
rope.maxBending = 0.5f;
rope.bendCompliance = 1f;
rope.selfCollisions = true;
var ropeRenderer = ropeObject.AddComponent<ObiRopeExtrudedRenderer>();
ropeRenderer.material = ropeMaterial;
ropeRenderer.section = ropeSection;
var smoother = rope.GetComponent<ObiPathSmoother>();
smoother.decimation = 0.1f;
smoother.smoothing = 1;
var ropeReel = ropeObject.AddComponent<ObiRopeReel>();
ropeReel.enabled = false;
ropeReel.outThreshold = 0.08f;
ropeReel.inThreshold = 0.05f;
ropeReel.outSpeed = 6f;
ropeReel.inSpeed = 0.1f;
ropeReel.maxLength = Mathf.Max(1f, length);
var cursor = ropeObject.GetComponent<ObiRopeCursor>();
cursor.cursorMu = 0.05f;
cursor.sourceMu = 0.1f;
return rope;
}
public ObiRopeBlueprint CreateBlueprint(DemoSnake snake, ObiCollider[] points, out float distance)
{
distance = 0;
//Create the blueprint
var blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
blueprint.resolution = 1f;
blueprint.thickness = 0.03f;
blueprint.pooledParticles = 0;
//Convert ropePoints to controlPoints
var controlPoints = new Vector3[points.Length];
blueprint.path.Clear();
blueprint.ClearParticleGroups();
var placeholder = Vector3.back * 1000;
for (int i = 0; i < controlPoints.Length; i++)
{
var point = points[i];
var controlPoint = snake.transform.InverseTransformPoint(point.transform.position);
controlPoints[i] = controlPoint;
Vector3 previousPoint =
i <= 0
? placeholder
: controlPoints[i - 1];
Vector3 nextPoint =
i >= controlPoints.Length - 1
? placeholder
: controlPoints[i + 1];
previousPoint = previousPoint != placeholder
? previousPoint
: controlPoint;
nextPoint = nextPoint != placeholder
? nextPoint
: controlPoint;
if(i != 0) distance += Vector3.Distance(previousPoint, controlPoint);
var inTangentVector = -(controlPoint - previousPoint) * 0.02f;
var outTangentVector = (nextPoint - controlPoint) * 0.02f;
blueprint.path.AddControlPoint(controlPoint, inTangentVector, outTangentVector,
Vector3.up, 0.1f, 0.1f, (i == 0 || i == controlPoints.Length-1 ? ropeThickness*0.8f : ropeThickness), 1, Color.white,
//i.ToString());
$"{(i == 0 ? "start" : i == controlPoints.Length - 1 ? "end" : i.ToString())}");
blueprint.path.filters[i] = ObiUtils.MakeFilter(-1, 0);
}
blueprint.path.FlushEvents();
return blueprint;
}
}
}