스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
visionOS의 게임 입력 방식 살펴보기
visionOS에서 게임에 사용할 멋진 입력 방식을 디자인하고 구현하는 방법을 살펴보세요. 시스템 제스처를 활용하여 플레이어가 게임과 원활히 상호작용할 수 있게 지원하는 방법을 알아봅니다. 맞춤형 제스처 및 게임 컨트롤러를 지원하는 모범 사례도 확인해 보세요.
챕터
- 0:00 - Introduction
- 0:56 - Design around gestures
- 3:28 - System gestures
- 6:14 - Combination gestures
- 7:43 - Custom gestures
- 11:24 - Physical controllers
리소스
- Composing SwiftUI gestures
- Forum: Graphics & Games
- Human Interface Guidelines: Game controls
- Human Interface Guidelines: Gestures
관련 비디오
WWDC24
WWDC23
WWDC22
WWDC21
-
다운로드
안녕하세요, Technology Evangelism 팀의 Charlyn입니다 오늘은 visionOS의 게임 입력 옵션을 살펴보겠습니다
visionOS에서 만드는 게임과 그러한 게임 개발에 사용할 수 있는 프레임워크는 다양하게 선택할 수 있습니다
Apple Vision Pro에서 사용자는 눈과 손을 사용해 시스템의 앱과 게임을 탐색합니다 외부의 물리적 입력 기기도 지원합니다 제스처 중심으로 디자인되는 게임 입력은 플레이어가 추가 장비 없이 게임에 참여할 수 있도록 하는 가장 쉬운 방법입니다 이번 시간에는 선택 가능한 여러 제스처 유형을 설명하겠습니다 그런 다음, visionOS에서 지원하는 물리적 입력 기기를 설명하고 해당 기기에 지원을 추가하려는 경우 유념해야 할 사항을 이야기하겠습니다 visionOS에서의 입력은 직접 또는 간접 방식으로 디자인할 수 있습니다 직접 입력에서는 사용자가 직접 손을 뻗어 가상의 물체를 다룹니다 염두에 둘 점은, visionOS의 무한한 캔버스를 사용하면 게임 내 모든 물체에 닿기 위해 팔 동작이 커야할 수 있습니다 간접 입력에서는 그러한 물체를 원거리에서 조작하며 물체를 눈으로 보고 탭하는 방식을 씁니다 그러면 손을 몸과 가까이 둘 수 있죠 플레이어는 간접 입력을 통해 작은 손 동작을 큰 동작으로 증폭할 수 있습니다 따라서 플레이어가 뷰 영역에서 먼 곳에 도달하려 하거나 큰 동작을 반복해서 하는 경우 간접 입력을 사용하면 긴 플레이 세션을 훨씬 더 편하게 즐길 수 있습니다 visionOS의 퍼즐 게임인 Loona를 예로 들겠습니다 멀리 있는 각 물체를 간접 입력으로 집을 수 있습니다 물체를 보고 선택한 다음 손가락으로 조각을 움직이면 되죠 사물을 퍼즐에 직접 넣을 수도 있고 손가락으로 두 번 탭하여 제 위치에 고정할 수도 있습니다 제스처를 사용하여 퍼즐을 돌리거나 움직여 모든 각도에서 볼 수 있습니다 이렇게 하면 서거나 앉아서 편안하게 게임을 즐길 수 있습니다 편안하게 앉아 플레이하는 게임에서는 간접 입력이 뛰어난 경험을 제공합니다 하지만 직접 입력이 적절한 경우도 있습니다 활력이 넘치는 게임의 경우 가상의 물체와 직접 상호작용하는 것이 더 재밌을 수 있죠 Super Fruit Ninja는 메뉴 버튼으로 날아다니는 과일을 베는 게임이죠 이 게임은 직접 입력 중심으로 디자인되었습니다 직접 입력이 적합한 게임의 경우 다양한 피드백을 포함해야 합니다 사물과의 모든 접촉이 플레이어에게 만족감을 주어야 하니까요
여러 시각적 피드백을 조합해 볼 수 있습니다 손 뒤의 번갯불 효과나 과육과 같은 효과처럼요 삐걱거리거나 물을 튀기는 효과음을 추가할 수도 있죠 햅틱이 없어도 플레이어는 UI 버튼이 있는지 날아다니는 파인애플인지 접촉한 순간 바로 알 수 있죠 가장 바람직한 건 게임에서 직접, 간접 제스처를 모두 지원하는 것으로 다양한 방식으로 구현할 수 있습니다 게임이 내장된 시스템 제스처에 반응할 수 있게 하거나 둘 이상의 시스템 제스처를 결합하여 게임이 응답하는 입력에 더 세밀한 컨트롤을 구현할 수 있습니다 필요하다면 ARKit 손 추적을 사용하여 고유의 맞춤형 제스처를 만들 수도 있죠 결정을 내리기 전에, 내장된 시스템 사용으로 얻을 수 있는 이점을 고려하세요 플레이어들은 시스템 제스처를 어떻게 사용하는지 정확히 알고 있습니다 이미 visionOS에서 다른 여러 앱과 게임을 탐색하며 사용하고 있으니까요 사용자에게 게임과 상호작용하는 방법을 안내할 필요가 없습니다 또한 추가 장비가 없어도 바로 플레이에 돌입할 수 있죠 플레이어는 언제든지 기기를 들고 플레이하면 됩니다 또한 게임이 visionOS에서 어느 위치에 있더라도 시스템 제스처는 지원됩니다 공유 공간에서 완전 몰입형까지 모든 공간에서 작동합니다 사용할 수 있는 시스템 제스처는 다양합니다 가장 간단한 방법은 물체를 응시하며 한 손의 두 손가락을 서로 탭하는 것입니다 게임에서 입력을 위해 2D 화면을 한 번 탭하는 경우 visionOS의 탭 제스처로 대체하기만 하면 됩니다
이중 탭에 응답할 수도 있습니다 핀치 및 홀드 제스처와 핀치 및 드래그 제스처가 있습니다 두 제스처 모두 물체에 직접 또는 간접적으로 작동합니다 플레이어는 두 손을 사용하여 물체를 확대, 축소하거나 회전시킬 수 있습니다 visionOS 게임 What the Golf는 시스템 제스처를 사용하여 게임에서 물체를 간접 제어하는 훌륭한 예시입니다 공을 응시하며 핀치 및 홀드 제스처를 사용하면 공이 활성화됩니다 이제 손을 움직여 공의 방향을 제어할 수 있습니다 손을 놓으면 공에 힘이 가해집니다
제 손이 입력을 대신하므로 레벨에서 자유롭게 주변을 둘러보며 조준하는 쪽을 확인할 수 있습니다
핀치를 해제하면 공이 날아가죠, 홀인원이네요!
시스템 제스처에 응답하기는 매우 쉽습니다 RealityView에서 탭할 수 있게 하려는 모든 엔티티에는 InputTargetComponent와 CollisionComponent가 있어야 합니다 그리고 RealityView에 항목이 포함된 모든 부분에 제스처를 연결하고 해당 뷰에 있는 모든 엔티티를 탭할 수 있음을 명시합니다 제스처가 감지되면 제스처의 핸들러에서 해당 제스처에 응답할 수 있습니다 시스템 제스처 응답에 대해 자세히 알아보려면 작년 비디오인 ‘첫 몰입형 앱 개발하기’를 시청하세요 developer.apple.com 사이트에서 상호작용에 관한 문서도 살펴보세요 Unity에서 게임을 개발하는 경우 제스처의 감지 및 응답에 Unity 입력 시스템을 사용합니다 탭 이벤트를 수신하려면 게임 오브젝트에 입력 콜라이더를 추가합니다 탭 데이터는 World Touch 이벤트로 제공됩니다 자세한 내용은 ‘몰입형 Unity 앱 만들기’에서 확인하세요 시스템 제스처를 사용하면 수많은 옵션을 즉시 이용할 수 있습니다 더 유연한 개발이 필요한 경우 시스템 제스처를 함께 조합하여 독창적으로 만들 수도 있습니다 둘 이상의 시스템 제스처를 페어링하여 언제 동시에 또는 순차적으로 발생하는지 감지할 수 있습니다 제스처를 키보드 한정자와 결합할 수도 있습니다 하위 수준 접근에 SpaceEventGesture를 사용하여 탭의 시작, 이동, 끝 등의 제스처 이벤트를 수행할 수도 있습니다 이 방식은 플레이어가 두 개의 서로 다른 물체를 동시에 다루기 위해 두 손을 사용할 때 유용합니다 SpaceEventGesture를 사용하여 각 동작이 개별 목표를 추적하도록 합니다 이 예시에서는 위성을 선택하고, 움직이고 크기를 조절하는 동시에 회전시킬 수 있습니다 시스템은 각 제스처를 발생 즉시 인식합니다 이전 제스처가 아직 종료되지 않은 경우에도 마찬가지입니다 코드는 이렇게 작성합니다 사용자가 드래그 제스처를 사용하여 물체를 움직이는 경우 simultaneously(with:)를 추가하면 확대 및 회전 제스처가 동시에 발생하는 경우를 대비하여 앱이 수신 대기하게 됩니다 덕분에 여러 제스처 사이의 전환이 매끄럽게 이루어지죠 제스처 조합에 대해 자세히 알아보려면 작년 비디오인 ‘SwiftUI 한 차원 높이기’ 및 관련 문서를 살펴보시기 바랍니다 해당 자료는 플레이어, 시스템 및 제스처 조합에 익숙하므로 visionOS의 게임 입력과 관련하여 가장 많이 참조하게 될 겁니다 하지만 원하는 입력 방식을 완성해야 하는 경우 고유의 맞춤형 제스처를 정의할 수 있습니다 게임을 위해 생성하는 모든 맞춤형 제스처는 플레이어가 쉽게 배우고 기억할 수 있어야 합니다 제스처가 게임 내 동작과 잘 맞는지 확인하여 직관적인 제스처를 지원하세요 또한 맞춤형 제스처를 디자인할 때 시각 효과와 사운드를 포함한 적절한 피드백을 추가하여 플레이어가 자신의 동작이 올바른지 알 수 있게 하세요 고유의 제스처를 정의하려는 경우 ARKit을 통해 전체 손 골격 추적을 사용할 수 있습니다 동일한 게임 내에서 시스템과 맞춤형 제스처를 결합할 수 있습니다 이 예시는 Blackbox로, 제스처를 창의적으로 조합한 좋은 예입니다 Blackbox는 플레이어가 온보딩에서 주변에 새로운 버블을 만드는 맞춤형 제스처를 생성하도록 안내합니다 이 제스처는 간단하고 직관적이며 쉽게 배우고 기억할 수 있습니다 또한 중요하기도 한데 게임에서 레벨에 들어가고 나갈 때 쓰는 주요 방법이기 때문입니다 하지만 맞춤형 제스처는 조금만 사용해야 합니다 Blackbox에서는 적합한 경우라면 항상 시스템 제스처를 사용합니다 예를 들면 거품을 보고 손가락으로 탭해 터뜨리는 것처럼요
Blackbox는 visionOS의 전체 공간에 있다는 점을 기억하세요 게임이 다른 visionOS 앱과 동시에 공유 공간에 있는 경우 ARKit 및 손 추적을 사용할 수 없습니다 전체 공간에 게임만 표시되는 경우입니다 Mixed, Progressive, Full 등 스타일에 상관 없이요 Blackbox에서처럼 게임 플레이 중 주변이 표시되거나 게임이 플레이어 주변을 새 환경으로 대체할 수도 있습니다 맞춤형 제스처를 생성하는 경우 ARKit 및 전체 손 골격 추적은 전체 공간에만 적용됩니다 원 모양의 맞춤형 제스처는 이런 식으로 만들 수 있습니다 손 추적에 필요한 모든 관절을 확인하고 월드 좌표에서 해당 관절의 위치를 가져옵니다 그리고 이 원 모양의 제스처를 위해 양 검지 끝과 양 엄지 손가락 사이의 거리를 확인합니다 거리가 4cm 미만이라면 제스처가 인식되고 게임이 반응할 수 있습니다 Unity에서 게임을 개발하는 경우 Unity의 손 추적 패키지를 사용하여 플레이어의 관절 정보에 접근할 수 있습니다 추가 정보를 확인하려면 Unity 게임을 visionOS로 가져오는 방법에 관한 비디오를 확인하세요 어떤 유형의 제스처를 사용하더라도 간접 또는 직접 제스처든 시스템 또는 맞춤형이든 신체적 능력이 다른 플레이어를 위해 대체 제스처를 구현하세요 예를 들어 Synth Rider에는 손과 팔을 모두 써서 물체에 접촉하도록 디자인된 입력이 있습니다 하지만 어떤 플레이어는 한 팔로만 플레이가 가능할 수도 있습니다 이 게임에서는 ‘Comfort and Accessibility’ 설정에서 게임을 한 손 모드로 플레이하고 왼손과 오른손 중에서 어떤 손으로 플레이할지 선택할 수 있습니다 이제 오른손만 사용하여 플레이할 수 있습니다 이러한 접근성 사용 사례를 고려하는 것이 중요합니다 그리고 플레이어를 위한 여러 대안 입력 시스템을 구축하세요 RealityKit을 사용하는 경우 네이티브 손쉬운 사용 프레임워크에 접근할 수 있습니다 ‘접근성 높은 공간 경험 생성하기’ 비디오를 확인하여 다양한 능력을 지원하는 디자인에 관한 자세한 내용을 참조하세요 Unity 개발자는 Apple Accessibility 플러그인을 사용하여 모든 손쉬운 사용 기능에 접근할 수 있습니다 ‘플러그인 앤 플레이‘에서 자세한 내용을 확인하세요 시스템과 맞춤형 제스처의 유연성을 활용하면 다양한 옵션을 염두에 두고 게임의 입력을 디자인할 수 있습니다 하지만 경우에 따라 visionOS에서 사용할 수 있는 물리적 입력 기기 지원을 추가해야 할 수 있는데요 visionOS는 iOS 및 macOS 지원 게임 컨트롤러를 모두 지원합니다 Xbox Series X와 Sony Playstation DualSense 컨트롤러 등도 포함됩니다 동시에 셋 이상의 입력이 필요한 경우 게임 컨트롤러 지원이 바람직한 선택일 수 있습니다 또는 다른 플랫폼에서 게임을 가져오려는 경우 이미 컨트롤러 입력을 구현한 경우도 있을 것입니다 visionOS는 블루투스 키보드와 트랙패드도 지원하므로 이러한 입력을 사용해야 하는 게임에도 대비할 수 있습니다
visionOS에 Bluetooth로 연결된 물리적 컨트롤러와 키보드 트랙패드의 게임 입력에 간단하게 응답할 수 있습니다 같은 기기들이 iOS, iPadOS macOS 및 visionOS 전반에서 작동합니다 또한 이러한 모든 플랫폼에서 동일한 게임 컨트롤러 프레임워크를 사용합니다 따라서 여러 유형의 기기에 게임을 추가하는 경우 모든 기기에서 같은 코드가 작동합니다 또한 실제 컨트롤러는 윈도우 모드 게임부터 완전 몰입형 공간까지 visionOS의 모든 경험 유형에서 작동합니다 Arcade 게임인 Wylde Flowers는 뛰어난 컨트롤러 지원 기능을 제공합니다 게임 컨트롤러가 Vision Pro와 페어링 및 연결되어 있다면 게임 입력은 해당 컨트롤러에 맞게 자동으로 작동합니다 이는 매우 중요한 작업인데요 연결된 컨트롤러를 감지하고 해당 컨트롤러에 맞게 작동하도록 입력을 전환하는 방식이 플레이어가 게임의 설정 메뉴에서 직접 전환하도록 하는 것보다 훨씬 바람직합니다 Wylde Flowers의 다른 뛰어난 부분은 모든 작업을 컨트롤러로 할 수 있게 한 것입니다 접근에 시스템 제스처가 필요한 UI가 혼재하면 플레이어가 불편하고 혼란스러울 것입니다 게임의 모든 요소, UI까지 컨트롤러로 접근할 수 있게 입력을 디자인하세요
플레이어가 게임 컨트롤러를 연결했는지는 쉽게 감지할 수 있습니다 GCControllerDidConnect에 관찰자를 설정하기만 하면 시스템에 연결된 모든 게임 컨트롤러와 관련하여 알림을 받을 수 있습니다 GCControllerDidDisconnect 이벤트도 수신 대기해야 합니다 그래야만 게임 컨트롤러가 없는 경우를 대비하여 시스템 제스처 같은 다른 입력 형식을 준비할 수 있습니다 게임 컨트롤러가 연결되었다는 알림을 수신하면 컨트롤러에 있는 특정 버튼의 상태를 폴링할 수 있습니다 원하는 경우 변경 핸들러를 설정하여 입력 상태 업데이트 알림을 받을 수 있습니다 키보드와 트랙패드의 입력도 동일한 흐름을 따릅니다 연결을 수신 대기하면서 입력에 폴링하거나 변경 핸들러를 설정하세요 이것은 컨트롤러를 감지하고 응답하기 위한 코드입니다 관찰자를 설정하고 컨트롤러 연결 알림을 받을 때까지 기다립니다 게임 실행 전에 이미 컨트롤러가 연결되어 있는 경우에도 visionOS에 연결된 모든 기기의 알림을 여기에서 받습니다 알림을 받으면 입력 변경 핸들러를 설정하거나 예상되는 입력에 기기를 폴링합니다 visionOS 2에만 적용되는 마지막 단계가 있는데요 컨트롤러 상호작용 한정자를 게임 객체가 포함된 RealityView에 추가합니다 RealityView로 전해지는 게임 컨트롤러 입력이 게임에서 처리됨을 나타냅니다 플레이어에게 동작 피드백을 주는 것도 중요합니다 제스처의 경우처럼요 CoreHaptics 프레임워크를 사용하여 화면에 보이는 동작과 일치하도록 컨트롤러의 진동을 트리거하세요 사운드 효과도 추가하세요 플레이어의 동작이 성공적일 때는 즉시 시각 피드백을 제공하세요 게임 컨트롤러 프레임워크를 처음 사용하는 경우 유용한 리소스를 참고하실 수 있습니다 Unity 엔진 기반 게임의 경우 Unity용 게임 컨트롤러 플러그인을 사용하여 컨트롤러 입력 지원을 추가할 수 있습니다 자세한 정보는 화면의 비디오에서 확인하세요 어떤 공간이 다양한 입력 유형을 지원하는지 간단히 살펴보겠습니다 대부분의 입력 유형 즉, 시스템 제스처 기존 게임 컨트롤러 및 키보드는 모든 유형의 공간에서 지원됩니다 공유 공간의 윈도우 또는 볼륨부터 전체 공간 경험까지 모두 해당됩니다 맞춤형 제스처의 입력에 응답하려는 경우 게임을 전체 공간에 맞게 디자인해야 합니다 요약하면, 다양한 옵션을 통해 플레이어에게 적합한 입력을 게임에 구현할 수 있습니다 플레이어는 visionOS의 시스템 제스처를 손쉽게 사용할 수 있습니다 더 높은 유연성이 필요한 경우 맞춤형 제스처를 1~2개 추가하는 것도 좋습니다 특정 유형의 게임은 물리적 컨트롤러가 지원되어 입력 옵션을 확장할 수 있습니다 끝으로 iOS 또는 iPadOS에 기존 게임이 있다면 게임을 몰입감 넘치는 visionOS용 게임으로 변환하는 방법을 ‘iOS 또는 iPadOS 게임을 visionOS로 가져오기’에서 살펴보세요 시청해 주셔서 감사합니다!
-
-
5:16 - Respond to a tap gesture
// Respond to a tap gesture. struct ContentView: View { var body: some View { RealityView { content in // For entity targeting to work, entities must have an InputTargetComponent // and a CollisionComponent! } .gesture(TapGesture().targetedToAnyEntity().onEnded { value in print("Tapped entity \(value.entity)!") }) } }
-
7:08 - Combine dragging, magnification, and 3D rotation gestures
// Gesture combining dragging, magnification, and 3D rotation all at once. var manipulationGesture: some Gesture<AffineTransform3D> { DragGesture() .simultaneously(with: MagnifyGesture()) .simultaneously(with: RotateGesture3D()) .map {bgesture in let (translation, scale, rotation) = gesture.components() return AffineTransform3D( scale: scale, rotation: rotation, translation: translation ) } }
-
9:33 - Create and detect a custom circle gesture
// Create and detect a custom circle gesture. // Get all required joints and check if they are tracked. let leftHandIndexFingerTip = leftHandAnchor.skeleton.joint(named: .handIndexFingerTip) // ... // Get the position of all joints in world coordinates. let leftHandIndexFingerTipWorldPosition = matrix_multiply(leftHandAnchor.originFromAnchorTransform, leftHandIndexFingerTip.anchorFromJointTransform).columns.3.xyz // ... // Circle gesture detection is true when the distance between the index finger tips centers // and the distance between the thumb tip centers is each less than four centimeters. let isCircleShapeGesture = indexFingersDistance < 0.04 && thumbsDistance < 0.04 if isCircleShapeGesture { // respond to gesture }
-
14:00 - Detect a connected game controller
// Detect connected game controller. // Add handler for when controller connects. NotificationCenter.default.addObserver( forName: NSNotification.Name.GCControllerDidConnect, object: nil, queue: nil) { (note) in guard let _controller = note.object? as GCController else { return } // Add controller input change handlers. _controller.physicalInputProfile[GCInputButtonA]?.valueChangedHandler = { //... } } // Poll for controller input if controller.physicalInputProfile[GCInputButtonA]?.pressed {... } if controller.physicalInputProfile[GCInputButtonB]?.pressed {... }
-
14:24 - Tag a RealityView to handle controller input
// Tag a RealityView to handle controller input. struct ContentView: View { var body: some View { RealityView { content in // Tag your RealityView to respond to controller input events. } .handlesGameControllerEvents(matching .gamepad) } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.