Dev environment: Obi Rope version 6.5.4 / Windows 10 / Unity 2021
Hello, sorry to bother you. I am working on a puzzle game and need to save the data of twisted ropes into a JSON file, then read the JSON during gameplay to restore the ropes to their previous twisted state. ()
I have implemented the save and load functions and can confirm that the saved data is correct because I have tested the vectors' paths of the ropes, and they match what was saved.
However, when I apply the data to the instantiated ropes, I cannot restore the ropes to their previous state. When I use the vectors stored in JSON to restore the positions of the rope's particles, the ropes can never maintain their previous twissted state. The ropes are always spread out or lying there alone.
I found some posts in the forum:
https://obi.virtualmethodstudio.com/foru...-116.html?
The first post from 2017 mentioned frequently calling ObiRopeCursor.ChangeLength() to restore the shape of the ropes, but this method does not work for me, and I am also unable to call the PushDataToSolver method mentioned in the post. (This method only implemented in class ObiStitcher) However, I understand the reason for calling ObiRopeCursor.ChangeLength(). Calling this method can increase the activeParticleCount. The number of actor.activeParticleCount may be much smaller than the number of actor.particleCount, but the number of actor.particleCount is limited by the particleCount in ObiActorBlueprint.
https://obi.virtualmethodstudio.com/foru...4112.html?
The second post was updated in February 2024, which is very recent and provides sample code. The save and load code in the post is very similar to mine, with the only difference being that the loop code during restoration ignores the legality of solver.positions[particlesIndices[i]]. During actual operation, the number of _rope.solverIndices may be much smaller than the total amount of data stored in JSON, resulting in either loss of some data or array out-of-bounds errors.
Here is the code I used to instantiate the rope / saving and restoring the ropes.
Instantiate the rope:
Save the positions of the particles in the rope:
Load the positions of the particles from JSON and set to particles in the rope:
Hello, sorry to bother you. I am working on a puzzle game and need to save the data of twisted ropes into a JSON file, then read the JSON during gameplay to restore the ropes to their previous twisted state. ()
I have implemented the save and load functions and can confirm that the saved data is correct because I have tested the vectors' paths of the ropes, and they match what was saved.
However, when I apply the data to the instantiated ropes, I cannot restore the ropes to their previous state. When I use the vectors stored in JSON to restore the positions of the rope's particles, the ropes can never maintain their previous twissted state. The ropes are always spread out or lying there alone.
I found some posts in the forum:
https://obi.virtualmethodstudio.com/foru...-116.html?
The first post from 2017 mentioned frequently calling ObiRopeCursor.ChangeLength() to restore the shape of the ropes, but this method does not work for me, and I am also unable to call the PushDataToSolver method mentioned in the post. (This method only implemented in class ObiStitcher) However, I understand the reason for calling ObiRopeCursor.ChangeLength(). Calling this method can increase the activeParticleCount. The number of actor.activeParticleCount may be much smaller than the number of actor.particleCount, but the number of actor.particleCount is limited by the particleCount in ObiActorBlueprint.
https://obi.virtualmethodstudio.com/foru...4112.html?
The second post was updated in February 2024, which is very recent and provides sample code. The save and load code in the post is very similar to mine, with the only difference being that the loop code during restoration ignores the legality of solver.positions[particlesIndices[i]]. During actual operation, the number of _rope.solverIndices may be much smaller than the total amount of data stored in JSON, resulting in either loss of some data or array out-of-bounds errors.
Here is the code I used to instantiate the rope / saving and restoring the ropes.
Instantiate the rope:
Code:
private GameObject InstantiateRope(GameObject objectToAttach1, GameObject objectToAttach2)
{
var rpObject = objectPool.GetRopeFromPool();
ObiRope rope = rpObject.GetComponent<ObiRope>();
Transform transformA = objectToAttach1.transform;
Transform transformB = objectToAttach2.transform;
Vector3 PositionA = transformA.position;
Vector3 PositionB = transformB.position;
Vector3 objectScale = transformA.localScale;
Vector3 offset = new Vector3(0, objectScale.y + 0.05f, 0);
Vector3 startPositionLS = transform.InverseTransformPoint(PositionA + offset);
Vector3 endPositionLS = transform.InverseTransformPoint(PositionB + offset);
//Vector3 tangentLS = (endPositionLS - startPositionLS).normalized;
Vector3 tangentLS = Vector3.zero;
ObiRopeBlueprint blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
blueprint.path.Clear();
blueprint.pooledParticles = 100;
// Build the rope path:
int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0);
blueprint.path.AddControlPoint(startPositionLS, -tangentLS, tangentLS, Vector3.up, 1f, 0.1f, ropeThickness, filter,
Color.white, "start");
blueprint.path.AddControlPoint(endPositionLS, -tangentLS, tangentLS, Vector3.up, 1f, 0.1f, ropeThickness, filter,
Color.white, "end");
blueprint.path.FlushEvents();
// Generate particles/constraints:
blueprint.GenerateImmediate();
ObiParticleAttachment attachment1 = rpObject.AddComponent<ObiParticleAttachment>();
ObiParticleAttachment attachment2 = rpObject.AddComponent<ObiParticleAttachment>();
// Set the blueprint:
rope.ropeBlueprint = blueprint;
// Attach both ends:
attachment1.target = transformA;
attachment2.target = transformB;
attachment1.particleGroup = blueprint.groups[0];
attachment2.particleGroup = blueprint.groups.Last();
// Parent the actor under a solver to start the simulation:
rpObject.transform.SetParent(obiSolverObject.transform);
return rpObject;
}
Save the positions of the particles in the rope:
Code:
public JSONObject SaveJson()
{
jsonObject.AddField("ropeLength", gameObject.GetComponent<ObiRope>().restLength);
jsonObject.AddField("particles", GetRopeParticleData());
return jsonObject;
}
JSONObject GetRopeParticleData()
{
var rope = gameObject.GetComponent<ObiRope>();
var solver = rope.solver;
var particlesIndices = rope.solverIndices;
var positions = new List<Vector3>();
for (int i = 0; i < particlesIndices.Length; i++)
{
//if (rope.IsParticleActive(i)) // I was considering save active particles only.
{
positions.Add(solver.positions[particlesIndices[i]]);
}
}
var positionsArray = JSONObject.emptyArray;
for (int i = 0; i < positions.Count; i++)
{
positionsArray.Add((JSONNode)positions[i]);
}
var ropeObject = JSONObject.emptyObject;
ropeObject.AddField("positions", positionsArray);
//ropeObject.AddField("velocities", velocitiesArray); // Velocities are all zero, so I didn't save.
return ropeObject;
}
Load the positions of the particles from JSON and set to particles in the rope:
Code:
public void LoadJson(JSONObject jsonObject)
{
ropeLength = jsonObject["ropeLength"].floatValue;
gameObject.GetComponent<ObiRopeCursor>().ChangeLength(RopeLength);
particleDataToRestore = jsonObject["particles"];
rope.OnBlueprintLoaded += Actor_OnBlueprintLoaded;
}
public void Actor_OnBlueprintLoaded(ObiActor actor, ObiActorBlueprint blueprint)
{
if (particleDataToRestore == null)
return;
var solver = actor.solver;
var particlesIndices = actor.solverIndices;
/*for (int i = 0; i < actor.solverIndices.Length; ++i)
solver.invMasses[actor.solverIndices[i]] = 0;*/
var positionsJsonArray = particleDataToRestore["positions"];
if (positionsJsonArray != null && positionsJsonArray.type == JSONObject.Type.Array)
{
for (int i = 0; i < positionsJsonArray.list.Count; i++)
{
if (i < particlesIndices.Length)
{
solver.positions[particlesIndices[i]] = positionsJsonArray[i].ToVector3();
// actor.ActivateParticle(i);
}
}
}
/*for (int i = 0; i < actor.solverIndices.Length; ++i)
solver.invMasses[actor.solverIndices[i]] = 1f;*/
particleDataToRestore = null;
}