Fusion Sticky Notes Meta SSA Colocation

Level
ADVANCED

개요

Fusion Sticky Notes Meta SSA Colocation 은 동일한 장소에 위치한 사용자가 Meta Quest 기기를 사용할 때 혼합현실에서 협업할 수 있는 방법을 보여줍니다.
이 공동 위치 시스템은 Meta Shared Spatial Anchors (SSA) 기능을 기반으로 합니다.

공동 위치 기능 외에도 사용자는 스티키 노트를 생성하고 그 위에 그림을 그릴 수 있습니다.
이 샘플은 그림을 그릴 수 있는 세 가지 상호작용 방법을 제공합니다:

  • 손가락 또는 컨트롤러 트래킹을 활용한 손가락 사용,
  • 가상 펜 사용,
  • Logitech MX Ink 사용.

또한, 펜으로 3D 그림을 만들고 그림과 스티키 노트의 색상을 변경할 수도 있습니다.

이 샘플의 더 복잡한 버전도 있습니다. 동일한 장소에 있지 않고 Meta Quest & Apple Vision Pro 등 다른 헤드셋을 사용하는 사용자도 관리할 수 있습니다.
Fusion Sticky Notes Cross-Platform Remote Relocation

기술 정보

이 프로젝트는 Unity 2022.3.47, Fusion 2.0.2, Photon Voice 2.53으로 개발되었으며, 다음 패키지로 테스트되었습니다:

  • Meta XR Core SDK 68.0.3 : com.meta.xr.sdk.core
  • Meta XR Platform 68.0.0 : com.meta.xr.sdk.platform
  • Meta MR Utility Kit 68.0.2 : com.meta.xr.mrutilitykit
  • Logitech MX Ink Unity Integration package (2024/08/28)

헤드셋 펌웨어 버전:

  • Meta Quest v69

시작 전 준비

  • 샘플을 실행하려면, PhotonEngine Dashboard에서 Fusion AppId를 생성하여 Fusion 메뉴에서 접근 가능한 Real Time Settings의 App Id Fusion 필드에 붙여 넣으세요.
    그런 다음 Launch 씬을 로드하고 Play를 누르면 됩니다.

다운로드 (Download)

버전 출시일 다운로드
2.0.5 2025년 3월 4일 Fusion Sticky Notes Meta SSA Colocation 2.0.5 Build 811

입력 처리

Meta Quest

  • 텔레포트: A, B, X, Y 버튼 또는 스틱을 눌러 포인터를 표시합니다. 버튼을 떼면 허용된 대상 위치로 이동합니다.
  • 터치: 버튼 위에 손을 올리면 토글 됩니다.
  • 잡기: 물체 위에 손을 올린 후 컨트롤러의 잡기 버튼을 눌러 집습니다.
  • 가상 펜:
    • 조이스틱을 위/아래로 움직여 펜 색상을 변경합니다.
    • 트리거 버튼을 눌러 3D 그림을 시작합니다. 펜을 놓으면 그림이 끝나고, 3D 그림을 잡을 수 있도록 큐브가 생성됩니다.
    • 펜 끝으로 스티키 노트를 터치하면 표면에 그림을 그립니다.
  • 스티키 노트: 조이스틱을 위/아래로 움직여 배경 색상을 변경합니다.

Logitech MX Ink (Meta 플랫폼 전용)

  • 앞쪽 버튼으로 펜 색상을 변경합니다.
  • 중간 버튼으로 3D 그림을 시작합니다.
  • 뒤쪽 버튼으로 현재 3D 그림을 종료합니다. 큐브가 생성되어 3D 그림을 잡을 수 있습니다.
  • 펜 끝으로 스티키 노트를 터치하면 표면에 그림을 그립니다.
  • 실제 표면을 펜 끝으로 터치하면 3D 그림을 시작합니다.

폴더 구조

  • /Photon 폴더: Fusion 및 Photon Voice SDK 포함
  • /Photon/FusionAddons 폴더: 이 샘플에서 사용하는 XR Addons 포함
  • /XR 폴더: 가상현실 설정 파일 포함
  • /Scripts 폴더: 이 샘플 전용 요소 포함

아키텍처 개요

이 샘플은 VR Shared 페이지에서 설명된 코드 베이스를 기반으로 하며, 특히 리그 동기화를 포함합니다.

이 기반 외에도, 샘플은 Fusion XR addons을 사용하여 재사용 가능한 기능을 처리합니다.

혼합현실

이 샘플은 패스스루(passthrough)와 평면 감지를 포함한 혼합현실에 초점을 맞추고 있습니다.
사용자의 로컬 수직 표면이 감지되면 벽 프리팹이 생성되어 상호작용할 수 있습니다.
감지된 표면은 동기화되지 않으며, 이를 감지한 사용자에게만 표시됩니다.

현재는 실제 표면을 감지하려면 먼저 Quest 시스템 설정에서 방(room) 설정을 수행해야 합니다.

Meta 컴포넌트

샘플의 메인 씬에는 Meta의 빌딩 블록을 통해 생성된 일부 컴포넌트가 포함되어 있습니다.

이들은 다양한 필요를 충족하며, 주요 요소는 다음과 같습니다:

하드웨어 리그

Meta hardware rig 은 Meta 빌딩 블록 OVR rig를 커스터마이즈 한 버전으로, XRHands synchronization addon을 통해 손 추적 동기화를 추가합니다.
자세한 내용은 Meta Core integration add-on을 참고하세요.

Passthrough 빌딩 블록은 사용자의 실제 카메라 화면을 볼 수 있도록 합니다.

네트워크 초기화

  • Network Manager 빌딩 블록에는 NetworkRunner가 포함되어 있습니다.
    이 구성 요소는 UserSpawner에서 플레이어의 네트워크 rig 프리팹을 참조하도록 커스터마이즈 되어, 연결 시 자동으로 스폰 됩니다.

  • Auto Matchmaking 빌딩 블록은 Fusion Bootstrap 컴포넌트를 포함하여 방 이름을 설정하고 연결을 시작합니다.

벽 감지

MRUKMR Utility Kit 핵심 컴포넌트를 포함하여 물리적 환경을 분석하고 벽을 감지합니다.

AnchorPrefabSpawner는 MRUK 컴포넌트로, 물리적 환경에서 수직 표면이 감지될 때 벽 프리팹을 생성하여 포스트잇을 붙일 수 있게 합니다.

공유 공간 앵커 (SSA) 공동 위치 (Shared spatial anchor (SSA) colocation)

  • Platform Init 빌딩 블록은 Meta 플랫폼을 초기화하며, 이는 Meta의 공유 공간 앵커(SSA)를 통한 공동 위치 로직에 필요합니다.
  • Shared Spatial anchors 빌딩 블록은 실제 참조 지점을 공유할 수 있도록 Meta의 SSA 지원을 추가합니다.
  • 마지막으로 Colocation 빌딩 블록은 SSA를 활용해 사용자의 가상 위치를 실제 위치와 일치하도록 재배치합니다.

추가적으로 SSAHandler 컴포넌트가 포함되어 있어 SSA 재배치 후 MRUK 씬을 다시 로드하여 벽이 실제 위치에 맞게 유지되도록 합니다.

Oculus 대시보드 요구사항 (Oculus dashboard requirements)

공유 공간 앵커를 사용하려면 Oculus 사용자 ID가 필요합니다.

이를 얻기 위해서는 Oculus dashboard에서 애플리케이션을 생성해야 합니다.

이후, user Iduser profile 접근 권한을 요청하는 data use checkup을 작성해야 합니다.

Data use checkup

마지막으로, Quest에서 Oculus 사용자 ID에 실제로 접근하려면 release channel을 통해 애플리케이션을 배포해야 합니다.
그렇지 않으면 사용자 ID 값으로 항상 0을 반환받을 수 있습니다.

벽 감지

애플리케이션이 실행되면, 환경의 실제 수직 표면이 강조 표시됩니다.

이를 위해 먼저 시스템 설정에서 방 스캔을 수행해야 합니다.
Meta MRUK(Mixed Reality Utility Kit) 프리팹은 씬 데이터를 관리하는 데 사용되며, 환경이 알려진 방과 일치하는지 확인하고 해당 씬 데이터를 로드합니다.
씬 데이터가 로드되면 AnchorPrefabSpawner 프리팹을 사용해 벽을 표시할 수 있습니다.
이를 위해 프리팹은 수직 표면(예: WALL_FACE, DOOR_FRAME, WINDOW_FRAME, STORAGE, WALL_ART 레이블이 지정된 객체)을 감지하면 MagneticARWall Meta 벽 프리팹을 생성하도록 구성됩니다.

Anchor Prefab Spawner Label
Anchor Prefab Spawner Prefab
Magnetic AR Wall

스티키 노트의 MagnetPoint 컴포넌트는 적절한 레이어를 가진 IMagnets 인터페이스를 구현한 모든 객체에 끌리게 됩니다.
여기서는 StaticMagnet 컴포넌트가 해당 인터페이스를 구현하여 벽 역할을 합니다.

벽 가시성

벽 프리팹의 WallVisibilityHandler 클래스는 현재 상태에 따라 벽의 가시성을 관리합니다:

  • 아직 벽이 선택되지 않은 경우
  • 이 벽이 메인 벽으로 선택된 경우
  • 이 벽이 기본 벽인 경우

벽 상태는 플레이어가 벽 버튼을 터치하면 변경됩니다. 선택된 벽은 유일하게 보이는 벽이 됩니다.

Magnetic AR Wall button

벽 상태에 따른 가시성 외에도, IProximityTarget 인터페이스는 스티키 노트가 근처에 있을 때 시각 효과를 생성하는 데 사용됩니다.
스티키 노트의 ProximityHighlighter 컴포넌트는 MagnetPoint 이벤트를 사용해 IProximityTarget 인터페이스를 구현한 객체에 거리 변화를 알립니다.
이 인터페이스는 WallVisibilityHandler에 의해 구현됩니다.

Meta 플랫폼에서 사용되는 셰이더가 Apple 플랫폼과 호환되지 않기 때문에, 두 가지 효과가 제공됩니다:

  • 첫 번째는 색상의 알파(투명도)를 변경
  • 두 번째는 색상의 강도를 변경
wall visibilty

사용자는 가상 펜 또는 Logitech MX Ink를 사용하여 3D 그림을 만들거나 스티키 노트와 상호작용할 수 있습니다.

가상 펜

첫 번째 사용자가 네트워크에 연결되면 SpawnOnConnect 클래스를 통해 씬에 두 개의 가상 펜이 생성됩니다.

가상 펜으로는 다음과 같은 작업이 가능합니다:

  • 컨트롤러 트리거 버튼을 눌러 새로운 3D 그림 시작
  • 조이스틱을 위/아래로 움직여 색상 변경
  • 펜을 놓으면 그림이 완료되고, 3D 그림을 잡기 위한 큐브가 생성됨
  • 펜의 끝으로 스티키 노트를 터치하면 표면에 그림을 그림

Logitech MX Ink

Logitech MX Ink를 사용하면 다음 작업이 가능합니다:

  • 중간 버튼으로 새로운 3D 그림 시작
  • 앞쪽 버튼으로 색상 변경
  • 뒤쪽 버튼으로 그림 완료
  • 펜 끝이 실제 표면을 터치하면 3D 그림 생성
  • 펜 끝이 스티키 노트를 터치하면 그림을 그림 (이때 가상 시각화는 포스트잇에 의해 막힘)

플레이어의 NetworkRig에는 NetworkMXInkPen 프리팹이 포함되어 있으며, ColoredNetworkMXPenNetworkMXPen 클래스를 상속합니다.

Logitech MX Ink

MX Ink 펜은 IsStylusActive 네트워크 변수로 활성화된 경우에만 표시됩니다.
MX Ink의 상태가 변경될 때마다 OnChangeIsStylusActive() 메서드가 호출됩니다.

C#

    [Networked, OnChangedRender(nameof(OnChangeIsStylusActive))]
    public NetworkBool IsStylusActive { get; set; }

    protected virtual void OnChangeIsStylusActive()
    {
        UpdateDisplayedGameObjects();
    }

NetworkMXPen 클래스는 MX Ink 3D 모델을 실제 MX Ink 펜의 위치와 동기화하는 역할도 담당합니다.

C#


        public override void Spawned()
        {
            base.Spawned();
#if OCULUS_SDK_AVAILABLE
            if (Object.HasStateAuthority)
            {
                localHardwareStylus = FindObjectOfType<VrStylusHandler>();
            }
#endif
            UpdateDisplayedGameObjects();
        }

        public override void FixedUpdateNetwork()
        {
            base.FixedUpdateNetwork();

            if (localHardwareStylus)
            {
                transform.position = localHardwareStylus.transform.position;
                transform.rotation = localHardwareStylus.transform.rotation;
                IsStylusActive = localHardwareStylus.CurrentState.isActive;
                IsReplacingRightHand = localHardwareStylus.CurrentState.isOnRightHand;
            }
        }

하드웨어 리그에는 Logitech의 VrStylusHandler 클래스를 상속한 HardwareMXInkPen 클래스가 포함됩니다.

Hardware Rig Meta

그림 동기화

피드백

두 펜 모두 사용 시 오디오 및 햅틱 피드백이 발생합니다.
이를 위해 피드백을 생성해야 하는 클래스는 IFeedbackHandler 인터페이스를 참조합니다.
이 인터페이스는 XRShared 애드온에서 제공하는 BasicFeedback 클래스에 의해 구현됩니다.

Drawer Feedback

예를 들어, GrabbableColorSelection 클래스는 사용자가 펜 색상을 변경할 때 햅틱 및 오디오 피드백을 생성합니다.

C#

        [Header("Feedback")]
        IFeedbackHandler feedback;
        [SerializeField] string audioType;
        [SerializeField] FeedbackMode feedbackMode = FeedbackMode.AudioAndHaptic;

        protected virtual void Awake()
        {
        ...
            feedback = GetComponent<IFeedbackHandler>();
        ...
        }

        private void CheckColorModification()
        {
            ...
            // Audio & Haptic feedback
            if (feedback != null) 
                feedback.PlayAudioAndHapticFeeback(audioType: audioType, audioOverwrite: true, feedbackMode: feedbackMode);
            ...
        }

스티키 노트

스티키 노트는 Sticky Notes 애드온을 기반으로 제작되었습니다.

스티키 노트 표면에 그림 동기화

사용자는 손가락, 가상 펜 또는 Logitech MX Ink를 사용해 스티키 노트 표면에 그림을 그릴 수 있습니다.

스티키 노트 텍스처 동기화는 Texture Drawings addon
TextureDrawerTextureDrawing 클래스에서 처리합니다.

손가락 그림

일반 펜을 사용할 때는 애드온의 TexturePen 클래스를 사용합니다.
하지만 손가락으로 그림을 그리기 위해, 샘플은 TouchDrawing이라는 전용 클래스를 제공합니다.
이 클래스는 각 손에 대해 사용자 네트워크 리그 내 TextureDrawer 컴포넌트와 연결됩니다.

TouchDrawingITouchable 인터페이스를 구현하며, 인덱스에 위치한 Toucher (실제로는 Pincher 하위 클래스)가 포스트잇을 터치할 때 알림을 받습니다.

잘못된 선이 그려지는 것을 방지하기 위해, DetectGrabbingOrDrawingStart는 손가락 방향을 확인하여 실제로 그림을 시작하는 동작인지 확인합니다.

손가락이 표면과 접촉해 있는 동안, Draw 메서드에서 SmoothPosition이 호출되어 평균 접촉 지점을 계산하고 이를 사용합니다.
이는 손가락 추적의 불안정성으로 인해 선이 흔들리는 문제를 방지하기 위한 것으로, numberOfToucherPositionsToSmoothOn으로 조정할 수 있습니다 (0으로 설정하면 비활성화).

마지막으로, TextureDrawerAddDrawingPoint 메서드가 호출되어 실제 그림이 적용됩니다.

UI & 상호작용

스티키 노트 애드온 프리팹과 비교했을 때, 본 샘플은 다음 UI를 추가했습니다:

  • 배경색 변경
  • 그림 색상 변경
  • 그림 삭제
  • 스티키 노트 삭제
sticky note UI

펜 상호작용

스티키 노트 버튼(SpatialButton, SpatialButtonWithIcons)은 SpatialTouchable 클래스에서 ITouchable 인터페이스를 구현합니다.
펜은 Toucher 컴포넌트를 통해 스티키 노트 UI와 상호작용할 수 있으며, 이는 ITouchable 인터페이스를 가진 객체와의 충돌을 감지합니다.

virtual pen
Logitech MX Ink

손 상호작용

손가락으로 상호작용하기 위해, Toucher에서 파생된 Pincher 컴포넌트가 하드웨어 리그의 인덱스에 배치됩니다.
따라서 사용자는 IPinchable 인터페이스를 가진 객체와 상호작용하거나, Touchable 컴포넌트를 가진 객체를 터치할 수 있습니다.
이는 UI 버튼(SpatialButton, SpatialButtonWithIcons)이 SpatialTouchable 클래스에서 ITouchable을 구현하기 때문에 가능합니다.

Hardware Rig Index

HandRepresentationManager 컴포넌트는 현재 사용 중인 손 추적 모드(컨트롤러 추적 또는 손가락 추적)에 따라 어떤 손 메시를 표시할지 관리합니다.

이 샘플에서는 손가락 추적 모드가 활성화되었을 때 하드웨어 리그 손 모델을 표시하도록 선택되었습니다.

  • 로컬 손 표현:
  • 원격 사용자 손 표현: XR Hands 패키지 손 모델

손 상태 동기화에 대해서는 XR Hands Synchronization addon을 참고하세요.

여기에서 사용된 그랩 시스템은 VR Shared - Local rig grabbing에 설명된 로컬 리그 그랩 방식입니다.

스티키 노트 UI로 그림 색상 변경

스티키 노트 색상 버튼은 ColorDipper 컴포넌트를 포함합니다.
펜 쪽에서는 ColorDipperDetector가 충돌을 감지하여 ColorDipper와의 접촉 여부를 확인합니다.

Color Dipper

충돌이 발생하면, ColorDipperDetector는 새로운 색상을 IColorProvider (예: GrabbableColorSelection)에 전달하여 색상을 변경합니다.

C#

    private void OnTriggerEnter(Collider other)
    {
        if (Object && Object.HasStateAuthority)
        {
            ColorDipper colorDipper = other.GetComponentInParent<ColorDipper>();
            if (colorDipper != null)
            {
                colorChangeRequest = true;
                requestedColor = colorDipper.color;
            }
        }
    }

    public override void FixedUpdateNetwork()
    {
        base.FixedUpdateNetwork();
        if(colorChangeRequest)
        {
            colorProvider.CurrentColor = requestedColor;
            colorChangeRequest = false;
        }
    }

이후, GrabbableColorSelection은 네트워크에서 색상 변경을 동기화합니다 (OnColorChanged()).

  • 조이스틱으로 색상을 바꿀 경우도 GrabbableColorSelection이 관리
  • Logitech MX Ink의 경우 ColoredNetworkMXPen 클래스가 IColorProvider를 구현하여 버튼 입력으로 색상 변경

UI 역시 네트워크에서 동기화됩니다.
새로운 색상이 선택되면 SpatialButtonStickyNoteColorSelectionWithMenu 클래스의 ChangeDrawingColor() 메서드를 호출합니다.
StateAuthority를 확인한 후, 색상 인덱스 변수를 업데이트합니다.
이 변수는 FixedUpdateNetwork()에서 확인되어, DrawingColorMenuIndex 네트워크 변수가 업데이트되며 모든 사용자에게 전파됩니다.
그 후 OnDrawingColorMenuIndexChanged()가 호출되어 UI에 반영됩니다.

... (코드 생략 – 원문 유지)

배경색

스티키 노트 배경색 변경 과정은 위의 그림 색상 변경 과정과 유사합니다.

SpatialButtonStickyNoteColorSelectionWithMenu.ChangeStickyNoteColor() → StateAuthority 확인 → 변수 업데이트 →
FixedUpdateNetwork()에서 반영 → GrabbableColorSelection.ChangeColor() 호출 → TextureSurface 적용

... (코드 생략 – 원문 유지)

삭제

스티키 노트 삭제는 DespawnObject.DeleteObject() 호출로 처리됩니다.
사운드 피드백 재생을 위해 삭제 전에 지연이 필요합니다.

... (코드 생략 – 원문 유지)

스냅

벽 프리팹의 Static Magnet 컴포넌트에는 PostItStickingSurface 자석 레이어가 설정되어 있습니다.

Wall static magnet

따라서, 스티키 노트 프리팹의 Magnet Point에도 동일한 레이어를 설정해야 합니다.

sticky note magnet point

자세한 내용은 Magnets addon을 참고하세요.


사용된 XR Addons & Industries Addons

Fusion XR 프로젝트를 빠르게 시작할 수 있도록 여러 무료 애드온을 제공합니다.
XR Addons 참고.

여기서는 Sticky Notes SSA 샘플에 사용된 주요 애드온들을 정리합니다:

  • XRShared → 리그 동기화, 기본 상호작용 (잡기)
  • Meta Core Integration → Meta 빌딩 블록 지원
  • MXInkIntegration → Logitech MX Ink 지원
  • Voice Helpers → Photon Voice 세팅 지원
  • ConnectionManager → 애플 프로필 연결 관리 (Meta 프로필은 Network Manager가 담당)
  • ExtendedRigSelection → Apple Vision Pro / Meta Quest 리그 전환
  • Line Drawing → 펜으로 3D 그림 생성
  • Texture Drawing → 스티키 노트에 텍스처 기반 그림 동기화
  • Data Sync Helpers → 3D/2D 드로잉 및 스티키 노트 동기화
  • Blocking Contact → 스티키 노트 표면에서 펜 차단
  • Magnets → 스티키 노트를 벽에 스냅
  • Sticky Notes → 스티키 노트 생성
  • XRHands Synchronization → 손가락 상태 (포함: 제스처 감지) 동기화

서드파티 컴포넌트

Back to top