Obi Official Forum
Help Custom Force to Match Object - Printable Version

+- Obi Official Forum (https://obi.virtualmethodstudio.com/forum)
+-- Forum: Obi Users Category (https://obi.virtualmethodstudio.com/forum/forum-1.html)
+--- Forum: Obi Rope (https://obi.virtualmethodstudio.com/forum/forum-4.html)
+--- Thread: Help Custom Force to Match Object (/thread-3623.html)



Custom Force to Match Object - docgonzzo - 25-10-2022

Hi folks,

I'm working on a top-down 2D game where the player navigates around objects that attract/pull the player affecting their path through the level. The player has an Obi Rope (and Solver) attached to him, kind of like a long tail.

The Physics 2D gravity (in project settings) and the Solver.gravity are set to zero. I have set-up an attraction system using Rigidbody2D.AddForce() which attracts the player towards the different "attraction objects", depending on distance.

I set all the rope's CP masses to 0. This is because as the rope moved/waved around, it was affecting the character's center of mass, making the control difficult.

When the character collides with an attraction object, he naturally sticks there due to the force of attraction.  The rope currently just drifts/floats around when the character is stuck to an attraction object, as expected in zero gravity and with zero-mass.

I need the rope to also stick to this attracting object with the same force the character is attracted.


What do you recommend I do to get this working? Could I increase the mass of the particles (or CPs) directly at runtime when the character is in contact with an attraction object? Should I use the Solver.gravity or Solver.externalForces to control this?

Also, what's the difference between using Solver.gravity and Solver.externalForces from the perspective of a particle?


RE: Custom Force to Match Object - josemendez - 25-10-2022

(25-10-2022, 12:31 AM)docgonzzo Wrote: I set all the rope's CP masses to 0. This is because as the rope moved/waved around, it was affecting the character's center of mass, making the control difficult.

You could enable the rigidbody's "kinematic for particles" checkbox. This will ensure the rigidbody is unaffected by Obi particles, but still allows the particles to have any mass and be affected by forces (and other rigidbodies that have this setting disabled). See:
http://obi.virtualmethodstudio.com/manual/6.3/collisions.html

(25-10-2022, 12:31 AM)docgonzzo Wrote: I need the rope to also stick to this attracting object with the same force the character is attracted. Should I use the Solver.gravity or Solver.externalForces to control this?

This is an external sticking force, so using externalForces seems like the way to go.

(25-10-2022, 12:31 AM)docgonzzo Wrote: Also, what's the difference between using Solver.gravity and Solver.externalForces from the perspective of a particle?

Same as in the real world: gravity is not a force, but an acceleration. Force = acceleration x mass. The same force will have less effect on heavier objects, but gravity affects all objects equally regardless of their mass.

kind regards,


RE: Custom Force to Match Object - docgonzzo - 26-10-2022

Thanks for the response! Excellent support here as usual Interesante







I was wondering if you could please post a good example of how to use the solver externalForces array. I tried the code below, but this is throwing a NullReferenceException runtime error on the for loop line.



Code:
    public ObiRope myRope; //this is assigned in Start()

    private void FixedUpdate()
    {

        for (int i = 0; i < myRope.solverIndices.Length; ++i)
            myRope.solver.externalForces[i] = myForce;

    }








In fact, this experiment below produced both a NullReferenceException runtime error, and the actual length of the array in the console Confundido




Code:
    private void FixedUpdate()
    {

        Debug.Log("myRope.solverIndices.Length: " + myRope.solverIndices.Length);



    }

NullReferenceException: Object reference not set to an instance of an object
ObiRopePlanetGravity.FixedUpdate () (at Assets/ObiRopePlanetGravity.cs:55)


myRope.solverIndices.Length: 10
UnityEngine.Debug:Log (object)
ObiRopePlanetGravity:FixedUpdate () (at Assets/ObiRopePlanetGravity.cs:55)



RE: Custom Force to Match Object - josemendez - 26-10-2022

(26-10-2022, 12:22 AM)docgonzzo Wrote: I was wondering if you could please post a good example of how to use the solver externalForces array. I tried the code below, but this is throwing a NullReferenceException runtime error on the for loop line.
Code:
    public ObiRope myRope; //this is assigned in Start()

    private void FixedUpdate()
    {

        for (int i = 0; i < myRope.solverIndices.Length; ++i)
            myRope.solver.externalForces[i] = myForce;

    }

The manual contains examples of accessing per-particle properties for any actor:
http://obi.virtualmethodstudio.com/manual/6.3/scriptingparticles.html

Your code does not check whether the rope is null (could potentially be if you're manually assigning it from some other script) or if it has been loaded or not. So add this at the beginning of FixedUpdate():

Code:
if (myRope == null || !myRope.isLoaded)
return;

Also "myRope.solverIndices" and "myRope.solver.externalForces" don't have the same length, so naturally you'll get an out of bounds access exception accessing them that way.
All data arrays in the solver (positions, velocities, externalForces, invMasses, etc) have one entry per particle loaded in the solver, if there's multiple ropes/actors loaded then they'll have many more entries than particles in each individual rope. The index at which data for each particle in stored in the solver arrays is provided by the rope.solverIndices array:

Code:
for (int i = 0; i < myRope.solverIndices.Length; ++i){

// retrieve index of the particle in the solver:
int solverIndex = myRope.solverIndices[i];

// set the external force of this particle:
myRope.solver.externalForces[solverIndex] = myForce;
}

See: http://obi.virtualmethodstudio.com/manual/6.3/scriptingparticles.html
"Actor particle indices run from 0 to the amount of particles in that actor. However, since particle data for an actor might be scattered across the solver arrays (see architecture), you need to map from actor index to solver index. You can map from actor particle indices to solver particle indices using the actor's solverIndices array."

Also note that generally, you want to add forces instead of forcing the force to a specific value:
Quote:myRope.solver.externalForces[solverIndex] += myForce;

If you just set the force to a value, your script won't work well with other scripts that might add additional forces to the particles. For instance if you have a directional force script and a gravity well script, the one that gets executed last will overwrite the forces set by the first one.


(26-10-2022, 12:22 AM)docgonzzo Wrote: In fact, this experiment below produced both a NullReferenceException runtime error, and the actual length of the array in the console Confundido

Code:
    private void FixedUpdate()
    {

        Debug.Log("myRope.solverIndices.Length: " + myRope.solverIndices.Length);

    }

NullReferenceException: Object reference not set to an instance of an object
ObiRopePlanetGravity.FixedUpdate () (at Assets/ObiRopePlanetGravity.cs:55)


myRope.solverIndices.Length: 10
UnityEngine.Debug:Log (object)
ObiRopePlanetGravity:FixedUpdate () (at Assets/ObiRopePlanetGravity.cs:55)

Nothing weird about this: NullRefException on the first frame because the rope isn't loaded yet, then correct length on subsequent frames. Thing is you're not checking whether the rope is loaded into the solver yet. If it isn't, the solverIndices array will be null since particles are not included in any solver. You can check whether the rope is loaded like this:

Code:
if (rope.isLoaded){
// do something with the particles
}

As a side note, you might want to use the solver's callbacks instead of FixedUpdate. This is important because during a single FixedUpdate(), Obi's physics engine will be updated more than once if you're using more than 1 substep. See: http://obi.virtualmethodstudio.com/manual/6.3/scriptingsolver.html

let me know if you need further help,

cheers


RE: Custom Force to Match Object - docgonzzo - 27-10-2022

Thanks again!

I am now successfully using externalForces to apply a force to my rope's particles. The missing piece for me was that the mapping the actor particle indices to the solver particle indices.

Now externalForces appears to be acting in local space rather than world space. I must make the vector adjustments manually to convert to world space forces. No big deal Guiño


RE: Custom Force to Match Object - josemendez - 27-10-2022

(27-10-2022, 12:06 AM)docgonzzo Wrote: Now externalForces appears to be acting in local space rather than world space. I must make the vector adjustments manually to convert to world space forces. No big deal Guiño

You're welcome! Sonrisa

Yes, all particle data is expressed in the solver's local space, since simulation is also performed in local space. See:
http://obi.virtualmethodstudio.com/manual/6.3/scriptingparticles.html

Quote:Each ObiSolver has an array for each one of these properties, that stores the current data for all particles in use by any actor managed by the solver. All spatial properties (positions, orientations, velocities, vorticities, etc) are expressed in the solver's local space.