Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
"Stick" rigidbodies to softbody
#1
Hello.

I have a softbody to simulate like Slime where I have used Volume blueprint instead of Surface blueprint.

Now I have a requirement where small spheres kinematic rigidbodies are spread across the scene and as soon as the Softbody collides with small spheres, those spheres should "stick" to slime softbody.

Anyway to do this?

Thanks.
Reply
#2
Depending on how "solid" the sticking must be, you can rely on collision stickinness/friction:
http://obi.virtualmethodstudio.com/tutor...rials.html

Sticky materials this won't result in perfect sticking though, as if the spheres were attached/parented to the softbody particles. Over time they will slide off the softbody.

If you're after attaching, you can use collision callbacks (see:http://obi.virtualmethodstudio.com/tutorials/scriptingcollisions.html)
to detect which particles are colliding with your spheres, then use the particles API (see:http://obi.virtualmethodstudio.com/tutorials/scriptingparticles.html) to move the spheres to a fixed position relative to these particles.
Reply
#3
(07-05-2021, 10:27 AM)josemendez Wrote: Depending on how "solid" the sticking must be, you can rely on collision stickinness/friction:
http://obi.virtualmethodstudio.com/tutor...rials.html

Sticky materials this won't result in perfect sticking though, as if the spheres were attached/parented to the softbody particles. Over time they will slide off the softbody.

If you're after attaching, you can use collision callbacks (see:http://obi.virtualmethodstudio.com/tutorials/scriptingcollisions.html)
to detect which particles are colliding with your spheres, then use the particles API (see:http://obi.virtualmethodstudio.com/tutorials/scriptingparticles.html) to move the spheres to a fixed position relative to these particles.

Thanks for the reply.

Yes I am actually after later attaching part.

I have few questions though.

1. The slime softbody has max of 312 particles. Which means I will be only able to attach max 312 spheres? What if I want to attach over 1000 of spheres anywhere in the body?
2. Will it be performance heavy to move lots of spheres to particles positions every frame?
3. Can Pin Constraint work in this case? If yes, then could you please show any code snippet?

Thanks.
Reply
#4
(07-05-2021, 11:42 AM)Spectra_7 Wrote: 1. The slime softbody has max of 312 particles. Which means I will be only able to attach max 312 spheres? What if I want to attach over 1000 of spheres anywhere in the body?

I don't see why that would be the case. You can attach as many spheres as you want, since you can attach more than 1 sphere per particle. You simply get whatever particle is in contact with the sphere, then store the position of the sphere relative to that particle at collision time, and move it to that position every frame.

(07-05-2021, 11:42 AM)Spectra_7 Wrote: 2. Will it be performance heavy to move lots of spheres to particles positions every frame?

Depends on what's "lots", and depends on how you implement it. Shouldn't really be slow to update a couple hundred, if you do it correctly.

(07-05-2021, 11:42 AM)Spectra_7 Wrote: 3. Can Pin Constraint work in this case? If yes, then could you please show any code snippet?

Yes, but pin constraints are intended for two-way coupling. I doubt that's what you need in this case. It's often easier to just move the spheres to where they should be.

You have snippets for constraint updating/creation in the manual:
http://obi.virtualmethodstudio.com/tutor...aints.html
Reply
#5
Okay I tried what you said.

It did kinda worked what I wanted, but some of the spheres are inside the softbody whereas some are outside floating in air.

Code:
Component contactObject;
if (ObiCollider.idToCollider.TryGetValue(e.contacts.Data[i].other, out contactObject))
{
    if (contactObject != null)
    {
        if (contactObject.gameObject.CompareTag("Glitter"))
        {
            Glitter glitterInstance = contactObject.gameObject.GetComponent<Glitter>();
            if(glitterInstance != null)
            {
                if(!glitterInstance.isAttached)
                {
                    glitterInstance.isAttached = true;
                    glitterInstance.glitterRigidbody.isKinematic = true;
                    glitterInstance.glitterObiCollider.Phase = 1;
                    glitterInstance.particleIndex = e.contacts.Data[i].particle;
                    glitterInstance.particleAttachOffset = slimeActor.GetParticlePosition(glitterInstance.particleIndex) - (Vector3)e.contacts.Data[i].point;

                    if (!glittersList.Contains(glitterInstance))
                        glittersList.Add(glitterInstance);
                }
            }                               
        }
    }
}

private void HandleGlitters()
{
    if (glittersList.Count <= 0)
        return;

    for (int i = 0; i < glittersList.Count; i++)
    {
        var particlePos = slimeActor.GetParticlePosition(glittersList[i].particleIndex);
        glittersList[i].transform.position = particlePos - glittersList[i].particleAttachOffset;
        //glittersList[i].SetPosition(Pos);
    }
}

HandleGlitters() is called in update method.

Sorry I am still noob in coding. Am I doing something wrong here?

I have attached a screenshot.
   
Reply
#6
Objects have both orientation and position, and softbody particles do as well.

If you only store the position, when a particle rotates around any spheres attached to it will not, and they will end up inside the softbody.

What you want is to build a transform matrix using both the position and the orientation (using GetParticleOrientation()), to find your sphere's position in the particle's local space. You need to know what vector spaces are, how to build a basis matrix and convert data between spaces, as this is a basic 3D skill.

You can use Unity's Matrix4x4.TRS() to build a matrix out of a position and orientation.

cheers!
Reply
#7
A coding tip:

Code:
if (!glittersList.Contains(glitterInstance))
     glittersList.Add(glitterInstance);

That will kill performance once you have a few dozen glitter objects. Every time an object contacts a particle, you're iterating over the entire list of all attached glitters to see if it's already there.

This "if (!glittersList.Contains(glitterInstance)" condition is not needed, as you're already checking whether the glitter instance is attached or not (if(!glitterInstance.isAttached)). You can't attach a glitter twice, so no need to check if it's already in the list.
Reply
#8
Hello.

First of all thanks for that coding tip.

I know I am troubling you a lot, but please I really need help regarding Matrix4x4 you mentioned.

Code:
private void HandleGlitters()
{
    if (glittersList.Count <= 0)
        return;

    for (int i = 0; i < glittersList.Count; i++)
    {
        Matrix4x4 particlesMatrix = Matrix4x4.TRS(slimeActor.GetParticlePosition(glittersList[i].particleIndex), slimeActor.GetParticleOrientation(glittersList[i].particleIndex), Vector3.one);

        glittersList[i].transform.position = MatrixUtils.ExtractPosition(particlesMatrix) - glittersList[i].particleAttachOffset;
        glittersList[i].transform.rotation = MatrixUtils.ExtractRotation(particlesMatrix);
    }
}


Is this code correct? Because those small spheres are still floating in air.

This is the MatrixUtils class:

Code:
using UnityEngine;

public static class MatrixUtils
{
    public static Quaternion ExtractRotation(this Matrix4x4 matrix)
    {
        Vector3 forward;
        forward.x = matrix.m02;
        forward.y = matrix.m12;
        forward.z = matrix.m22;

        Vector3 upwards;
        upwards.x = matrix.m01;
        upwards.y = matrix.m11;
        upwards.z = matrix.m21;

        return Quaternion.LookRotation(forward, upwards);
    }

    public static Vector3 ExtractPosition(this Matrix4x4 matrix)
    {
        Vector3 position;
        position.x = matrix.m03;
        position.y = matrix.m13;
        position.z = matrix.m23;
        return position;
    }

    public static Vector3 ExtractScale(this Matrix4x4 matrix)
    {
        Vector3 scale;
        scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
        scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
        scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
        return scale;
    }
}
Reply
#9
(07-05-2021, 04:12 PM)Spectra_7 Wrote: Hello.

First of all thanks for that coding tip.

I know I am troubling you a lot, but please I really need help regarding Matrix4x4 you mentioned.

Code:
private void HandleGlitters()
{
if (glittersList.Count <= 0)
return;

for (int i = 0; i < glittersList.Count; i++)
{
Matrix4x4 particlesMatrix = Matrix4x4.TRS(slimeActor.GetParticlePosition(glittersList[i].particleIndex), slimeActor.GetParticleOrientation(glittersList[i].particleIndex), Vector3.one);

glittersList[i].transform.position = MatrixUtils.ExtractPosition(particlesMatrix) - glittersList[i].particleAttachOffset;
glittersList[i].transform.rotation = MatrixUtils.ExtractRotation(particlesMatrix);
}
}


Is this code correct? Because those small spheres are still floating in air.

This is the MatrixUtils class:

Code:
using UnityEngine;

public static class MatrixUtils
{
    public static Quaternion ExtractRotation(this Matrix4x4 matrix)
    {
        Vector3 forward;
        forward.x = matrix.m02;
        forward.y = matrix.m12;
        forward.z = matrix.m22;

        Vector3 upwards;
        upwards.x = matrix.m01;
        upwards.y = matrix.m11;
        upwards.z = matrix.m21;

        return Quaternion.LookRotation(forward, upwards);
    }

    public static Vector3 ExtractPosition(this Matrix4x4 matrix)
    {
        Vector3 position;
        position.x = matrix.m03;
        position.y = matrix.m13;
        position.z = matrix.m23;
        return position;
    }

    public static Vector3 ExtractScale(this Matrix4x4 matrix)
    {
        Vector3 scale;
        scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
        scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
        scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
        return scale;
    }
}

This script does exactly the same as your previous one, but in a more convoluted way: you’re storing the position in a matrix, then immediately extracting and applying it. So the result is the same as before.

Also, you’re applying the particle rotation to your sphere directly, which will make no differente since a sphere rotating around its center looks as if was not rotating at all.

You need to express the position of your sphere in the particle’s local space. Read about local/world space and basis vectors/matrices, this is a fundamental part of making games that you will use very often.

On monday I will write a small sample script for you. cheers!
Reply
#10
(07-05-2021, 04:26 PM)josemendez Wrote: This script does exactly the same as your previous one, but in a more convoluted way: you’re storing the position in a matrix, then immediately extracting and applying it. So the result is the same as before.

Also, you’re applying the particle rotation to your sphere directly, which will make no differente since a sphere rotating around its center looks as if was not rotating at all.

You need to express the position of your sphere in the particle’s local space. Read about local/world space and basis vectors/matrices, this is a fundamental part of making games that you will use very often.

On monday I will write a small sample script for you. cheers!


Okay thanks a lot. Meanwhile I will also try to figure it out.
Reply