Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ObiColliders and Procedural Load
#5
Here's the sample script:

Code:
using System;
using System.Collections;
using UnityEngine;
using Obi;

public class RuntimeRopeGenerator
{
    private ObiRope rope;
    private ObiRopeCursor cursor;
    private ObiSolver solver;

    /// <summary>
    /// Creates a straight rope anchored to a transform at the top.
    /// Transform may or may not move around and may or may not have a rigidbody.
    /// When you call this the rope will appear in the scene and immediately interact with gravity and objects with ObiColliders.
    /// Called from anywhere (main thread only)
    /// Called when there is an empty scene (aside from passed transform) with nothing loaded and no prefabs available.
    /// </summary>
    public IEnumerator MakeRope(Transform anchoredTo, Vector3 attachmentOffset, float ropeLength)
    {
        // create a new GameObject with the required components: a solver, a rope, and a curve.
        // we also throw a cursor in to be able to change its length.
        GameObject ropeObject = new GameObject("rope",typeof(ObiSolver),
                                                      typeof(ObiRope),
                                                      typeof(ObiCatmullRomCurve),
                                                      typeof (ObiRopeCursor));

        // get references to all components:
        rope                     = ropeObject.GetComponent<ObiRope>();
        cursor                     = ropeObject.GetComponent<ObiRopeCursor>();
        solver                     = ropeObject.GetComponent<ObiSolver>();
        ObiCatmullRomCurve path = ropeObject.GetComponent<ObiCatmullRomCurve>();

        // set up component references (see ObiRopeHelper.cs)
        rope.Solver = solver;
        rope.ropePath = path;    
        rope.section = Resources.Load<ObiRopeSection>("DefaultRopeSection");
        cursor.rope = rope;

        // set path control points (duplicate end points, to set curvature as required by CatmullRom splines):
        path.controlPoints.Clear();
        path.controlPoints.Add(Vector3.zero);
        path.controlPoints.Add(Vector3.zero);
        path.controlPoints.Add(Vector3.down*ropeLength);
        path.controlPoints.Add(Vector3.down*ropeLength);

        // parent the rope to the anchor transform:
        rope.transform.SetParent(anchoredTo,false);
        rope.transform.localPosition = attachmentOffset;

        // generate particles/constraints and add them to the solver (see ObiRopeHelper.cs)
        yield return rope.StartCoroutine(rope.GeneratePhysicRepresentationForMesh());
        rope.AddToSolver(null);

        // fix first particle in place (see http://obi.virtualmethodstudio.com/tutorials/scriptingparticles.html)
        rope.invMasses[0] = 0;
        Oni.SetParticleInverseMasses(solver.OniSolver,new float[]{0},1,rope.particleIndices[0]);
    }

    /// <summary>
    /// make_rope and add_pendulum may NOT be called on the same frame. You must wait for the make_rope coroutine to finish first, as creating a rope is an asynchronous operation.
    /// Just adds a pendulum to the rope on the un-anchored end.
    /// </summary>
    public void AddPendulum(ObiCollider pendulum, Vector3 attachmentOffset)
    {
        // simply add a new pin constraint (see http://obi.virtualmethodstudio.com/tutorials/scriptingconstraints.html)
        rope.PinConstraints.RemoveFromSolver(null);
        ObiPinConstraintBatch batch = (ObiPinConstraintBatch)rope.PinConstraints.GetBatches()[0];
        batch.AddConstraint(rope.UsedParticles-1, pendulum, attachmentOffset, 1);
        rope.PinConstraints.AddToSolver(null);
    }

    /// <summary>
    /// remove_pendulum and add_pendulum may be called on the same frame.
    /// </summary>
    public void RemovePendulum()
    {
        // simply remove all pin constraints (see http://obi.virtualmethodstudio.com/tutorials/scriptingconstraints.html)
        rope.PinConstraints.RemoveFromSolver(null);
        rope.PinConstraints.GetBatches()[0].Clear();
        rope.PinConstraints.AddToSolver(null);
    }

    /// <summary>
    /// Like extending or retracting a winch.
    /// May need to include changes to the iteration count,
    /// tether setup, or rope weight to prevent rope stretching depending on length and attached mass.
    /// Further explanation: there's no way to correlate iteration count or weight to length in order to maintain constant stiffness (not in Obi, nor any existing engine). You could create a single
    /// tether constraint joining the first and last particle in the rope, and set its length to be rope.RestLenght. This is a common workaround in many physics engines.
    /// </summary>
    public void ChangeRopeLength(float changeAmount)
    {
        // the cursor will automatically add/remove/modify constraints and particles as needed to obtain the new length.
        cursor.ChangeLength(rope.RestLength + changeAmount);
    }
}

And a minimal example on how to use it:

Code:
using System;
using System.Collections;
using UnityEngine;
using Obi;

public class RuntimeRopeGeneratorUse : MonoBehaviour
{
    public ObiCollider pendulum;
    RuntimeRopeGenerator rg;

    public IEnumerator Start()
    {
        rg = new RuntimeRopeGenerator();

        // Create a rope:
        yield return rg.MakeRope(transform,Vector3.zero,1);

        // Add a pendulum (you should adjust the attachment point depending on your particular pendulum object)
        rg.AddPendulum(pendulum,Vector3.up*0.5f);
    }

    public void Update(){
    
        if (Input.GetKey(KeyCode.W)){
            rg.ChangeRopeLength(- Time.deltaTime);
        }

        if (Input.GetKey(KeyCode.S)){
            rg.ChangeRopeLength(  Time.deltaTime);
        }
        
    }
}

Everything is created from scratch. No pre-existing rope, solver, or anything is needed, an empty scene will do fine. Most of the code is copy-pasted from the scripting section of the manual or the ObiRopeHelper class, the only real difference is that the rope and its components are created using new GameObject() instead of being public variables. Also I´ve made a few assumptions: the rope is completely straight, looks down the Y axis, and has standard resolution and thickness. Also I've added no material to its renderer, so it will be pink.

To test it just add an empty GameObject to your scene, add the RuntimeRopeGeneratorUse component to it and press Play. Optionally, provide a ObiCollider object to act as the pendulum. Note that this collider can also be created at runtime, by simply calling new GameObject() with the appropriate parameters.

I've commented both scripts to make things simpler to understand. Note that creating a rope is a potentially heavy operation (it all depends on your rope length and resolution!) so rope.GeneratePhysicRepresentationForMesh() is a coroutine to avoid stalling your game. This means that you cannot make a rope and begin using it in the same frame, you must use the yield instruction to wait until it has been created. Please note that if calling MakeRope() and other methods in the same frame is a requirement, you can just advance the coroutine manually until it is done by calling MoveNext.

You can call AddPendulum(), RemovePendulum() and ChangeLength() multiple times per frame, as often as you wish and in any order. By default the rope will collide with all ObiColliders present in the scene, as long as they are in the "Default" layer.

cheers,

Edit: quick and dirty video showing the result (apologies for the quality). I use the W and S keys to lengthen/shorten the rope. Keep in mind that the maximum length of the rope is determined by the amount of particles in the particle pool (see http://obi.virtualmethodstudio.com/tutor...eters.html). Once no more particles are available in the pool, the rope will stop increasing its length.
Reply


Messages In This Thread
ObiColliders and Procedural Load - by jonworks - 18-01-2018, 07:03 AM
RE: ObiColliders and Procedural Load - by josemendez - 01-02-2018, 10:31 PM