Posts: 2
Threads: 0
Joined: Feb 2021
Reputation:
0
18-02-2021, 04:16 AM
(This post was last modified: 18-02-2021, 04:38 AM by mo1ok.)
Hey everyone,
Just came to this forum for a completely unrelated reason and came across this thread. Coincidentally just solved this problem this week to build a grappling hook in the vanilla SteamVR 2.0 unity plugin. Proof:
The trick was getting steamVR to interact with the obi particles. Here's my ROUGH, unpolished code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using Obi;
using Valve.VR;
[RequireComponent(typeof(ObiSolver))]
public class RopeClimb : MonoBehaviour
{
public ObiSolver solver;
protected Valve.VR.InteractionSystem.VelocityEstimator velocityEstimator;
public GameObject Player;
public float releaseVelocityTimeOffset = -0.011f;
private CharacterController m_CharacterController;
private bool detectingCollisions = true;
private Valve.VR.InteractionSystem.Hand draggingHand = null;
void Start()
{
m_CharacterController = Player.GetComponent<CharacterController>();
}
void Update()
{
// subscribe to collision
if (detectingCollisions == true) {
solver.OnCollision += Solver_OnCollision;
detectingCollisions = false;
}
// only set upon successful collision + grip
if (draggingHand != null) {
DragMove(draggingHand);
}
}
void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
{
var world = ObiColliderWorld.GetInstance();
foreach (Oni.Contact contact in e.contacts)
{
// this one is an actual collision:
if (contact.distance < 0.01)
{
ObiColliderBase collider = world.colliderHandles[contact.other].owner;
if (collider != null && collider.gameObject.tag == "VRHand")
{
Valve.VR.InteractionSystem.Hand hand = collider.gameObject.GetComponent<Valve.VR.InteractionSystem.Hand>();
// set that we are currently dragging
draggingHand = hand;
}
}
}
}
void DragMove(Valve.VR.InteractionSystem.Hand hand) {
Vector3 velocity;
Vector3 massagedVelocity;
Vector3 angularVelocity;
Vector3 currentPositon = hand.transform.position;
Valve.VR.InteractionSystem.GrabTypes startingGrabType = hand.GetGrabStarting();
if (velocityEstimator != null) {
velocityEstimator.BeginEstimatingVelocity();
}
velocity = hand.GetTrackedObjectVelocity(releaseVelocityTimeOffset);
angularVelocity = hand.GetTrackedObjectAngularVelocity(releaseVelocityTimeOffset);
massagedVelocity = -velocity / 25; //arbitrary
Valve.VR.InteractionSystem.GrabTypes bestGrabType = hand.GetBestGrabbingType();
if ( bestGrabType != Valve.VR.InteractionSystem.GrabTypes.None && hand.currentAttachedObject == null) {
hand.Hide();
m_CharacterController.Move(massagedVelocity);
}
// upon release grip, stop dragmove state
// TODO - unset when out of range (can't drag forever)
if ( bestGrabType == Valve.VR.InteractionSystem.GrabTypes.None ) {
hand.Show();
draggingHand = null;
};
}
}
A couple of notes:
-I tagged the VR hands with the tag "VRHand".
-I added a simple box collider with "trigger" set to true and Obi Collider to Valve's VR hands on the Player object.
-You have to pass in the player object and Obi Solver to the script. There's probably a better way of doing this. Like I said, this is an unpolished script!
-If you want to have this "grab" the rope particle instead of climbing it in a dragmove style, you could apply the velocity from the hands ("velocity" in the script) to the velocity of the currently grabbed rope particle (could be derived from "collider" in the script here, could create new top-level private variable "activeParticle" and set it when grabbed).
Posts: 25
Threads: 3
Joined: Jan 2021
Reputation:
2
(18-02-2021, 04:16 AM)mo1ok Wrote: -If you want to have this "grab" the rope particle instead of climbing it in a dragmove style, you could apply the velocity from the hands ("velocity" in the script) to the velocity of the currently grabbed rope particle (could be derived from "collider" in the script here, could create new top-level private variable "activeParticle" and set it when grabbed).
As Jose pointed out to me early in this thread, I think it's better create a new pin constraint between the grabbed particle and the hand if you want the other rope constraints to factor in rather than allowing the rope to stretch infinitely by overriding the velocity directly. This is my understanding at least! Nice to see someone else working on a similar project!
Posts: 20
Threads: 6
Joined: Feb 2021
Reputation:
0
22-02-2021, 12:11 PM
(This post was last modified: 22-02-2021, 12:17 PM by tpaslou.)
I have tried to add many Obi Particle Attachments so I could grab each one of them instead of particles but there are some problems with this approach. There is a digression from the actual rope and when you grab one Particle and the velocity does not apply to the rest of them.
Maybe I could make all other Rigid Bodies Kinematic apart from the one being grabbed.
But probably working with particles directly instead of Particle Attachments is the right way. Although I still cant find a solution for the weird jitter and rotations when I attach my hand to particles.
Posts: 6,347
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
22-02-2021, 12:21 PM
(This post was last modified: 22-02-2021, 12:30 PM by josemendez.)
(22-02-2021, 12:11 PM)tpaslou Wrote: I have tried to add many Obi Particle Attachments so I could grab each one of them instead of particles but there are some problems with this approach.
Dynamic particle attachments create pin constraints for individual particles under the hood, so it's exactly the same as you manually creating pin constraints, only more convoluted.
Quote:There is a digression from the actual rope and when you grab one Particle the velocity does not apply to the rest of them.
There should not be any digression if the attachment (or pin constraints) are properly set up: no pin constraints inside colliders, appropriate offset passed to the pin constraint, etc.
Grabbing one particle will only set the velocity for that one particle, it can't possible affect other particles directly. The rest of particles will move as a result of distance/bend constraints in the rope.
Posts: 6,347
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
22-02-2021, 12:28 PM
(This post was last modified: 22-02-2021, 12:30 PM by josemendez.)
(19-02-2021, 09:35 PM)Xanduffy Wrote: As Jose pointed out to me early in this thread, I think it's better create a new pin constraint between the grabbed particle and the hand if you want the other rope constraints to factor in rather than allowing the rope to stretch infinitely by overriding the velocity directly. This is my understanding at least!
100% correct . Pin constraints enable what's typically known in physics as two-way coupling: the rope will exert forces on the object it is pinned to, and the object will also exert forces on the rope. Both become "aware" of each other.
Directly setting the velocity or position of a particle only affects the particle, which means the object will act as if no rope was present at all. This is what static attachments do.
When grabbing objects in VR, you want your hands to behave within the physical constraints of the VR environment, which means you don't want to be able to stretch a rope indefinitely (as you wouldn't be able to lift a very heavy object, put your hands inside a wall, etc). The rope should resist stretching once it's completely tense, by applying a force to your hand that opposes the pulling force. So you need dynamic attachments (or pin constraints, which is what dynamic attachments use internally) for this.
Posts: 20
Threads: 6
Joined: Feb 2021
Reputation:
0
(22-02-2021, 12:21 PM)josemendez Wrote: Dynamic particle attachments create pin constraints for individual particles under the hood, so it's exactly the same as you manually creating pin constraints, only more convoluted.
There should not be any digression if the attachment (or pin constraints) are properly set up: no pin constraints inside colliders, appropriate offset passed to the pin constraint, etc.
Grabbing one particle will only set the velocity for that one particle, it can't possible affect other particles directly. The rest of particles will move as a result of distance/bend constraints in the rope.
So I created a script which enables and disables Kinematic and Particle attachments based on the "node" grabbed. I want the kinematic nodes to follow the transform of the closest Particle. How can I achieve that via script ?
Posts: 6,347
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
23-02-2021, 12:45 PM
(This post was last modified: 23-02-2021, 12:55 PM by josemendez.)
(23-02-2021, 12:42 PM)tpaslou Wrote: So I created a script which enables and disables Kinematic and Particle attachments based on the "node" grabbed. I want the kinematic nodes to follow the transform of the closest Particle. How can I achieve that via script ?
Should be pretty straightforward: iterate over all particle positions, find the closest one, and then set the kinematic object's position to that. See:
http://obi.virtualmethodstudio.com/tutor...icles.html
Obi exposes particle positions that you can iterate over, the rest of it is run of the mill code.
Let me know if you have trouble calculating distances, finding the closest, etc.
Edit: if the idea is to have one kinematic rigidbody per particle, and then use these for collision detection and grabbing, that's not a good one imho. Particles already perform collision detection and can be pinned to any position of a body, so this is just reinventing the wheel. Having a rigidbody per particle won't be very performant, either. A good solution is to iterate over contacts, find the one closest to the hand, and pin it (as Xanduffy's code does).
Posts: 20
Threads: 6
Joined: Feb 2021
Reputation:
0
(23-02-2021, 12:45 PM)josemendez Wrote: Edit: if the idea is to have one kinematic rigidbody per particle, and then use these for collision detection and grabbing, that's not a good one imho. Particles already perform collision detection and can be pinned to any position of a body, so this is just reinventing the wheel. Having a rigidbody per particle won't be very performant, either. A good solution is to iterate over contacts, find the one closest to the hand, and pin it (as Xanduffy's code does).
I have used that code but the rope behaves unexpectedly when grabbing. It rotates at the point of grab and this affects the whole rope
Posts: 6,347
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
(24-02-2021, 10:35 AM)tpaslou Wrote: I have used that code but the rope behaves unexpectedly when grabbing. It rotates at the point of grab and this affects the whole rope
It shouldn't do that, it's just constraining the position of a particle. Can you share a video of this?
Posts: 20
Threads: 6
Joined: Feb 2021
Reputation:
0
(24-02-2021, 10:39 AM)josemendez Wrote: It shouldn't do that, it's just constraining the position of a particle. Can you share a video of this?
So this is the video : https://vimeo.com/516147132
The code that I used is :
Code: using UnityEngine;
using System.Collections.Generic;
using Obi;
using HurricaneVR.Framework.Shared;
using HurricaneVR.Framework.ControllerInput;
/**
* Sample component that makes a collider "grab" any particle it touches (regardless of which Actor it belongs to).
*/
[RequireComponent(typeof(ObiCollider))]
public class ObiGrabber : MonoBehaviour
{
/*public HVRController RightController => HVRInputManager.Instance.RightController;
public HVRController LeftController => HVRInputManager.Instance.LeftController;*/
public HVRController handController;
public bool canGrab = true;
ObiSolver solver;
ObiCollider obiCollider;
public ObiRope rope;
ObiSolver.ObiCollisionEventArgs collisionEvent;
ObiPinConstraintsBatch newBatch;
ObiConstraints<ObiPinConstraintsBatch> pinConstraints;
void Awake()
{
solver = FindObjectOfType<ObiSolver>();
obiCollider = GetComponent<ObiCollider>();
}
void Start()
{
InitializeController();
// get a hold of the constraint type we want, in this case, pin constraints:
pinConstraints = rope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
}
private void OnEnable()
{
if (solver != null)
solver.OnCollision += Solver_OnCollision;
}
private void OnDisable()
{
if (solver != null)
solver.OnCollision -= Solver_OnCollision;
}
private void InitializeController()
{
if (tag == "LeftHandGrabber")
{
handController = HVRInputManager.Instance.LeftController;
}
else if (tag == "RightHandGrabber")
{
handController = HVRInputManager.Instance.RightController;
}
else
{
Debug.LogError("Failed Initializing Controller on Rope Grabber");
}
}
private void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
{
collisionEvent = e;
}
public void Grab()
{
var world = ObiColliderWorld.GetInstance();
Debug.Log(pinConstraints);
if (solver != null && collisionEvent != null)
{
Debug.Log("Collision");
foreach (Oni.Contact contact in collisionEvent.contacts)
{
if (contact.distance < 0.01f)
{
var contactCollider = world.colliderHandles[contact.bodyB].owner;
ObiSolver.ParticleInActor pa = solver.particleToActor[contact.bodyA];
Debug.Log(pa + " hit " + contactCollider);
if (canGrab)
{
if (contactCollider == obiCollider)
{
Debug.Log("Hand Collision");
var batch = new ObiPinConstraintsBatch();
int solverIndex = rope.solverIndices[contact.bodyA];
Vector3 positionWS = solver.transform.TransformPoint(solver.positions[solverIndex]); // particle position from solver to world space
Vector3 positionCS = obiCollider.transform.InverseTransformPoint(positionWS); // particle position from world to collider space
batch.AddConstraint(rope.solverIndices[contact.bodyA], obiCollider, positionCS, Quaternion.identity, 0, 0, float.PositiveInfinity);
batch.activeConstraintCount = 1;
newBatch = batch;
pinConstraints.AddBatch(newBatch);
canGrab = false;
// this will cause the solver to rebuild pin constraints at the beginning of the next frame:
rope.SetConstraintsDirty(Oni.ConstraintType.Pin);
}
}
}
}
}
}
public void Release()
{
if (!canGrab)
{
Debug.Log("Release");
pinConstraints.RemoveBatch(newBatch);
rope.SetConstraintsDirty(Oni.ConstraintType.Pin);
canGrab = true;
}
}
private void Update()
{
if (handController.TriggerButtonState.Active || handController.GripButtonState.Active)
{
Debug.Log("PRESSED");
Grab();
}
else
{
Release();
}
}
}
|