플레이어가 주사위를 굴려 보드 위를 이동하고 카드를 모으는 게임이다.

TabletopKit은 Apple Vision Pro용 spatial multiplayer tabletop experience를 위한 framework이며 카드 게임이나 주사위 게임, 더 복잡한 보드 게임 제작도 지원한다.
TabletopKit이 제스처와 일반 레이아웃을 처리하므로 사용자를 위한 즐겁고 혁신적인 경험을 제공하는 작업에 집중할 수 있다.

TabletopKit은 GroupActivities나 RealityKit과 같이 잘 알려진 프레임워크에 자연스럽게 연동된다. 코드 몇줄로 원활한 네트워킹과 gorgeous rendering을 추가할 수 있다.

스마트한 기본값과 여러 계층의 convinience function을 제공하므로 기능이 작동하는 게임을 실행할 수 있는 동시에 자유롭게 원한느데로 커스터마이징 할 수 있어 특별한 경험을 제작할 수 있다.
TabletopKit을 이용한 Soliaire가 좋은 예시이다. 하지만 게임을 제작하려면 상당히 많은 요소를 고려해야한다. table layout, animation, physics simulation, multiplayer performance 등 말이다.

TabletopKit을 사용하면 이러한 진입 장벽을 낮출 수 있다.
오늘 보여줄 게임은 고전 보드 게임을 변형한 것. 향수와 재미를 느낄 수 있다.
게임 설정.
테이블 게임을 제작할 때는 테이블부터 시작하는 게 가장 쉽다.

그래서 테이블부터 시작하여 사용자가 앉을 자리를 결정하고 보드 위에 게임 타일을 배치하고 그 위에 사물을 추가한다.

모든 설정과 게임 플레이가 테이블 위에서 이루어진다.
Table은 반경이 정의된 원형 또는 너비와 깊이가 있는 직사각형이다.
각 게임은 테이블이 필요하다.
참여자 간 거리가 가까운 게임에는 작은 원형 테이블이 좋다.
방사형의 사각형 테이블은 보드가 크고 사물이 많은 복잡한 보드 게임에 적절하다.
테이블이 설정되면 이후의 모든 위치와 방향은 테이블의 원점과 좌표계를 기준으로 설정된다.

테이블은 간단히 정의할 수 있다. 테이블의 형태 및 크기는 제작하려는 게임의 플레이 가능한 공간을 나타낸다. 대부분 이 형태는 렌더링하려는 table entity와 일치하지만 반드시 그럴 필요는 없다.
직사각형 테이블을 만들었고 framework가 entity의 bounding box를 결정한다.

테이블을 정의한 뒤에는 테이블 주변에 자리를 배치한다. - 04:05 자막 이상
Seat는 테이블의 원점을 기준으로 배치된다. 한 시트는 한 번에 한 플레이어만 할당될 수 있다.
플레이어는 시트에 앉아야 게임과 interaction할 수 있다. 관전자는 앉지 않는다.

게임을 진행하며 players는 다른 자리로 옮길수도 있다.

이 게임에는 3명의 Player가 있다. 그래서 고유 ID를 가진 seat 3개를 테이블 주변에 같은 간격으로 배치하고 seat가 테이블 중앙을 향하도록 설정하겠다.

Multiplayer session에서는 추가 인원이 관전할 수 있지만 시트에 앉을수는 없으므로 테이블 위의 물체와 상호작용할 수 없다.
테이블 위에 있는 것은 모두 equipment다.
샘플에서는 보드, 타일, 말, 카드, 주사위가 모두 다양한 유형의 equipment에 해당한다.

pawn은 플레이어가 보드 위에서 움직이는 물체다. pawn은 게임의 물리적인 물체이며 RealityKit entity로 렌더링된다. 따라서 고유한 크기를 가지고 플레이어가 상호작용 할 수 있다.
각 말은 처음에 각 플레이어 seat 앞에 배치되며 해당 seat에 소유되어 해당 플레이어만 말을 이동할 수 있다.

샘플 프로젝트의 pawn 코드다.
pawn은 EntityEquipment 프로토콜을 따르고 이는 연결된 RealityKit entity가 있음을 의미하므로 게임에서 tangile한(형태를 가진) object이다.

초기 상태에서 말의 주요 속성을 설정했다. seatControl을 해당 seat로 제한하여 해당 플레이어만 말을 움직일 수 있다.

말의 시작 위치를 테이블을 기준으로 설정한다. 따라서 말은 처음에 각 플레이어 앞에 배치된다.

또한 entity를 전달하므로 framework가 entity의 bounding box를 결정할 수 있다.

타일은 이 게임의 또 다른 equipment이다.
떠 있는 컨베이어 벨트가 게임 보드의 역할을 하고 보드 위의 타일에서 말들이 움직인다.

각 타일은 보드 위의 특정 위치이다. 게임 동안 플레이어는 여러 타일 위로 말을 움직인다. 두 플레이어의 말이 같은 타일에 배치되면 타일 하나에 둘 이상의 말이 존재할 수 있다.

각 타일에는 카테고리가 있어 trigger할 게임 플레이와 애니메이션을 결정한다.

컨베이어 타일이고 Equiment protocol을 채택한다.

역시나 다양한 초기 셋팅이 가능한데
부모를 보드로 설정할 수 있다. 이렇게 하면 타일이 보드의 바운딩 박스 위에 위치하게 된다.

그리고 위치를 설정한다. tile은 board의 자식이므로 타일의 위치는 모두 보드의 좌표계를 기준으로 한다.

타일의 entity는 렌더링하지 않을것이므로 바운딩 박스를 명시해서 정의한다.

게임을 얼마나 자동화할지, 플레이어가 조작 가능한 범위는 어느 정도로 할 지 결정할 수 있다.
TabletopKit은 시스템 제스처를 모니터링해서 SwiftUI Scene과 동일한 제스처를 사용할 수 있다. 각 interaction마다 게임 상태를 변경하는 동작을 추가할 수 있다.
system pinch를 모니터링하고 TabletopKit interaction으로 변환하여 카드를 새 더미로 옮기는 동작을 추가할 수 있다.

system gesture는 TabletopKit interaction을 생성한다. 제스처가 변할 때마다 TabletopKit이 interaction callback을 호출한다.
콜백에서는 target Equipment와 현재 단계를 지정한다.
제스처 단계는 시스템 제스처의 단계를 제공하므로 사용자가 핀치하면 시작되고 손가락을 놓으면 종료된다.
interaction phase는 TabletopKit interaction의 단계를 제공한다. 예를 들어 사용자가 주사위를 집으면 시작되고 사용자가 던진 주사위가 테이블에 놓이면 종료된다.

제스처는 시작 될 때만 Started 단계에서 한 번 시작되고 지속되는 동안 Update 단계를 유지한다. 제스처는 언제든지 취소될 수 있다. 예를 들어 손으로 드래그 했다가 손을 등 뒤로 옮기면 취소된다.
취소는 intentional ended와 다르다. 사물을 높으려고 핀치를 해제하는 것은 의도적인 종료이다.

게임을 RealityView에 연결할 때 TabletopInteracion object를 구현한 결과물을 전달한다.

제스처가 업데이트 될 때마다 업데이트 콜백이 호출된다. 컨텍스트에는 관련된 구성요소나 배치할 수 있는 위치 등 수정 가능한 속성이 있다. 또한 interaction을 취소하거나 종료하는 등의 동작을 수행하는 함수가 있다.

값은 읽을 수 있는 정보이며 제스처나 상호작용 단계, proposed destination그리고 관련된 자세 등이다.

각 interaction update마다 게임 상태를 변경할 수 있다.
Action은 게임 상태에 적용되는 개별 동작으로 사물을 새 그룹으로 이동하거나 카드를 뒤집는 동작 등이다.
동작은 요청될 때마다 queue에 추가되며 하나씩 적용된다.

흔한 예시는 부모 간 객체 이동이다. 이 코드 snippet에는 상호작용할 수 있는 모든 객체가 유효한 모든 부모 구성 요소에 할당될 수 있도록 했다.

그래서 제스처가 종료되었다는 콜백을 수신하면 요청한 부모가 유효한 값 중에 있는지 확인한 다음

존재한다면 상호작용 컨텍스트에 동작을 추가해서 해당 equipment를 요청된 부모 equipment로 이동한다. 따라서 플레이어가 게임 보드에서 말을 움직이고 카드를 손으로 가져오고 주사위를 던질 수 있다.

TabletopKit은 움직인 모든 요소에 대한 정보를 제공하므로 올바른 동작과 발생하지 않아야 할 동작을 파악할 수 있다. 이를 통해 튜토리얼 같은 것을 만들 수 있다.

Game mechanic은 재밌는 경험에 중요한 요소지만, 플레이어 간의 역학 관계도 그만큼 중요하다. 각 게임마다 특별한 조합이 필요하다.
그림자와 조명이 있는 초현실적인 모델이나 탁월한 스타일의 애니메이션 제공 가능. 제작된 모든 것은 RealityKit이 렌더링 할 수 있다.
Entity를 직접 로드하므로 여기에 원하는 모든 특수 효과를 추가하여 interaction이 일어날 때 트리거 할 수 있다. RealityKit에서는 아주 쉽게 효과음을 재생할 수 있다.
주사위 굴리는 소리를 추가해보겠다.

audioLibraryComponent에서 효과음을 찾은 뒤 soundResource가 있으면 RealityKit이 주사위 entity에서 효과음을 재생하도록 지시한다. RealityKit은 Spatial Audio를 처리할 수 있으므로 공간 내에서 효과음이 발생한다.

Spatial Persona를 통해 한 방에 있지 않더라도 친구 또는 가족과 함께 게임을 플레이 할 수 있다.

기본 seat 설정을 사용하는 네트워크 게임의 경우 단 몇 줄의 코드만으로 GroupActivities 세션을 시작해 framework에 전달할 수 있다.
그 후에는 멋진 신규 기능과 custom spatial template를 활용해 경험을 customizing하고 플레이어와 관전자를 방에서 원하는 위치에 배치할 수 있다.

TabletopKit이 멀티플레이어 네트워킹을 처리해 주며 아주 쉽게 설정할 수 있다.
이 framework는 동작을 동기화해서 모든 플레이어의 게임 상태를 일치시킨다.

플레이어가 카드를 집는 동작을 전송하면 동작은 검증을 거친 다음 determistic 순서로 게임 상태에 추가된다.

애니메이션이나 물리 시뮬레이션 등 성능 제약이 많은 일부 동작은 각 플레이어별로 로컬로 처리되므로 멀티 플레이어 환경이 빠르고 원활하게 유지된다.

원할 때 shareplay 시작할 수 있도록 toolbar에 추가

세션이 활성화되면 GroupActivities 세션과 상호작용하도록 TabletopKit에 알린다. 이렇게 하면 활성 플레이어 간 게임 상태가 동기화 된다.

게임에 특별한 공간 레이아웃을 사용하려면 custom spatial persona template API를 사용하면 된다.
기본적으로 테이블 중앙을 바라보게 되어있다.

다른 공간 설정을 사용하려면 custom spatial persona template API로 원하는 템플릿을 설정하면 TabletopKit에서 설정하는 기본 템플릿을 오버라이드 한다.

