D4. Attaching Objects in VR
Learning Outcomes
- Explain the concept of sockets for object snapping in XR. Before class, explore how sockets work by relating them to real-world analogies like plugs or mechanical joints.
- Configure a grab-ready object in Unity for socket interaction. For preparation, set up a 3D object with a
Rigidbody
andXR Grab Interactable
component, then test its basic grabbing behavior.- Use vector comparisons to validate alignment in snapping interactions. Ahead of the session, review and experiment with
Vector3.Angle()
andVector3.Distance()
in a script to see how they can enforce positional and rotational constraints.- Apply best practices for socket-based object attachment. Come ready with one real-world engineering example where precise part alignment is critical, and connect it to XR development considerations.
What Is a Socket?
In VR, a socket is a designated area or docking station where an interactable object snaps into a predetermined position and orientation. This ensures objects—like gears in a gearbox or sensors on a panel—align correctly when placed, enhancing user experience through predictable, visually coherent interactions. By using the XR Socket Interactor
to define clear attachment points and aligning attach transforms, objects consistently snap into place. Additionally, employing interaction layers restricts attachments to the correct objects, further enhancing performance and usability.
In the assembly station of XFactory, users can assemble a V8 engine by placing components such as cylinder heads, pistons, and the crankshaft into precise positions. Each engine part can be configured with an
XR Grab Interactable
, and each corresponding location on the engine block is equipped with anXR Socket Interactor
. As users snap parts into their correct locations, the engine takes shape. This process mimics real-world assembly alignment requirements and emphasizes precision and order—critical in engineering systems.
XR Socket Interactor
A socket behaves like a snap-to mechanism, ensuring that objects align correctly—enhancing functional realism and user experience. The XR Socket Interactor
component is essential for the attachment mechanism. It automatically detects interactable objects within its trigger area and attaches them to a specified attach point. Unlike direct interactors that require explicit user input (e.g., grabbing with a controller), the socket interactor passively monitors for eligible objects. For example, In the V8 engine assembly example, the engine block must be assembled by attaching engine components like pistons and heads into the correct sockets. Each component has its own socket interactor to ensure proper placement, simulating a realistic assembly process.
Key Properties
-
Interaction Manager: References the
XRInteractionManager
that coordinates interactions between different interactors (sockets, grab interactables) in the scene. -
Interaction Layer Mask: Filters which objects are eligible for attachment. By assigning objects and sockets to specific layers, you ensure that only intended items interact.
-
Attach Transform
: A child transform of the socket that defines the exact position and rotation where an object should be attached. Proper alignment is crucial for a natural-looking attachment. -
Starting Selected Interactable: Allows an object to be automatically attached when the scene starts. Useful for pre-positioning objects.
-
Keep Selected Target Valid: Determines if the socket maintains its attachment even when the interactable temporarily leaves the valid interaction area.
-
Visual Feedback Options: Configurable features (e.g., hover meshes, materials) provide visual cues to indicate when an object is near or ready for attachment.
Configuring Sockets
In engineering contexts, sockets can represent part mounts, assembly points, holders, or loading positions. In this example, we simulate assembling a tire onto a brake caliper and disk, and securing it using five lug nuts, grabbed from a stack box.
- Prepare Your Interactable Objects:
- In the
Hierarchy > Assembly
, locate your 3D model of theTire
on theMetal Table
. This object will be socketed (i.e., assembled) onto theTire Stand
. - Locate the
Tire Lug Nut
GameObjects inside theStack_Box_02_Prefab_02
on theMetal Table
. These will be picked up and assembled onto theTire
.
Interactable objects are those that users can grab, move, and place using XR controllers.
- In the
- Configure Physics:
- Add
Rigidbody
components to theTire
andTire Lug Nut
GameObjects. This allows realistic physics and prevents clipping through socket points. - Enable
Use Gravity
. - Set
Collision Detection
toContinuous Dynamic
.
- Add
- Make the Objects Grabbable:
- Add
XR Grab Interactable
components to theTire
andTire Lug Nut
GameObjects. - Enable
Smooth Position
andSmooth Rotation
for natural grabbing behavior. - Since the tire is relatively large and heavy, set its
Movement Type
toVelocity Tracking
to simulate realistic physics while maintaining responsive control. - Lug nuts are small and lightweight. Therefore,
Movement Type = Kinematic
gives more precise control, especially when aligning with small sockets. - Confirm that the models have
Collider
components.
- Add
- Create the Tire Socket:
- On the
Tire Stand
, create an empty GameObject and name itTire Socket
. - Add a
Sphere Collider
toTire Socket
. - Adjust the collider radius (e.g.,
0.2
) to detect the approaching Tire. - Enable
Is Trigger
. - With
Tire Socket
selected, add theXR Socket Interactor
component.
- On the
- Set Up the Attach Transform:
- Create a child GameObject under
Tire Socket
, name itAttach Tire
. - Position and rotate it so the
Tire
aligns correctly. - Assign
Attach Tire
to theAttach Transform
field ofXR Socket Interactor
.
- Create a child GameObject under
- Measure the Attach Transform:
- To accurately set up the
Attach Transform
for the Tire socket, temporarily mount the full Tire model onto the stand in the correct position and orientation. - Create a child GameObject under the socket and manually copy the Tire’s transform values (position and rotation) into it. This ensures a perfect snap alignment when the
Tire
is placed in VR.
- To accurately set up the
- Create Lug Nut Sockets:
- On the
Brake Disk
GameObject (a child of theTire
underTire Stand
), create an empty GameObject and name itLug Socket
. - Position it at the tip of the first screw post where a lug nut will be attached.
- Add a
Sphere Collider
toLug Socket
. - Enable
Is Trigger
. - Adjust the radius (e.g.,
0.03–0.05
) to create a small, accurate detection zone. - Add the
XR Socket Interactor
component toLug Socket
. - Optionally, configure the
Interaction Layer Mask
to only accept objects tagged asLugNut
. - Create a child GameObject under
Lug Socket
and name itAttach Lug
. - Adjust its position and rotation so the lug nut aligns properly with the screw post.
- Assign
Attach Lug
to theAttach Transform
field of the socket.
Make sure all
Lug Nut
objects have theirInteraction Layer Mask
set toLugNut
so that the sockets can detect and accept them correctly without interfering with theTire
socket. Interaction layers restrict sockets to accept only specific types of parts. This prevents misplacement and guides users toward correct assembly behaviors. - On the
- Duplicate for Remaining Lug Nut Positions:
- In the
Hierarchy
, duplicateLug Socket
four times. - Reposition each socket at the tip of its corresponding screw post.
- This method ensures consistent and accurate socket configuration across all lug nut positions while saving setup time.
- In the
- Deploy and Test:
- Deploy the app to your device.
- You can experience a complete tire assembly sequence.
Snapping with Constraints
While the XR Socket Interactor
provides basic snapping behavior, engineering applications often demand precise spatial alignment. For example, parts in the tire and lug nut assembly or the V8 engine may need to be within specific rotational or positional tolerances to be accepted—just as in real-world mechanical assemblies. To enable this behavior, the VR app should enforce custom alignment rules for snapping, reject improperly placed parts using haptics and audio, and validate placement based on vector math and distance checks.
Angle Tolerances
Vector math allows you to check angle tolerances. You can use dot products to calculate the angle between the up vector of the part and the correct alignment vector (e.g., global up for vertical orientation). This allows you to enforce a tolerance range, such as ±10°. In XFactory’s assembly station, for example, you may want to validate that a lug nut is aligned correctly before snapping to the brake disk. Get the lug nut’s transform.up
, compare it to Vector3.up
, use Vector3.Angle()
to measure the deviation, and accept the snap only if within a defined range (e.g., ±10°):
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class LugNutAngleValidator : MonoBehaviour
{
public float maxAngleDeviation = 10f; // degrees
public AudioSource errorAudio;
private XRSocketInteractor socket;
void Start()
{
socket = GetComponent<XRSocketInteractor>();
socket.selectEntered.AddListener(CheckLugNutAngle);
}
void CheckLugNutAngle(SelectEnterEventArgs args)
{
Transform lugNut = args.interactableObject.transform;
float angle = Vector3.Angle(lugNut.up, Vector3.up); // Compare part's up with world up
if (angle > maxAngleDeviation)
{
RejectLugNut(args.interactableObject, "Lug nut angle too steep.");
}
}
void RejectLugNut(IXRSelectInteractable lugNut, string reason)
{
socket.interactionManager.CancelInteractableSelection(lugNut);
if (errorAudio != null)
errorAudio.Play();
Debug.Log("Snap rejected: " + reason);
}
}
Attach the LugNutAngleValidator.cs
script to the Lug Socket
GameObject. Make sure to configure the maxAngleDeviation
in the inspector and assign an AudioSource
for feedback (optional).
Distance
You can prevent snapping if a part is not close enough to its intended attach point, ensuring users move the part with adequate precision. In the tire and lug nut assembly example, use Vector3.Distance()
to measure how far a Lug Nut
is from the socket’s Attach Transform
, and reject the snap if it exceeds a threshold (e.g., 0.15 meters):
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class LugNutDistanceValidator : MonoBehaviour
{
public Transform attachTransform; // Drag in the Attach point of the socket
public float maxSnapDistance = 0.15f; // meters
public AudioSource errorAudio;
private XRSocketInteractor socket;
void Start()
{
socket = GetComponent<XRSocketInteractor>();
socket.selectEntered.AddListener(CheckLugNutDistance);
}
void CheckLugNutDistance(SelectEnterEventArgs args)
{
Transform lugNut = args.interactableObject.transform;
float distance = Vector3.Distance(lugNut.position, attachTransform.position);
if (distance > maxSnapDistance)
{
RejectLugNut(args.interactableObject, "Lug nut too far from socket.");
}
}
void RejectLugNut(IXRSelectInteractable lugNut, string reason)
{
socket.interactionManager.CancelInteractableSelection(lugNut);
if (errorAudio != null)
errorAudio.Play();
Debug.Log("Snap rejected: " + reason);
}
}
Attach the LugNutDistanceValidator
script to each Lug Socket
sockets in your scene. Then, in the Inspector
, drag the corresponding Attach Lug
GameObject (child of the socket) into the script’s Attach Transform
field.
Feedback
When misalignment is detected during socket interaction, it is important to give the user clear and immediate feedback. This can include audio cues (e.g., error sounds), haptic feedback via the VR controllers, visual cues or messages, or rejection of the snap, allowing retry or resetting the part’s position. In the tire and lug nut assembly example, if a lug nut is misaligned or too far from the socket, you can reject the interaction, play a sound, and vibrate the controller to alert the user. Here’s how you can implement that:
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class LugNutSnapValidator : MonoBehaviour
{
public Transform attachTransform;
public float maxAngle = 10f;
public float maxDistance = 0.15f;
public AudioSource errorAudio;
private XRSocketInteractor socket;
void Start()
{
socket = GetComponent<XRSocketInteractor>();
socket.selectEntered.AddListener(ValidateLugNut);
}
void ValidateLugNut(SelectEnterEventArgs args)
{
Transform lugNut = args.interactableObject.transform;
float angle = Vector3.Angle(lugNut.up, Vector3.up);
float distance = Vector3.Distance(lugNut.position, attachTransform.position);
if (angle > maxAngle || distance > maxDistance)
{
RejectLugNut(args);
}
}
void RejectLugNut(SelectEnterEventArgs args)
{
IXRSelectInteractable lugNut = args.interactableObject;
socket.interactionManager.CancelInteractableSelection(lugNut);
// Audio feedback
if (errorAudio != null)
errorAudio.Play();
// Haptic feedback
XRBaseControllerInteractor controller = args.interactorObject as XRBaseControllerInteractor;
if (controller != null)
{
controller.SendHapticImpulse(0.7f, 0.2f); // intensity, duration
}
Debug.Log("Lug nut rejected: not aligned or positioned correctly.");
}
}
Attach the LugNutSnapValidator
script to each Lug Socket
GameObjects. Make sure to also assign the correct Attach Transform
(child GameObject) in the Inspector
. Optionally, assign an AudioSource
to play error feedback when misalignment is detected.
Best Practices
The following best practices will help ensure reliable, realistic, and maintainable socket-based interactions in your XR projects—particularly when assembling complex objects.
-
Attach Transforms: Ensure that the local axes of the
Attach Transform
match the intended orientation of the attached object. This guarantees seamless and realistic snapping during placement. In XFactory, when inserting a piston into a V8 engine block, a misaligned attach transform can cause the piston to appear rotated or offset. -
Trigger Colliders: Always use the smallest collider radius that still detects the intended object. Prefer
Sphere Colliders
orBox Colliders
that tightly match socket volume. Set the collider asIs Trigger
to detect entry without physical collisions. In XFactory, inserting a piston onto the engine block should not trigger the nearby crankshaft socket—use fine-tuned colliders to avoid overlap. -
Interaction Layers: Define dedicated
Interaction Layer
s for object categories (e.g.,Pistons
,Tires
,Tools
). Assign both interactable objects and sockets to the correct layers via theInteraction Layer Mask
. This limits what objects can interact with each socket and reduces unintended behavior. In XFactory, only allow parts with theEngineParts
layer to snap into engine sockets, preventing users from accidentally inserting another object into a piston slot. -
Alignment Validation: For realistic mechanical constraints, validate position and orientation before accepting a snap. Use
Vector3.Angle()
for angular checks andVector3.Distance()
for proximity. Reject poorly aligned parts and provide user feedback (e.g., audio, haptics). In XFactory, a piston must be within ±10° of vertical and under 0.15 meters from the socket before it’s accepted. -
Improper Object Snapping: If a part snaps incorrectly, check that the
Attach Transform
is positioned and rotated accurately. Ensure theAttach Transform
is assigned in theXR Socket Interactor
component. Use debugging tools (e.g.,Debug.DrawRay()
) to visualize socket orientation in theScene
view. You can even write a script to visually validate attach transforms during development using gizmos. -
Unintended Interactions: Ensure that only intended object types can enter and attach to a socket. Double-check
Interaction Layer Mask
on both socket and object. Use Tag filtering or custom scripts for more granular control. In XFactory, a socket expecting a cylinder head should not accept a tire, even if they share colliders orRigidbody
settings. -
Physics Behavior: Assign
Rigidbody
components to all grabbable objects. EnableContinuous Dynamic
collision detection to prevent fast-moving objects from tunneling through sockets or floors. If the object jitters or behaves unpredictably when grabbed, check for incorrectmass
ordrag
settings,Collider
components that intersect at spawn time, or missing or incorrect layer-based collisions in Unity’s Physics settings. In XFactory, a scanner dropped near a rack may fall through the floor if itsRigidbody
usesDiscrete
collision detection. -
Custom Logic:
XR Socket Interactors
are powerful but limited to proximity-based attachment. For realistic workflows (e.g., enforcing assembly order, alignment tolerances), pair sockets with scripts that validate geometry, check user sequence, and give meaningful feedback. In XFactory, only allow cylinder head snapping after all V8 pistons are correctly installed—using a simple script that loops through each piston socket and validates itsselectTarget
.
Key Takeaways
Sockets in VR function as precise snap points that ensure objects align correctly, enhancing both realism and usability in XR assembly tasks. By configuring interactable objects with the appropriate physics settings, XR Grab Interactable
components, and XR Socket Interactors
, developers can create intuitive and reliable attachment systems. Careful setup of attach transforms, interaction layers, and collider sizes helps prevent misplacements and unintended interactions. For engineering-grade accuracy, adding alignment and distance validation through vector math ensures that only correctly positioned parts snap into place, with immediate audio, visual, or haptic feedback guiding the user. When combined with best practices in organization, physics configuration, and custom logic, these techniques enable robust, realistic, and maintainable socket-based interactions for complex VR assembly experiences.