Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Controlling specific particle force / custom constraints.
#1
Hello.

I want to create the 3D Snake character. The closest example is the game Snake Pass player character.
https://www.youtube.com/watch?v=7ZwiRoA-kns

I thinking about using Obi Rope as the tools to create Snake character and want to ask if it's a good way to solve this problem.

Particular questions that bother me:
1. Can particles be controlled by me directly?
2. Can custom constraints be added?
3. Can parameters of an actor be tweaked at runtime?

I didn't buy obi rope still, so i want to decide if this asset can help me and then buy it.

Gladly accept any advices or opinions on where it is a good idea to use obi rope in this case or not.

Thank you.
Reply
#2
1.- Yes. You can get/set any particle property at runtime (positions, velocities, mass, etc). See: http://obi.virtualmethodstudio.com/tutor...icles.html

2.- Yes, but only on the velocity level. There's no API to create custom position-based constraints as of now. You can only add/remove constraints of the existing types.

3.- Yes, all actor/solver parameters can be changed at runtime. You can even generate actors programmatically: http://obi.virtualmethodstudio.com/tutor...ctors.html

Obi is a quite large and complex engine. Before buying, I'd recommend taking a look at the manual and the FAQ:

http://obi.virtualmethodstudio.com/tutorials/
http://obi.virtualmethodstudio.com/faq.html

Let me know if I can answer any more questions. cheers!
Reply
#3
(22-04-2021, 11:47 AM)josemendez Wrote: 1.- Yes. You can get/set any particle property at runtime (positions, velocities, mass, etc). See: http://obi.virtualmethodstudio.com/tutor...icles.html

2.- Yes, but only on the velocity level. There's no API to create custom position-based constraints as of now. You can only add/remove constraints of the existing types.

3.- Yes, all actor/solver parameters can be changed at runtime. You can even generate actors programmatically: http://obi.virtualmethodstudio.com/tutor...ctors.html

Thank you for your reply!

How you think, is it a good idea to use Obi Rope to create Snake Pass like character? 

I think it can be problems with something like muscles, because Snake is something between rod and rope, because it can try to keep shape, but despite rod - any shape. 

Maybe I'm wrong and all these problems can be solved using Obi Rope?
Reply
#4
(22-04-2021, 11:52 AM)rastleks Wrote: Thank you for your reply!

How you think, is it a good idea to use Obi Rope to create Snake Pass like character? 

I think it can be problems with something like muscles, because Snake is something between rod and rope, because it can try to keep shape, but despite rod - any shape. 

Maybe I'm wrong and all these problems can be solved using Obi Rope?

Judging from the videos, you could use a rod to do this. Note that rods do not have to be rigid. You can control their bending, twisting, shearing stiffness and plasticity. Think of them as a "more powerful" rope, however they can also behave like rope.

The "snake-like" behavior in the video probably comes from imposing a custom constraint on particles: they have to follow the path of the previous particle in the body, up to some percentage. At 0% they behave freely, like a rope. At 100%, they follow the exact path taken by the snake's head. You can implement this by adjusting particle velocities.
Reply
#5
(22-04-2021, 12:00 PM)josemendez Wrote: Judging from the videos, you could use a rod to do this. Note that rods do not have to be rigid. You can control their bending, twisting, shearing stiffness and plasticity. Think of them as a "more powerful" rope, however they can also behave like rope.

The "snake-like" behavior in the video probably comes from imposing a custom constraint on particles: they have to follow the path of the previous particle in the body, up to some percentage. At 0% they behave freely, like a rope. At 100%, they follow the exact path taken by the snake's head. You can implement this by adjusting particle velocities.

Thank you very much. 

Documentation says that Rods can't change length in runtime. But as we all know, any snake want to eat something and grow larger Sonrisa May be there is some way to implement this using Rod?
Reply
#6
(22-04-2021, 12:23 PM)rastleks Wrote: Thank you very much. 

Documentation says that Rods can't change length in runtime. But as we all know, any snake want to eat something and grow larger Sonrisa May be there is some way to implement this using Rod?

Unfortunately not :/. Rods can't change their length, because unlike ropes they contain a special constraint that encompasses all particles in it. Changing the length would be too costly.
Reply
#7
(22-04-2021, 12:27 PM)josemendez Wrote: Unfortunately not :/. Rods can't change their length, because unlike ropes they contain a special constraint that encompasses all particles in it. Changing the length would be too costly.

Thank you very much for your answers!
Reply
#8
You're welcome Sonrisa
Reply
#9
Hi!

Got my curiosity piqued, so I implemented the basic mechanics of the game using Obi. Will post a video and some code tomorrow. cheers!
Reply
#10
Here's my version of it using ropes (instead of rods). Haven't played Snake Pass though, only seen videos, so I'm not sure if this mimics the gameplay feel close enough. It's kinda tricky to move around as a snake but I guess that's the challenge Sonrisa

You can move the head using WASD, lift up the head using Space, and slither (accelerate) pressing J:



Here's the full snake controller code:

Code:
using UnityEngine;
using Obi;

public class SnakeController : MonoBehaviour
{
    public Transform headReferenceFrame;
    public float headSpeed = 20;
    public float upSpeed = 40;
    public float slitherSpeed = 10;

    private ObiRope rope;
    private ObiSolver solver;
    private float[] traction;
    private Vector3[] surfaceNormal;

    private void Start()
    {
        rope = GetComponent<ObiRope>();
        solver = rope.solver;

        // initialize traction array:
        traction = new float[rope.activeParticleCount];
        surfaceNormal = new Vector3[rope.activeParticleCount];

        // subscribe to solver events (to update surface information)
        solver.OnBeginStep += ResetSurfaceInfo;
        solver.OnCollision += AnalyzeContacts;
        solver.OnParticleCollision += AnalyzeContacts;
    }


    private void OnDestroy()
    {
        solver.OnBeginStep -= ResetSurfaceInfo;
        solver.OnCollision -= AnalyzeContacts;
        solver.OnParticleCollision -= AnalyzeContacts;
    }

    private void ResetSurfaceInfo(ObiSolver s, float stepTime)
    {
        // reset surface info:
        for (int i = 0; i < traction.Length; ++i)
        {
            traction[i] = 0;
            surfaceNormal[i] = Vector3.zero;
        }
    }

    private void AnalyzeContacts(object sender, ObiSolver.ObiCollisionEventArgs e)
    {
        // iterate trough all contacts:
        for (int i = 0; i < e.contacts.Count; ++i)
        {
            var contact = e.contacts.Data[i];
            if (contact.distance < 0.005f)
            {
                int simplexIndex = solver.simplices[contact.bodyA];
                var particleInActor = solver.particleToActor[simplexIndex];

                // using 1 here, could calculate a traction value based on the type of terrain, friction, etc.
                traction[particleInActor.indexInActor] = 1;

                // accumulate surface normal:
                surfaceNormal[particleInActor.indexInActor] += (Vector3)contact.normal;
            }
        }
    }

    public void Update()
    {
        if (Input.GetKey(KeyCode.J))
        {
            for (int i = 1; i < rope.activeParticleCount; ++i)
            {
                int solverIndex = rope.solverIndices[i];
                int prevSolverIndex = rope.solverIndices[i - 1];

                // direction from current particle to previous one, projected on the contact surface:
                Vector4 dir = Vector3.ProjectOnPlane(solver.positions[prevSolverIndex] - solver.positions[solverIndex], surfaceNormal[i]).normalized;

                // move in that direction:
                solver.velocities[solverIndex] += dir * traction[i] * slitherSpeed * Time.deltaTime;
            }
        }

        int headIndex = rope.solverIndices[0];

        if (headReferenceFrame != null)
        {
            Vector3 direction = Vector3.zero;

            // Determine movement direction:
            if (Input.GetKey(KeyCode.W))
            {
                direction += headReferenceFrame.forward * headSpeed;
            }
            if (Input.GetKey(KeyCode.A))
            {
                direction += -headReferenceFrame.right * headSpeed;
            }
            if (Input.GetKey(KeyCode.S))
            {
                direction += -headReferenceFrame.forward * headSpeed;
            }
            if (Input.GetKey(KeyCode.D))
            {
                direction += headReferenceFrame.right * headSpeed;
            }

            // flatten out the direction so that it's parallel to the ground:
            direction.y = 0;

            solver.velocities[headIndex] += (Vector4)direction * Time.deltaTime;
        }

        if (Input.GetKey(KeyCode.Space))
            solver.velocities[headIndex] += (Vector4)Vector3.up * Time.deltaTime * upSpeed;
    }
}

This is the interesting bit:

Code:
if (Input.GetKey(KeyCode.J))
{
        for (int i = 1; i < rope.activeParticleCount; ++i)
        {
                int solverIndex = rope.solverIndices[i];
                int prevSolverIndex = rope.solverIndices[i - 1];

                // direction from current particle to previous one, projected on the contact surface:
                Vector4 dir = Vector3.ProjectOnPlane(solver.positions[prevSolverIndex] - solver.positions[solverIndex], surfaceNormal[i]).normalized;

                // move in that direction:
                solver.velocities[solverIndex] += dir * traction[i] * slitherSpeed * Time.deltaTime;
        }
}

By moving each particle in the direction of the previous one in the snake's body, the typical slithery-snake movement is achieved. That's all there is to it.

Only particles that are in contact with something can slither, though. This is achieved by storing a "traction" value for each particle. Particles in contact with something have a traction of 1, if they're not in contact with anything they have zero traction. If you wanted to I guess you could have different traction values for different terrains and stuff.

This will be included as a sample scene in upcoming updates.

cheers!
Reply