VR控制器间物体传递异常:释放原控制器后物体掉落
Alright, let's tackle this frustrating hand-off problem. The core issue here is that when you release the original controller's trigger after passing the object to your other hand, your code is still running the full release routine—setting the object to non-kinematic and letting it fall—even though the second hand is already holding onto it. Let's break down the fixes step by step.
What's Causing the Problem?
Looking at your Update() method, when you're holding an object, the code checks if you've released the trigger and immediately activates the object's physics, regardless of whether the other hand has taken over. There's no check to see if the secondary hand is now in possession of the object, so it drops every time you let go of the original trigger.
Step-by-Step Fixes
1. Add a Check for Secondary Hand Ownership Before Releasing
Modify the release logic in the else block (where you're holding an object) to first check if the secondary hand is already holding the same object. If it is, just clear your hand's reference without messing with the object's physics.
2. Ensure Proper State Reset When Transferring
When the secondary hand grabs the object, make sure the original hand's _isHeld flag is set to false—right now you're only clearing CurrentGrabObject, which leaves the original hand thinking it's still holding something.
Modified Code Snippet
Here's how your updated HandGrab script should look with these fixes:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; public class HandGrab : MonoBehaviour { // Variable for Controller button register public string buttonName; // Allows objects to be transferred between hands public HandGrab secondaryHandRef; // Determines the referenced object type public XRNode nodeType; // This affects the distance of object grab from controller public Vector3 objectGrabOffset; // Default grab distance relative to the controller, (Where the collider sphere is) This can be changed to affect the visuals and where the items get manipulated from public float grabDistance = 0.1f; // Force at which an object is thrown public float throwingMultiplier = 1.5f; // To determine if object has been grabbed or cant be grabbed public string grabName = "Grabbing"; // Stores the values of the currently grabbed object public Transform CurrentGrabObject { get { return _currentlyHeldObject; } set { _currentlyHeldObject = value; } } // Does as it says and stores the last frame and is used to work out velocity calculations private Vector3 _lastFramePosition; // Determines the transform values for the currently selected objects private Transform _currentlyHeldObject; // boolean to determine if an object is currently being held private bool _isHeld; // Use this for initialization void Start() { // Clearing all variables back to zero _currentlyHeldObject = null; _lastFramePosition = transform.position; _isHeld = false; // Determining the tracking space available XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale); } // Update is called once per frame void Update() { // rotation and position tracking. Updates every frame, should be 90fps transform.localPosition = InputTracking.GetLocalPosition(nodeType); transform.localRotation = InputTracking.GetLocalRotation(nodeType); // Check if hand is empty, look for objects to pick up if (_currentlyHeldObject == null) { Collider[] colliders = Physics.OverlapSphere(transform.position, grabDistance); if (colliders.Length > 0) { if (Input.GetAxis(buttonName) >= 0.5F && colliders[0].transform.CompareTag(grabName)) { if (_isHeld) { return; } _isHeld = true; // Take ownership of the object colliders[0].transform.SetParent(transform); if (colliders[0].GetComponent<Rigidbody>() == null) { colliders[0].gameObject.AddComponent<Rigidbody>(); } colliders[0].GetComponent<Rigidbody>().isKinematic = true; _currentlyHeldObject = colliders[0].transform; // If secondary hand was holding this object, reset its state if (secondaryHandRef.CurrentGrabObject == _currentlyHeldObject) { secondaryHandRef.CurrentGrabObject = null; secondaryHandRef._isHeld = false; } } } } // We're holding an object, handle release or position update else { if (Input.GetAxis(buttonName) < 0.1f) { // Check if secondary hand has taken over the object if (secondaryHandRef.CurrentGrabObject == _currentlyHeldObject) { // Just clear our reference, physics is already managed by the other hand _currentlyHeldObject = null; _isHeld = false; return; } // Original release logic for when no other hand is holding it Rigidbody _objectRB = _currentlyHeldObject.GetComponent<Rigidbody>(); _objectRB.isKinematic = false; _objectRB.collisionDetectionMode = CollisionDetectionMode.Continuous; Vector3 velocity = (transform.position - _lastFramePosition) / Time.deltaTime; _objectRB.velocity = velocity * throwingMultiplier; _currentlyHeldObject.SetParent(null); _currentlyHeldObject = null; _isHeld = false; } } // Update last frame position for velocity calculations _lastFramePosition = transform.position; } }
Key Changes Explained
- Release Check: When releasing the trigger, we first verify if the secondary hand is holding the same object. If yes, we only reset our hand's state instead of activating physics.
- Transfer Cleanup: When grabbing an object that the other hand was holding, we now reset both the secondary hand's
CurrentGrabObjectand_isHeldflag, ensuring neither hand thinks it's holding the object anymore. - State Reset: Added
_isHeld = falsein the release logic to make sure the hand doesn't get stuck in a "holding" state after dropping or transferring.
This should fix the issue where the object drops when you release the original controller after passing it to the other hand—now the transfer will be seamless, with the object staying kinematic while held by either hand.
内容的提问来源于stack exchange,提问作者Batteredburrito

