스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
RealityKit으로 공간 컴퓨팅 앱 강화하기
윈도우를 넘어, RealityKit을 사용해 흥미롭고 몰입감 있는 3D 콘텐츠를 앱에 가져오는 방법을 알아보세요. SwiftUI 씬이 어떤 방식으로 RealityView와 함께 작동하는지, 어떻게 엔티티 계층에 콘텐츠를 삽입하는지도 살펴보세요. 앵커를 사용해 가상 콘텐츠와 현실을 연결하는 방법, 앱에 파티클 효과를 가져오는 방법, 비디오 콘텐츠를 추가하는 방법, 그리고 포털을 사용해 더 몰입감 넘치는 경험을 만드는 방법도 소개해 드립니다.
챕터
- 0:00 - Introduction
- 2:05 - RealityView attachments
- 6:11 - Video playback
- 10:59 - Portals
- 15:04 - Particle emitters
- 17:06 - Anchors
- 19:28 - Wrap-up
리소스
관련 비디오
WWDC23
-
다운로드
♪ 감미로운 인스트루멘탈 힙합 ♪ ♪ 안녕하세요, 유진입니다 RealityKit 팀의 엔지니어죠 지금부터 공간 컴퓨팅 앱을 더 개선할 수 있는 RealityKit의 새로운 기능을 소개해 드리려고 합니다 2019년 RealityKit가 출시된 이래 여러 앱이 풍부한 기능을 활용해 놀라운 경험을 제공하고 있죠 이제 RealityKit에 더 많은 공간 컴퓨팅 기능이 추가됩니다 포털, 파티클 이미터 RealityView 어태치먼트 등 많은 기능이 추가되죠 다른 세션인 'RealityKit로 공간 경험 빌드하기'에서 RealityKit의 기초적인 빌딩 블록을 알아보았습니다 컨테이너 객체인 엔티티와 엔티티에 특정 행동을 정의하는 컴포넌트 엔티티와 컴포넌트에 작용해 기능을 추가하는 시스템도 말이죠 SwiftUI와 RealityKit를 다리처럼 잇는 RealityView API도 살펴보았고 RealityKit 씬에 상호 작용과 애니메이션 공간 음향을 추가하는 법도 소개해 드렸죠 아직 시청하지 않은 분은 먼저 시청하시는 것을 추천합니다 이번 세션에서는 RealityKit의 새 기능으로 여러분의 앱을 더 흥미롭고 몰입감 있게 만드는 방법을 살펴볼게요 먼저 RealityView의 어태치먼트를 활용해 RealityKit 콘텐츠에 SwiftUI 뷰를 삽입하는 법을 알아보죠 다음으로 RealityKit 씬에 비디오 재생 기능을 추가하는 방법을 살펴봅시다 그리고 포털을 사용해 다른 월드로 통하는 윈도우를 여는 방법과 파티클 이미터 API를 사용해 씬의 시각 효과를 개선하는 법을 알아보죠 마지막으로 RealityKit에서 앵커를 사용해 현실의 위치에 3D 콘텐츠를 첨부하는 방법도 소개해 드릴 겁니다 그럼 RealityView 어태치먼트부터 시작하죠 RealityKit 씬에 SwiftUI 콘텐츠를 표시할 때 어태치먼트는 유용한 도구입니다 예시로 보여드린 앱에서는 어태치먼트로 지구와 달 모델 아래에 텍스트 레이블을 넣었죠 달이 지구의 조석에 어떻게 영향을 주는지 적힌 뷰도 첨부했습니다 코드로 구현한 방법을 살펴보죠 앱 내에서 RealityView를 사용해 지구 모델을 렌더링했습니다 RealityView는 RealityKit의 엔티티를 추가할 수 있는 뷰죠 엔티티를 렌더링하거나 애니메이팅, 시뮬레이팅하려면 RealityView에 엔티티를 추가해야 해요 여기서는 간단하게 지구에 쓰일 엔티티를 로딩하고 RealityView의 콘텐츠에 추가했습니다 이제 어태치먼트를 쓸 수 있게 RealityView를 바꿔 볼게요 어태치먼트는 RealityKit 콘텐츠와 관련된 특정 위치에 놓을 수 있는 뷰죠 어태치먼트 설정은 두 부분으로 나뉩니다 먼저 RealityView 생성 클로저에 매개변수가 추가됩니다 그리고 RealityView에 어태치먼트 뷰 빌더가 추가되죠 어태치먼트 뷰 빌더부터 살펴보죠 여기서 RealityKit 콘텐츠에 추가하려는 SwiftUI 뷰를 제공할 수 있습니다 예시에서는 지구 레이블을 표시할 텍스트 뷰를 추가했어요 뷰에는 태그 수정자를 추가해서 이후에 뷰가 생성 클로저에 엔티티 형태로 전송될 때 식별할 수 있도록 했죠 이 태그는 해시 가능한 값이면 무엇이든 가능합니다 여기서는 earth_label 문자열을 사용했어요 RealityView의 생성 클로저에서는 이제는 엔티티로 표시되는 뷰가 어태치먼트 매개변수에 포함됩니다 뷰를 엔티티 형태로 만들려면 entity(for:)를 어태치먼트에 호출한 다음 뷰 빌더와 earth_label을 제공했던 태그에 전달해야 합니다 그 결과 일반적인 엔티티처럼 RealityKit 콘텐츠에 추가할 수 있는 뷰 어태치먼트 엔티티가 생성됩니다 지구 아래에 레이블이 표시되게 하려면 지구 엔티티의 자식으로 어태치먼트를 추가하고 조금 아래쪽 위치로 설정합시다 이제 서로 다른 태그를 쓰는 모든 어태치먼트에 이 과정을 반복할 수 있습니다 Xcode를 살펴보죠 예시로 든 앱에서 RealityView에 어태치먼트 세 개를 추가할게요 먼저 지구 아래에 레이블을 추가합니다 달 아래에도 추가할게요 마지막으로 달의 궤도가 조석에 미치는 영향을 적은 짧은 문단을 추가하겠습니다 SwiftUI의 glassBackgroundEffect로 레이블을 꾸며보죠 RealityView 생성 클로저에서는 제 콘텐츠와 상응하는 엔티티를 추가합시다 먼저 earthAttachment를 지구 아래에 추가합니다 달 아래에도 추가할게요 마지막으로 조석을 설명하는 글을 컨테이너 엔티티 왼쪽에 놓겠습니다 앱을 빌드하고 실행해 보면 제가 생성한 어태치먼트가 모델 옆에 표시되죠 어태치먼트의 데이터 흐름을 다시 짚어 보겠습니다 어태치먼트는 RealityView의 어태치먼트 뷰 빌더에서 시작합니다 여기서 RealityKit 씬에 추가할 SwiftUI 뷰를 제공할 수 있어요 RealityView의 생성 클로저에서 어태치먼트를 엔티티 형태로 돌려받게 되고 이 엔티티를 씬에 추가할 수 있습니다 업데이트 클로저 내의 엔티티도 업데이트할 수 있어요 SwiftUI 뷰 상태가 변경되면 이 클로저가 호출되죠 이를 통해 RealityView에서 수시로 변경되는 콘텐츠에 대응할 수 있습니다 어태츠먼트 사용법을 더 자세히 알아보려면 다른 세션 'Xcode로 Reality Composer Pro 콘텐츠 작업하기'를 참고해 주세요 RealityView 어태치먼트는 씬의 다른 UI 요소에 텍스트 콘텐츠를 넣을 때 유용하죠 이에 더해 비디오를 추가해서 앱을 더 흥미롭게 만들 수도 있습니다 VideoPlayerComponent로 비디오를 추가해 보겠습니다 VideoPlayerComponent는 새로운 RealityKit 컴포넌트 형식으로 3D 씬에 비디오 콘텐츠를 삽입할 때 사용됩니다 말씀드렸듯이 컴포넌트는 엔티티에 첨부할 수 있는 특정 행동을 정의합니다 VideoPlayerComponent로 비디오를 재생하려면 먼저 리소스 번들에서 비디오 파일을 로딩해야 합니다 그리고 비디오 파일로 AVPlayer 인스턴스를 생성합니다 이 인스턴스를 사용해 VideoPlayerComponent를 생성하죠 VideoPlayerComponent를 엔티티에 첨부할 때 비디오의 화면 비율과 일치하는 직사각형의 메시가 자동으로 생성됩니다 이 행동은 SwiftUI의 VideoPlayer나 Core Animation의 AVPlayerLayer와 같은 기존 비디오 플레이어 API와 유사하죠 하지만 RealityKit는 3D 프레임워크라서 3D 공간에서 움직이고 위치를 바꿀 수 있도록 비디오가 메시를 포함한 엔티티로 표현됩니다 2D 비디오 포맷과 MV-HEVC를 사용하는 3D 비디오 등 AV Foundation에서 지원하는 모든 비디오 포맷이 VideoPlayerComponent와 함께 작동할 수 있죠 끝으로 VideoPlayerComponent는 AVPlayer를 통해 제공된 캡션을 자동으로 표시합니다 3D 비디오 등의 비디오 콘텐츠를 만드는 자세한 방법은 '공간 경험에 맞게 비디오 콘텐츠 전달하기' 세션을 참고해 주세요 제 RealityKit 씬에 비디오를 추가하려면 우선 비디오 에셋의 URL을 사용하는 AVPlayerItem을 생성해야 합니다 그리고 AVPlayer를 생성할게요 엔티티에는 방금 생성한 AVPlayer와 함께 생성된 VideoPlayerComponent를 추가할게요 VideoPlayerComponent는 자동으로 메시를 생성합니다 제 비디오의 화면 비율에 맞는 크기로 설정되죠 RealityKit는 현실의 단위로 작동하므로 비디오의 높이는 기본값으로 1m가 됩니다 다른 크기의 비디오를 만들려면 엔티티의 스케일을 조절해야 합니다 여기서는 비디오의 높이를 40cm로 설정하려고 합니다 그러니 엔티티 스케일에 0.4를 곱해 줍시다 이제 비디오를 재생할 준비가 끝났습니다 현재 항목을 AVPlayerItem에 설정하고 AVPlayer에서 play를 호출하겠습니다 이 코드로 앱을 다시 빌드하고 실행해 봅시다 씬에 비디오 엔티티를 추가하는 '더 알아보기' 버튼을 앱에 추가했습니다 버튼을 클릭하면 비디오가 페이드인됩니다 fromToByAnimation과 불투명도 컴포넌트를 사용하죠 비디오 콘텐츠로는 짧은 영상을 준비했습니다 달의 중력이 지구의 조석에 어떤 역할을 하는지 설명하는 영상이에요 한번 봅시다 달은 지구 주위를 공전합니다 달의 중력에 의한 인력은 지구 바다에 강한 힘을 가해 바다가 달의 방향으로 미세하게 부풀게 하죠 VideoPlayerComponent의 캡션 관련 설정은 시스템 설정을 따릅니다 설정 앱의 손쉬운 기능 섹션에서 캡션을 켜 보겠습니다 그로 인해 조수는 하루에 두 번씩 끝없이 순환하며 지구와 달의 계속되는 상호 작용에 따라 높아지고 낮아지게 됩니다 VideoPlayerComponent는 passthrough 틴팅도 지원합니다 이 기능이 활성화되면 passthrough 콘텐츠가 비디오 색상에 맞게 조정되죠 Apple 플랫폼의 TV 앱에서 영화나 TV 쇼를 볼 때 사용되는 방식과 동일합니다 passthrough 틴팅을 사용하려면 isPassthroughTintingEnabled 프로퍼티를 true로 설정하세요 VideoPlayerEvents를 구독하면 콘텐츠 형식이나 보기 모드 그리고 비디오 크기 같은 VideoPlayerComponent가 바뀔 때 알림을 받을 수 있습니다 이벤트를 구독하려면 RealityViews 콘텐츠에서 subscribe 함수를 호출하고 이벤트 형식과 엔티티를 지정하세요 이벤트 핸들러 클로저 내의 이벤트에도 응답할 수 있습니다 VideoPlayerComponent로 3D 씬이 훨씬 나아졌네요 지금 제 앱에는 지구와 달 모델만 있는데 모델의 배경에 우주 공간을 표시할까 합니다 우주 공간에서 공전하는 달을 방 안에 표시하는 마법 같은 윈도우를 띄울 수 있다면 정말 멋질 것 같네요 포털을 사용해 씬을 렌더링하면 구현할 수 있습니다 포털은 메시 표면을 통해 들여다볼 수 있는 다른 월드의 입구를 생성합니다 이 월드의 엔티티는 별개의 조명을 사용하고 포털의 지오메트리에 따라 마스킹되죠 예시에서는 RealityKit의 세 가지 기능을 보여 드릴 거예요 먼저 포털을 사용해 우주 공간을 렌더링하고 파티클 효과를 사용해 포털의 테두리를 장식한 다음 마지막으로 앵커를 사용해 방에 있는 벽에 포털을 배치하겠습니다 포털부터 시작하죠 포털을 만들려면 먼저 월드를 생성해야 합니다 우선 World 컴포넌트를 포함하는 엔티티를 씬에 추가합니다 World 컴포넌트는 엔티티 트리를 다른 월드 소속으로 표시하죠 월드에 속한 엔티티는 포털 표면을 통해서만 볼 수 있습니다 월드에 콘텐츠를 추가하려면 엔티티를 월드 엔티티의 자식으로 첨부해야 합니다 여기서 하늘과 지구 달의 모델을 추가해 보죠 월드 내부의 조명을 정의할 ImageBasedLight도 추가합니다 월드 엔티티의 모든 하위 요소는 월드 내부에서만 표시될 겁니다 다음은 포털을 만들겠습니다 먼저 모델 컴포넌트를 포함하는 엔티티를 추가합니다 모델 컴포넌트는 두 프로퍼티를 포함합니다 메시와 머티리얼이죠 메시에는 포털의 표면으로 기능할 원 모양의 평면을 생성합니다 머티리얼에는 새로운 포털 머티리얼을 할당해 메시가 포털로 표시될 수 있게 하겠습니다 포털을 월드와 연결하려면 포털 컴포넌트를 엔티티에 추가하고 타깃 프로퍼티를 월드 엔티티로 설정합니다 이렇게 하면 포털이 마스크로 기능해서 월드 내부의 콘텐츠를 나타낼 수 있게 됩니다 코드는 어떤 모습인지 보죠 RealityView에서 makeWorld와 makePortal을 실행할 두 가지 함수를 호출했습니다 makeWorld 함수에서는 월드 엔티티를 생성해서 포털의 콘텐츠를 배치할 거예요 makePortal 함수에서는 포털을 생성하고 방금 생성한 월드에 연결할 겁니다 마지막으로 두 엔티티를 RealityView 콘텐츠에 추가합니다 각각의 함수를 자세히 살펴보죠 makeWorld 함수 내부에서는 엔티티 하나를 생성하고 WorldComponent를 첨부합니다 그리고 ImageBasedLight로 사용할 EnvironmentResource를 로딩합시다 ImageBasedLight 컴포넌트와 ImageBasedLight ReceiverComponent를 사용해 월드에 적용할 겁니다 RealityKit의 이미지 기반 조명을 자세히 알아보려면 다른 세션 '공간 컴퓨팅을 위한 렌더링 살펴보기'를 참고하세요 이제 월드에 콘텐츠를 배치해 보죠 earth와 moon sky 모델을 로딩하고 월드의 자손으로 추가하겠습니다 이 엔티티들은 월드의 자손이므로 포털을 통해서만 볼 수 있을 겁니다 makePortal 함수로 넘어가죠 포털을 생성하려면 먼저 메시가 있어야 합니다 엔티티에 모델 컴포넌트를 만들어 메시를 생성하겠습니다 포털을 원 모양으로 만들려면 너비와 높이가 동일하고 모서리 반지름이 그 절반인 평면을 생성합니다 PortalMaterial도 생성해 ModelComponent의 머티리얼로 사용할게요 그리고 조금 전에 월드 엔티티를 생성할 때 함께 생성된 포털 컴포넌트도 첨부하겠습니다 포털 컴포넌트는 포털과 월드를 연결해 메시를 통해 월드의 콘텐츠를 들여다볼 수 있게 합니다 이제 파티클 효과를 사용해 포털의 테두리를 꾸며 보죠 RealityKit에 제공되는 ParticleEmitterComponent를 사용하겠습니다 파티클 이미터를 쓰면 스파크나 눈, 충격 효과 등 RealityKit의 여러 시각 효과를 표현할 수 있죠 파티클 이미터는 Reality Composer Pro로 생성할 수 있고 RealityKit의 ParticleEmitterComponent를 써서 런타임에 생성할 수도 있어요 여기서는 Reality Composer Pro로 미리 파티클 에셋을 만들어 두었습니다 조금 전에 생성한 포털을 이 파티클 효과로 꾸며 보죠 파티클 효과를 씬에 로딩하고 런타임에 RealityKit로 파티클 프로퍼티를 수정합시다 파티클을 시간에 따라 업데이트하기 위해 ParticleTransitionSystem이라는 커스텀 시스템을 생성했습니다 여기서 EntityQuery를 사용해 ParticleEmitterComponent를 가지는 엔티티를 찾겠습니다 시스템 업데이트 내부에서 검색을 진행한 후 검색된 엔티티에 반복을 실시합니다 각각의 엔티티에는 그다음에 실행할 updateParticles 함수를 호출합니다 RealityKit의 커스텀 시스템을 더 자세히 알아보려면 다른 세션 'RealityKit로 공간 경험 빌드하기'를 참고하세요 updateParticles 함수 내부에서는 먼저 ParticleEmitterComponent를 엔티티에서 받아 옵니다 ParticleEmitterComponent는 파티클의 외형과 행동에 관한 다양한 속성을 제어하는 여러 프로퍼티를 포함합니다 여기서 엔티티 크기에 따라 lifeSpan과 vortexStrength 프로퍼티를 설정해서 엔티티의 크기가 커질 때 파티클이 더 빠른 속도로 포털 주위를 돌게 합니다 마지막으로 컴포넌트를 다시 엔티티에 할당해 변경 사항을 적용합니다 그럼 끝이에요 파티클 이미터와 관련한 모든 프로퍼티를 살펴보려면 'Reality Composer Pro 알아보기' 세션을 참고하세요 앱의 마무리 작업이 거의 끝나갑니다 마지막으로 포털을 방에 있는 벽에 연결해 보죠 RealityKit의 앵커를 사용해 구현해 보겠습니다 앵커는 벽과 바닥 또는 머리나 손과 관련된 장소에 콘텐츠를 배치할 때 사용됩니다 RealityKit의 앵커는 두 가지 추적 모드를 지원하죠 .continuous와 .once입니다 continuous 추적 모드를 사용할 때 앵커 엔티티는 계속해서 앵커를 따라 이동합니다 예를 들면 머리가 움직일 때요 once 추적 모드를 사용할 때는 앵커 엔티티가 한번 설정된 위치에서 이동하지 않죠 엔티티가 앵커링되는 시점을 수신하려면 RealityKit에서 AnchoredStateChanged 이벤트를 구독하면 됩니다 부모 엔티티에 앵커를 사용해 3D 콘텐츠를 배치할 수는 있지만 앵커 자체의 명시적 트랜스폼은 사용자 개인정보 보호를 위해 앱에서 볼 수 없으니 유의하세요 앵커 트랜스폼에 접근하려면 ARKit를 사용해야 합니다 더 자세한 관련 정보는 '공간 컴퓨팅을 위한 ARKit 알아보기' 세션을 참고하세요 앱에서 앵커를 사용하려면 먼저 앱을 조정해서 몰입형 공간을 사용해야 합니다 몰입형 공간은 특수한 형식의 컨테이너로 앱이 윈도우 바깥의 콘텐츠를 렌더링할 수 있게 합니다 몰입형 공간을 사용하려면 ImmersiveSpace를 SwiftUI 씬에 추가해야 합니다 .immersionStyle 수정자도 추가해 mixed로 설정해 줍시다 ImmersiveSpace 내부에서 RealityView를 사용해 앵커링될 콘텐츠를 배치할 수 있습니다 몰입형 공간에 관한 더 자세한 정보는 'SwiftUI와 함께 윈도우 너머로' 세션을 참고하세요 RealityView 내부에서 앵커 엔티티를 포털의 컨테이너로 사용할 수 있습니다 앵커 엔티티를 생성하면서 콘텐츠를 앵커링할 표면의 형식을 지정합니다 여기서는 크기가 최소 1제곱미터는 되는 수직 벽을 지정할 거예요 지정한 형식에 맞는 앵커가 발견되면 RealityKit가 자동으로 벽에 콘텐츠를 연결할 겁니다 드디어 완성됐네요 이제 앱을 실행하면 벽에 연결된 포털이 나타납니다 포털, 파티클에서 앵커와 어태치먼트까지 RealityKit를 사용하면 다양한 기능으로 몰입감 넘치는 경험을 만들 수 있습니다 이번 세션의 내용을 요약해 보죠 RealityView의 어태치먼트로 SwiftUI 콘텐츠를 엔티티 계층에 삽입하여 3D 요소 옆에 UI 요소를 배치할 수 있습니다 VideoPlayerComponent, 포털 그리고 파티클 효과로는 RealityKit 씬을 개선할 동적 요소를 추가할 수 있죠 마지막으로 앵커를 사용하면 3D 콘텐츠를 벽이나 바닥 같은 현실의 표면에 연결할 수 있습니다 'RealityKit로 공간 경험 빌드하기' 세션은 엔티티, 컴포넌트 그리고 RealityView 같은 핵심 개념을 다룹니다 'Xcode로 Reality Composer Pro 콘텐츠 작업하기' 세션에서는 Reality Composer Pro와 RealityKit를 함께 사용하여 몰입형 앱을 빌드하는 과정을 알아볼 수 있습니다 여러분이 RealityKit의 새 기능을 활용해 어떤 것을 만들어 낼지 정말 기대되네요 시청해 주셔서 감사합니다 ♪
-
-
2:30 - Attachments
import SwiftUI import RealityKit struct MoonOrbit: View { var body: some View { RealityView { content, attachments in guard let earth = try? await Entity(named: "Earth") else { return } content.add(earth) if let earthAttachment = attachments.entity(for: "earth_label") { earthAttachment.position = [0, -0.15, 0] earth.addChild(earthAttachment) } } attachments: { Attachment(id: "earth_label") { Text("Earth") } } } }
-
8:03 - VideoPlayerComponent
public func makeVideoEntity() -> Entity { let entity = Entity() let asset = AVURLAsset(url: Bundle.main.url(forResource: "tides_video", withExtension: "mp4")!) let playerItem = AVPlayerItem(asset: asset) let player = AVPlayer() entity.components[VideoPlayerComponent.self] = .init(avPlayer: player) entity.scale *= 0.4 player.replaceCurrentItem(with: playerItem) player.play() return entity }
-
10:05 - Passthrough tinting
var videoPlayerComponent = VideoPlayerComponent(avPlayer: player) videoPlayerComponent.isPassthroughTintingEnabled = true entity.components[VideoPlayerComponent.self] = videoPlayerComponent
-
10:40 - VideoPlayerEvents
content.subscribe(to: VideoPlayerEvents.VideoSizeDidChange.self, on: entity) { event in // ... }
-
13:12 - Portal
struct PortalView : View { var body: some View { RealityView { content in let world = makeWorld() let portal = makePortal(world: world) content.add(world) content.add(portal) } } } public func makeWorld() -> Entity { let world = Entity() world.components[WorldComponent.self] = .init() let environment = try! EnvironmentResource.load(named: "SolarSystem") world.components[ImageBasedLightComponent.self] = .init(source: .single(environment), intensityExponent: 6) world.components[ImageBasedLightReceiverComponent.self] = .init(imageBasedLight: world) let earth = try! Entity.load(named: "Earth") let moon = try! Entity.load(named: "Moon") let sky = try! Entity.load(named: "OuterSpace") world.addChild(earth) world.addChild(moon) world.addChild(sky) return world } public func makePortal(world: Entity) -> Entity { let portal = Entity() portal.components[ModelComponent.self] = .init(mesh: .generatePlane(width: 1, height: 1, cornerRadius: 0.5), materials: [PortalMaterial()]) portal.components[PortalComponent.self] = .init(target: world) return portal }
-
15:50 - Adding particles around the portal
public class ParticleTransitionSystem: System { private static let query = EntityQuery(where: .has(ParticleEmitterComponent.self)) public func update(context: SceneUpdateContext) { let entities = context.scene.performQuery(Self.query) for entity in entities { updateParticles(entity: entity) } } } public func updateParticles(entity: Entity) { guard var particle = entity.components[ParticleEmitterComponent.self] else { return } let scale = max(entity.scale(relativeTo: nil).x, 0.3) let vortexStrength: Float = 2.0 let lifeSpan: Float = 1.0 particle.mainEmitter.vortexStrength = scale * vortexStrength particle.mainEmitter.lifeSpan = Double(scale * lifeSpan) entity.components[ParticleEmitterComponent.self] = particle }
-
18:19 - Anchoring the portal
import SwiftUI import RealityKit struct PortalApp: App { @State private var immersionStyle: ImmersionStyle = .mixed var body: some SwiftUI.Scene { ImmersiveSpace { RealityView { content in let anchor = AnchorEntity(.plane(.vertical, classification: .wall, minimumBounds: [1, 1])) content.add(anchor) anchor.addChild(makePortal()) } } .immersionStyle(selection: $immersionStyle, in: .mixed) } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.