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.
Sockets
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.
Use Cases
Sockets are a flexible interaction pattern that can be applied across multiple VR domains:
- Engineering & Manufacturing: Assembly training (e.g., attaching machine parts, wiring circuits). Maintenance procedures (e.g., snapping tools back into holders, replacing components).
- Medical Training: Attaching prosthetics or surgical tools to rigs. Snapping medical instruments into sterilization trays or stands.
- Education & Simulation: Teaching physics concepts by building circuits or modular systems. Assembling molecules or models piece by piece.
- Gaming & Entertainment: Weapon or tool customization (e.g., attaching scopes, silencers, or handles). Puzzle mechanics (e.g., fitting shapes into sockets, key-lock systems).
- Architecture & Design: Furniture arrangement with snap-to points. Modular building systems with predefined docking stations.
- Everyday Utilities: Organizing objects on a workstation (e.g., snapping items into holders). Virtual prototyping where parts must consistently align.
Principles
Before diving into technical setup, it is important to understand the principles of socket-based interactions:
- Predictability: Users should always know how and where an object will attach. Clear visual and spatial cues reduce confusion.
- Precision vs. Flexibility: Decide whether sockets should allow loose placement (ease of use) or enforce strict tolerances (engineering realism).
- Feedback & Affordances: Provide visual, audio, or haptic feedback when an object is close to or correctly aligned with a socket.
- One-to-One Relationships: Each socket should be designed for a specific object type (via
Interaction Layers
) to avoid unintended snapping. - Performance Considerations: Use minimal colliders and avoid overlapping triggers to maintain smooth interactions.
- Reversibility: Decide whether objects can be removed after being socketed, and under what conditions (e.g., only with a specific tool or action).
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 attachment mechanics in Unity’s XR Interaction Toolkit. Unlike direct interactors (e.g., controllers that grab objects explicitly), a socket passively detects eligible interactables inside its trigger collider and aligns them to a specified attach point. For example, in the V8 engine assembly, the engine block contains sockets for pistons and cylinder heads. Each socket interactor ensures that parts only attach in their correct orientation, simulating a precise assembly workflow.
Review this Unity Documentation to learn more about
XR Socket Interactor
.
Inspector Properties
When you add an XR Socket Interactor
component to a GameObject, you’ll see the following key fields in the Inspector:
Interaction Manager
: Reference to theXR Interaction Manager
that coordinates all interactors and interactables. Default:None (XR Interaction Manager)
— if left blank, it will attempt to auto-find one in the scene.Interaction Layer Mask
: Determines which interactables are allowed to interact with this socket. Default:Everything
— meaning it will accept all interactables unless filtered.Handedness
: Restricts socket usage to a specific controller hand if required. Default:None
(no restriction).Attach Transform
: A child transform that defines the exact snap position and rotation. This must be set manually for precise alignment.Starting Selected Interactable
: Option to auto-attach a specific object when the scene starts.Keep Selected Target Valid
: If enabled, the socket maintains its selection even when the interactable temporarily leaves the socket’s trigger.Show Interactable Hover Mesh
: Toggle to render a hover mesh when an object is near the socket.Hover Mesh Material
: Assign a material for valid hover visualization.Can’t Hover Mesh Material
: Assign a material for invalid hover states.Hover Scale
: Adjust scale of the mesh preview during hover.
Hover Socket Snapping
: If enabled, the interactable will visually snap into the socket’s attach transform during hover—before being selected.Socket Scale Mode
: Defines how scaling is applied to objects when attached. Options include None, Match, or other scaling modes.Socket Active
: Toggles the socket’s availability. Useful for dynamically enabling or disabling sockets during gameplay.Recycle Delay Time
: Sets a cooldown before the socket can accept the same interactable again. Prevents rapid attach/detach cycling.
Events & Extensibility
XR Socket Interactor
provides several event hooks for developers to attach custom logic:
-
Interactor Filters
: Allow filtering or conditions for which interactables can hover or be selected. -
Interactor Events
:Hover Entered (HoverEnterEventArgs)
: Triggered when an interactable enters the socket’s detection zone.Hover Exited (HoverExitEventArgs)
: Triggered when it leaves.Select Entered (SelectEnterEventArgs)
: Triggered when an interactable is successfully socketed.Select Exited (SelectExitEventArgs)
: Triggered when the interactable is removed from the socket.
All of these event lists are empty by default but can be expanded in the
Inspector
to assign UnityEvents, scripts, or feedback mechanisms.
Example
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
- Add Hover Audio Feedback (Interactor Event):
- In your project, create a
HoverSoundPlayer.cs
script:
using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; public class HoverSoundPlayer : MonoBehaviour { public AudioSource hoverAudio; // assign in Inspector // Called by XR Socket Interactor → Interactor Events → Hover Entered public void PlayHoverSound(HoverEnterEventArgs args) { if (hoverAudio != null) hoverAudio.Play(); } }
- Create an
Empty GameObject
in your scene, name itHoverSoundManager
. Add theHoverSoundPlayer
component to it. - Add (or reference) an
AudioSource
with your hover sound and drag it into theHover Audio
field of the script. - Select a
XR Socket Interactor
(for example,Tire Socket
or eachLug Socket
). - In the
Inspector
, expandInteractor Events → Hover → Hover Entered
. - Click
+
, drag theHoverSoundManager
GameObject into the object field. - From the function dropdown, choose:
HoverSoundPlayer → PlayHoverSound(HoverEnterEventArgs)
.
You can reuse the same
HoverSoundManager
across all sockets to keep setup simple. - In your project, create a
- Deploy and Test:
- Deploy the app to your device.
- You can experience a complete tire assembly sequence.
- When the part hovers over the socket, you’ll hear the sound play.
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.