Sync SIK Components
While building Connected Lenses, developers may need to sync certain interactions across multiple connections. For example, if the host of a session interacts with a shared ScrollView that everyone can see, all users should see the scroll happen at the same time. To account for this, SpectaclesInteractionKit offers a mix of APIs to allow developers to sync interactions within their Lens.
To enable a synced version of SpectaclesInteractionKit, developers must first enable the [OPTIONAL] Connected Lens SceneObject in the SpectaclesInteractionKit prefab hierarchy. This will enable SpectaclesInteractionKit's sync logic to automatically search for the presence of SpectaclesSyncKit within the project hierarchy to then sync various interactions.
SyncEntity in SIK Componentsβ
The more complex SpectaclesInteractionKits components of Slider, ToggleButton, ScrollView, InteractableManipulation, and ContainerFrame have a sense of state (for example, scroll position, slider value, container placement). To account for this, these components use SpectaclesSyncKit's SyncEntity component directly to manage this state across multiple connections.
By setting isSynced = true in the Inspector Panel of these components, SpectaclesInteractionKit automatically handles:
- Join StateβUsers joining mid-session will sync to the latest component state.
- Live updatesβState changes propagate in real-time across all connected users, according to the currently interacting user.
π‘ Tip: Use isSynced whenever a component's state should persist across users and sessions, such as a shared scrolling menu.
SyncInteractionManager and onSync Eventsβ
Simpler components without persistent state (for example, InteractableAudioFeedback) use synced interaction events instead of SyncEntity. The SyncInteractionManager component detects local interaction events (for example, onHoverEnter) and propagates that event to other connections to invoke the synced equivalent (onSyncHoverEnter in this case) for the connected users' instance.
How It Worksβ
SyncInteractionManagercaches local events fromInteractionManagereach frame and writes them to a shared datastore.- The
InteractionManagerof any other connected user reads from the datastore and invokes the relevant sync event for the sameInteractable. - Basic scripts can listen for
onSyncevents to invoke logic whenever a connected user interacts with a sharedInteractable.
Exampleβ
- User A hovers Interactable 1 with left hand β
onHoverStart. - User B receives β
onSyncHoverStartvia a simulatedSyncInteractor.
Supported Eventsβ
- Hover:
onHoverStart,onHoverEndβonSyncHoverStart,onSyncHoverEnd - Trigger:
onTriggerStart,onTriggerEnd,onTriggerEndOutside,onTriggerCancelβonSyncTriggerStart,onSyncTriggerEnd,onSyncTriggerEndOutside,onSyncTriggerCancel - Drag:
onDragStart,onDragUpdate,onDragEndβonSyncDragStart,onSyncDragUpdate,onSyncDragEnd
All SIK helper components automatically subscribe to onSync events, so developers can immediately use multiplayer-friendly feedback components. For example, the InteractableAudioScript plays audio whenever the local user or any connected user starts a trigger with the associated Interactable:
- TypeScript
- JavaScript
this.interactable.onTriggerStart.add(() => {
try {
if (this.playAudioOnTriggerStart && this._triggerStartAudioComponent) {
this._triggerStartAudioComponent.play(1);
}
} catch (e) {
print('Error playing trigger start audio: ' + e);
}
});
this.interactable.onSyncTriggerStart.add(() => {
try {
if (this.playAudioOnTriggerStart && this._triggerStartAudioComponent) {
this._triggerStartAudioComponent.play(1);
}
} catch (e) {
print('Error playing trigger start audio: ' + e);
}
});
interactable.onTriggerStart.add(function () {
try {
if (playAudioOnTriggerStart && _triggerStartAudioComponent) {
_triggerStartAudioComponent.play(1);
}
} catch (e) {
print('Error playing trigger start audio: ' + e);
}
});
interactable.onSyncTriggerStart.add(function () {
try {
if (playAudioOnTriggerStart && _triggerStartAudioComponent) {
_triggerStartAudioComponent.play(1);
}
} catch (e) {
print('Error playing trigger start audio: ' + e);
}
});
Developer Responsibilities and Limitationsβ
While isSynced and onSync events cover many standard cases, developers are still responsible for syncing Lens-specific logic. Developers can either sync complex states of their Lens by using SpectaclesSyncKit's SyncEntity or subscribing to Interactable's onSync events for their own Lens logic.
For example, a developer can create shared button that launches a rocket for all connected users with the following code:
- TypeScript
- JavaScript
const interactable = this.sceneObject.getComponent(
Interactable.getTypeName()
);
interactable.onTriggerEnd.add(this.launchRocket);
interactable.onSyncTriggerEnd.add(this.launchRocket);
var interactable = script.sceneObject.getComponent(Interactable.getTypeName());
interactable.onTriggerEnd.add(launchRocket);
interactable.onSyncTriggerEnd.add(launchRocket);
β Limitation: SIK cannot automatically sync custom business logic or third-party scripts. Developers must manage these explicitly.
Developers will also need to manage their Scene Hierarchy with similar limitations as SpectaclesSyncKit's SyncEntity, where objects are identified by their position in the Scene Hierarchy. If a developer wants to subscribe to an Interactable for sync events, that Interactable must have the same parent and index in the Scene Hierarchy in every single connected users' Lens.