View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Sujets
  • Toutes les vidéos
  • À propos

Plus de vidéos

  • À propos
  • Résumé
  • Transcription
  • Code
  • Mieux ensemble : SwiftUI et RealityKit

    Découvrez comment combiner harmonieusement SwiftUI et RealityKit dans visionOS 26. Nous explorerons les améliorations apportées à Model3D, y compris la prise en charge de l'animation et de ConfigurationCatalog, et démontrerons des transitions en douceur vers RealityView. Vous apprendrez à tirer parti des animations SwiftUI pour piloter les modifications de composants RealityKit, à mettre en œuvre des manipulations interactives, à utiliser les nouveaux composants SwiftUI pour des interactions plus riches et à observer les modifications apportées à RealityKit depuis votre code SwiftUI. Nous verrons également comment utiliser la conversion unifiée des coordonnées pour effectuer des transformations de coordonnées entre différents frameworks.

    Chapitres

    • 0:00 - Introduction
    • 1:24 - Améliorations de Model3D
    • 6:13 - Transition vers RealityView
    • 11:52 - Manipulation d’objets
    • 15:35 - Composants SwiftUI
    • 19:08 - Flux d’informations
    • 24:56 - Conversion de coordonnées unifiées
    • 27:01 - Animation
    • 29:41 - Étapes suivantes

    Ressources

    • Canyon Crosser: Building a volumetric hike-planning app
    • Rendering hover effects in Metal immersive apps
      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC24

    • Compose interactive 3D content in Reality Composer Pro

    WWDC23

    • Discover Observation in SwiftUI

    WWDC21

    • Dive into RealityKit 2

    WWDC20

    • Data Essentials in SwiftUI
  • Rechercher dans cette vidéo…

    Bonjour. Je m’appelle Amanda et je suis ingénieure RealityKit. Et moi c’est Maks. Je suis ingénieur SwiftUI. Nous allons présenter les améliorations apportées à SwiftUI et RealityKit pour qu’ils fonctionnent encore mieux ensemble. Regardez cette scène adorable ! Nous avons un charmant robot SwiftUI qui flotte dans les airs et un robot RealityKit au sol, qui veulent faire connaissance. Quand ils se rapprochent, ça fait des étincelles ! Comment peuvent-ils se rapprocher suffisamment pour interagir ? Maks et moi allons expliquer comment combiner l’UI traditionnelle et le contenu 3D interactif. Je vais parler des améliorations apportées à Model3D. Ensuite, je vous montrerai comment passer de Model3D à RealityView, et je vous expliquerai quand choisir l’un ou l’autre. Je vais présenter la nouvelle API Object Manipulation.

    RealityKit bénéficie de nouveaux types de composants, intégrant plus d’aspects SwiftUI. Les infos circulent désormais dans les deux sens entre SwiftUI et RealityKit. Nous expliquerons comment.

    La conversion de l’espace de coordonnées est très simple. Modifiez le composant RealityKit à l’aide d’animations SwiftUI. Connectons-le tout.

    Affichez des modèles 3D dans vos apps avec une seule ligne de code grâce à Model3D. Dans visionOS 26, deux améliorations permettent d’aller plus loin avec Model3D : lire des animations et charger à partir d’un ConfigurationCatalog. Model3D étant une vue SwiftUI, il participe au système de mise en page SwiftUI. J’utilise ce système de mise en page pour créer un petit panneau affichant le nom du robot.

    Le panneau indique que ce robot s’appelle Sparky.

    Sparky fait aussi des pas de danse très sympas. L’artiste a intégré cette animation à la ressource du modèle de robot. Le type Model3DAsset est une nouveauté de visionOS 26. Chargez et contrôlez des animations sur le contenu 3D en construisant un Model3D avec un Model3DAsset. Le modèle charge les animations à partir de la ressource et permet de choisir celle à lire. « Modèle » a plusieurs significations, surtout dans cette session où nous faisons converger un framework d’UI et un framework de jeu 3D. Dans les frameworks d’UI, un « modèle » désigne la structure de données qui représente les infos utilisées par l’app. Le modèle contient les données et la logique métier, permettant à la vue d’afficher ces infos. Dans les frameworks 3D comme RealityKit, un modèle désigne un objet 3D qui peut être placé dans une scène. Vous y accédez via le ModelComponent, qui se compose d’une ressource maillée définissant sa forme et de matériaux déterminant son apparence. Cela arrive parfois. Deux mondes se rencontrent, avec leurs terminologies, et il y a parfois des chevauchements. Revenons maintenant à Sparky et à son animation.

    Je place Model3D au-dessus d’un sélecteur, d’un bouton de lecture et d’un curseur temporel. Dans mon RobotView, j’affiche le robot animé, et en dessous, je place un sélecteur pour choisir l’animation à lire, ainsi que les commandes de lecture de l’animation. D’abord, j’initialise un Model3DAsset avec le nom de la scène à charger depuis mon bundle. Une fois la ressource présente, je la transmets à l’initialiseur Model3D. En dessous, dans le VStack, je présente un sélecteur personnalisé qui répertorie les animations disponibles dans cette ressource de modèle. Lorsqu’un élément est sélectionné dans la liste, le sélecteur définit le selectedAnimation de la ressource sur la nouvelle valeur. Ensuite, le Model3DAsset crée un AnimationPlaybackController pour contrôler la lecture de l’animation choisie. La ressource fournit un animationPlaybackController. Utilisez cet objet pour mettre en pause, reprendre et rechercher dans l’animation.

    Je transmets cet animationController à ma vue RobotAnimationControls, que nous examinerons dans un instant. Dans visionOS 26, la classe RealityKit existante AnimationPlaybackController est observable. Dans ma vue SwiftUI, j’observe la propriété time pour afficher la progression de l’animation.

    J’ai une propriété @Bindable appelée controller, ce qui signifie que j’utilise AnimationPlaybackController comme modèle de données de ma vue. Quand la valeur isPlaying du contrôleur change, SwiftUI réévalue ma vue RobotAnimationControls. Un curseur affiche le moment actuel dans l’animation, par rapport à la durée totale de l’animation. Vous pouvez faire glisser ce curseur pour parcourir l’animation. Sparky fait son animation de célébration ! Je peux avancer et revenir en arrière avec le curseur. Allez Sparky, c’est ton anniversaire ! Une fois ses pas de danse appris, Sparky souhaite s’habiller avant de se rendre à la serre pour rencontrer les autres robots. Je peux l’aider grâce aux améliorations du type ConfigurationCatalog de RealityKit. Ce type stocke plusieurs représentations d’une entité, comme plusieurs géométries de maillage, valeurs de composants ou propriétés de matériaux. Dans visionOS 26, vous pouvez initialiser un Model3D avec un ConfigurationCatalog et basculer entre ses représentations.

    Pour permettre à Sparky d’essayer différentes tenues, mon artiste a regroupé un Reality File avec plusieurs types de corps. Je charge ce fichier en tant que ConfigurationCatalog à partir du bundle principal de l’app. Je crée ensuite mon Model3D avec la configuration. Cette fenêtre contextuelle montre les options de configuration. L’apparence de Sparky change selon le choix dans cette fenêtre. Des pas de danse ? C’est fait. La tenue ? C’est fait. Sparky est prêt à rencontrer son nouvel ami dans la serre RealityKit. Ça va faire des étincelles ! Pour créer ces étincelles, j’utilise un émetteur de particules. Cependant, cela n’est pas possible lors de l’exécution avec le type Model3D. L’émetteur de particules est un composant que j’ajoute à une entité RealityKit. Nous y reviendrons. Model3D ne prend pas en charge l’ajout de composants. Pour ajouter un émetteur de particules, je passe à RealityView. Je vais montrer comment remplacer mon Model3D par un RealityView sans modifier la mise en page. D’abord, je passe de Model3D à RealityView. Je charge le modèle botanist à partir du bundle d’apps dans la fermeture make de RealityView, ce qui crée une entité. J’ajoute cette entité au contenu de RealityView afin que Sparky apparaisse à l’écran.

    Mais... le panneau du nom est maintenant trop décalé sur le côté. Cela ne se produisait pas lorsque nous utilisions Model3D. Cela se produit maintenant car, par défaut, RealityView occupe tout l’espace disponible que lui accorde le système de mise en page SwiftUI. En revanche, Model3D s’ajuste en fonction de la taille intrinsèque du fichier de modèle sous-jacent. Je peux corriger cela. J’applique le nouveau modificateur .realityViewLayoutBehavior avec .fixedSize pour que RealityView s’adapte aux limites initiales du modèle. Beaucoup mieux. RealityView utilisera les limites visuelles des entités de son contenu pour déterminer sa taille. Cette dimension est évaluée une fois, après l’exécution de la fermeture make. Les autres options disponibles pour realityViewLayoutBehavior sont .flexible et .centered. Dans ces trois RealityViews, j’ai placé le bas du modèle Sparky à l’origine de la scène, et j’ai marqué cette origine avec un gadget, la petite croix multicolore indiquant les axes et l’origine. À gauche, avec l’option .flexible, RealityView se comporte comme si le modificateur n’était pas appliqué. L’origine reste au centre de la vue. L’option .centered déplace l’origine de RealityView de manière à ce que le contenu soit centré dans la vue. .fixedSize permet à RealityView d’envelopper les limites du contenu et fait en sorte que RealityView se comporte comme Model3D.

    Aucune de ces options ne repositionne ou ne redimensionne vos entités par rapport au RealityViewContent, elles repositionnent le point d’origine de RealityView. J’ai corrigé la taille de Sparky dans RealityView. Ensuite, je vais réanimer Sparky. Je passe de la nouvelle API d’animation de Model3D à une API d’animation RealityKit sur l’entité. Pour en savoir plus sur les manières de travailler avec l’animation dans RealityKit, regardez « Compose interactive 3D content in Reality Composer Pro ». J’ai basculé de Model3D à RealityView afin de pouvoir attribuer un ParticleEmitterComponent à Sparky, car des étincelles doivent jaillir lorsque ces deux robots se rapprochent. Les émetteurs de particules créent des effets avec de minuscules particules qui s’animent simultanément, comme des feux d’artifice, de la pluie ou des scintillements. RealityKit fournit des valeurs prédéfinies pour ces réglages, que vous pouvez ajuster pour obtenir l’effet souhaité. Vous pouvez utiliser Reality Composer Pro pour les concevoir, puis les configurer dans le code. Ajoutez ParticleEmitter à une entité en tant que composant. Les composants sont un élément central de RealityKit, qui repose sur le paradigme du système entités-composants. Chaque objet d’une scène est une entité, à laquelle vous ajoutez des composants pour lui attribuer des caractéristiques et des comportements. Un composant est le type qui contient des données sur une entité. Un système traite les entités qui possèdent des composants spécifiques et exécute la logique associée à ces données. Il existe des systèmes intégrés pour l’animation de particules, la gestion de la physique, le rendu et bien d’autres choses. Vous pouvez écrire votre système personnalisé dans RealityKit afin d’appliquer une logique spécifique à un jeu ou une app. Regardez « Dive into RealityKit 2 » pour découvrir en détail le système de composants d’entités dans RealityKit. J’ajoute un émetteur de particules de chaque côté de la tête de Sparky. Je crée deux entités invisibles qui serviront de conteneurs pour l’effet d’étincelles. J’ai conçu mon émetteur d’étincelles pour qu’il pointe vers la droite. Je l’ajoute directement à mon entité invisible sur le côté droit de Sparky.

    De l’autre côté, je fais pivoter l’entité de 180 degrés autour de l’axe y afin qu’elle pointe vers la gauche.

    En assemblant le tout dans RealityView, voici Sparky avec son animation, son nom à la bonne place et des étincelles qui fusent.

    RealityKit est idéal pour les créations détaillées comme celle-ci. Si vous créez un jeu ou une expérience ludique, ou pour un contrôle précis sur le comportement de votre contenu 3D, optez pour RealityView. D’autre part, utilisez Model3D pour afficher un élément 3D autonome. C’est comme la vue Image de SwiftUI, mais pour les ressources 3D.

    Les nouveaux catalogues d’animation et de configuration Model3D permettent d’aller plus loin avec Model3D. Si votre conception évolue et que vous avez besoin d’un accès direct aux entités, aux composants et aux systèmes, passez de Model3D à RealityView avec realityViewLayoutBehavior. Je vais maintenant présenter la nouvelle API Object Manipulation dans visionOS 26, qui permet de saisir les objets virtuels dans votre app. La manipulation d’objets fonctionne à la fois avec SwiftUI et RealityKit. Avec la manipulation d’objets, vous pouvez déplacer un objet d’une seule main, le faire pivoter avec une ou deux mains et le redimensionner en le pinçant et en le faisant glisser avec les deux mains. Vous pouvez même passer l’objet d’une main à l’autre.

    Il existe deux façons d’activer cette fonctionnalité, selon que l’objet est une entité RealityKit ou une vue SwiftUI. Dans SwiftUI, ajoutez le nouveau modificateur « manipulable ». Pour empêcher la mise à l’échelle, tout en ayant la possibilité de déplacer et de faire pivoter le robot avec l’une ou l’autre main, je précise les opérations prises en charge.

    Pour que le robot semble très lourd, je précise qu’il a une inertie élevée.

    Le modificateur .manipulable fonctionne lorsque Sparky est affiché dans une vue Model3D. Il s’applique à l’ensemble du Model3D ou à toute vue à laquelle il est associé. Lorsque Sparky est dans une RealityView, je souhaite activer la manipulation uniquement sur l’entité robot, pas sur l’ensemble de la RealityView. Dans visionOS 26, ManipulationComponent est un nouveau type à définir sur une entité pour activer la manipulation d’objets. La fonction statique configureEntity ajoute ManipulationComponent à votre entité. Elle ajoute aussi un CollisionComponent afin que le système d’interaction sache quand vous avez appuyé sur cette entité. Elle ajoute un InputTargetComponent qui indique au système que cette entité répond aux gestes. Enfin, elle ajoute un HoverEffectComponent qui applique un effet visuel lorsqu’une personne regarde ou passe sa souris dessus. C’est la seule ligne nécessaire pour activer la manipulation d’une entité dans votre scène. Pour personnaliser davantage l’expérience, vous pouvez définir plusieurs paramètres. Ici, je spécifie un effet de projecteur violet. J’autorise tous les types d’entrée : toucher direct, regard indirect et pincement. Je fournis des formes de collision qui définissent les dimensions extérieures du robot. Pour réagir lorsque l’on interagit avec un objet dans votre app, le système de manipulation d’objets déclenche des événements à des moments clés, par exemple au début et à la fin de l’interaction, lorsque l’entité est déplacée, pivotée ou mise à l’échelle, lorsqu’elle est relâchée ou quand elle est transférée d’une main à l’autre. Abonnez-vous à ces événements pour mettre à jour votre état. Par défaut, des sons standard sont émis au début de l’interaction, lorsqu’un transfert se produit ou que l’objet est relâché. Pour appliquer des sons personnalisés, je définis audioConfiguration sur none. Les sons standard sont désactivés. Ensuite, je m’abonne au ManipulationEvent DidHandOff, qui est livré lorsqu’une personne passe le robot d’une main à l’autre. Dans cette fermeture, je joue ma ressource audio. Bien, Maks. Le parcours de Sparky est passionnant : animation dans Model3D, découverte de sa maison dans RealityView, révélation de sa personnalité grâce à des étincelles et possibilité de l’approcher et d’interagir avec lui. Il a parcouru un long chemin pour arriver à la serre RealityKit. En effet. Pour que Sparky puisse interagir avec le robot qui l’attend, les objets de son espace virtuel requièrent de nouvelles fonctionnalités. Ils doivent répondre aux gestes, présenter des infos les concernant et déclencher des actions d’une manière qui semble native dans SwiftUI.

    Le parcours de Sparky vers la serre RealityKit consiste à établir des connexions. Une connexion profonde requiert des interactions riches. C’est exactement ce que permettent les nouveaux composants SwiftUI RealityKit. Les nouveaux composants de visionOS 26 fournissent les fonctionnalités efficaces et familières de SwiftUI aux entités RealityKit. RealityKit introduit trois composants clés : D’abord, ViewAttachmentComponent permet d’ajouter des vues SwiftUI directement à vos entités. Ensuite, GestureComponent rend vos entités réactives au toucher et aux gestes. Enfin, le composant PresentationComponent présente les vues SwiftUI, telles que les fenêtres contextuelles, depuis votre scène RealityKit.

    visionOS 1 vous permet de déclarer les pièces jointes à l’avance dans l’initialiseur RealityView. Après avoir évalué votre générateur de vue de pièce jointe, le système a appelé votre fermeture update avec les résultats sous forme d’entités. Vous pouvez ajouter ces entités à votre scène et les positionner dans l’espace 3D. Sous visionOS 26, cela est simplifié. Vous pouvez créer des pièces jointes avec un composant RealityKit n’importe où dans l’app. Créez votre ViewAttachmentComponent en lui attribuant n’importe quelle vue SwiftUI. Ajoutez-la ensuite à la collection de composants d’une entité.

    Et voilà, j’ai transféré notre NameSign de SwiftUI vers RealityKit. Voyons maintenant les gestes. Vous pouvez déjà associer des gestes à votre RealityView avec des modificateurs de geste targetedToEntity. GestureComponent est une nouveauté de visionOS 26. Tout comme ViewAttachmentComponent, vous ajoutez GestureComponent directement à vos entités, en lui transmettant des gestes SwiftUI classiques. Les valeurs des gestes sont par défaut rapportées dans l’espace de coordonnées de l’entité. Très pratique ! J’utilise GestureComponent avec un geste de tapotement pour afficher ou masquer le nom.

    Comme ceci. Ce robot s’appelle... Bolts !

    Astuce de pro : sur toute entité qui est la cible d’un geste, ajoutez aussi InputTargetComponent et CollisionComponent. Ce conseil s’applique à la fois à GestureComponent et à l’API des gestes ciblés.

    GestureComponent et ViewAttachmentComponent permettent de créer un panneau avec le nom Bolts. Cependant, Bolts se prépare pour un invité spécial : Sparky ! Bolts souhaite être à son avantage pour leur rencontre dans la serre. Il est temps de changer de tenue. Je remplace le nom de Bolts par une UI pour choisir la tenue de Bolts. C’est une décision très importante.

    Pour souligner cela, j’affiche cette UI dans une fenêtre contextuelle, avec PresentationComponent, directement depuis RealityKit.

    D’abord, je remplace ViewAttachmentComponent par PresentationComponent. Le composant utilise une liaison booléenne pour contrôler l’affichage de la fenêtre contextuelle et vous avertir lorsque quelqu’un la ferme. Le paramètre configuration correspond au type de présentation à afficher. Je spécifie popover. Dans la fenêtre contextuelle, je présente une vue avec des options de catalogue de configuration pour personnaliser Bolts. Maintenant, je peux aider Bolts à choisir la meilleure couleur pour la visite de Sparky.

    Maks, tu penses que Bolts préfère le style été ? Ou l’automne ?

    C’est une blague sur la mode.

    Bolts est sur son trente-et-un. Mais avant cela, il doit se rendre au travail. Bolts arrose les plantes dans la serre. Je vais créer une mini-carte, comme sur l’écran d’un jeu vidéo, pour suivre la position de Bolts dans la serre. Pour cela, je dois observer le composant Transform du robot. Sous visionOS 26, les entités sont désormais observables. Elles peuvent avertir d’autres codes lorsque leurs propriétés changent. Pour être averti, il suffit de lire la propriété observable d’une entité.

    Dans la propriété observable, vous pouvez surveiller les modifications de la position, l’échelle et la rotation de l’entité, de sa collection d’éléments enfants et de ses composants, y compris vos composants personnalisés. Observez ces propriétés directement à l’aide d’un bloc withObservationTracking. Vous utilisez le suivi d’observation intégré de SwiftUI. J’utilise SwiftUI pour implémenter ma mini-carte. Pour en savoir plus sur l’observation, regardez « Discover Observation in SwiftUI ». Dans cette vue, j’affiche la position de mon entité sur une mini-carte. J’accède à cette valeur observable sur mon entité. Cela indique à SwiftUI que ma vue dépend de cette valeur.

    À mesure que Bolts se déplace dans la serre pour arroser les plantes, sa position change. À chaque fois, SwiftUI appellera à nouveau le corps de ma vue, déplaçant son symbole correspondant dans la mini-carte. Pour en savoir plus sur le flux de données de SwiftUI, regardez « Data Essentials in SwiftUI ». Nos amis robots prennent vraiment forme ! Comme de la magie ! J’ai aimé ta description de la différence entre les deux termes « modèle » plus tôt. Parfois, vous devez transmettre des données de votre modèle de données à votre modèle d’objet 3D, et inversement. Sous visionOS 26, les entités observables offrent un nouvel outil pour cela. Depuis le début, vous pouvez transmettre des infos de SwiftUI à RealityKit dans la fermeture update de RealityView. Désormais, grâce à la propriété observable de l’entité, vous pouvez envoyer des infos dans l’autre sens. Les entités RealityKit peuvent se comporter comme des objets modèle pour mettre à jour vos vues SwiftUI. Ainsi, l’info peut circuler dans les deux sens : de SwiftUI à RealityKit et de RealityKit à SwiftUI. Mais... cela crée-t-il un risque de boucle infinie ? Oui. Voyons comment éviter de créer des boucles infinies entre SwiftUI et RealityKit. Lorsque vous lisez une propriété observable dans le corps d’une vue, vous créez une dépendance : votre vue dépend de cette propriété. Lorsque la valeur de la propriété change, SwiftUI met à jour la vue et réexécute son corps. RealityView présente un comportement particulier. Considérez sa fermeture update comme une extension du corps de la vue qui la contient.

    SwiftUI appelle la fermeture chaque fois que l’état de cette vue change, et pas seulement lorsque l’état explicitement observé dans cette fermeture change. Dans la fermeture update de RealityView, je modifie cette position. Cela écrira dans la valeur de position, ainsi SwiftUI mettra à jour la vue et réexécutera son corps, provoquant une boucle infinie.

    Pour éviter cela, ne modifiez pas l’état observé dans votre fermeture update.

    Vous êtes libre de modifier les entités que vous n’observez pas. Cela ne créera pas de boucle infinie, car les modifications de ces entités ne déclencheront pas la réévaluation du corps de la vue par SwiftUI. Si vous devez modifier une propriété observée, vérifiez sa valeur existante et évitez de réécrire la même valeur. Cela rompt le cycle et évite une boucle infinie. La fermeture make de RealityView est particulière. Lorsque vous accédez à une propriété observable dans la fermeture make, cela ne crée pas de dépendance. Ce n’est pas inclus dans la portée d’observation de la vue contenante. De plus, la fermeture make n’est pas réexécutée en cas de modifications. Elle ne s’exécute qu’à la première apparition de la vue contenante. Vous pouvez aussi mettre à jour les propriétés d’une entité observée à partir de votre système personnalisé. La fonction update d’un système n’est pas incluse dans l’évaluation du corps de la vue SwiftUI, c’est donc un bon endroit pour modifier les valeurs des entités observées.

    Les fermetures Gestures ne sont pas non plus incluses dans l’évaluation du corps de la vue SwiftUI. Elles sont appelées en réponse à une entrée utilisateur. Vous pouvez aussi modifier ici les valeurs de vos entités observées. Il est donc possible de modifier les entités observées à certains endroits, mais pas à d’autres.

    Si vous constatez une boucle infinie dans votre app, voici une astuce pour la corriger : Divisez vos vues plus grandes en vues plus petites et autonomes, chacune ne contenant que son propre état nécessaire. Ainsi, une modification dans une entité non liée n’entraînera pas la réévaluation de votre petite vue. C’est aussi super pour les performances. Tu sais, Maks, tu vas peut-être te rendre compte que tu n’as plus besoin d’utiliser ta fermeture update. Comme ton entité peut maintenant être l’état de ta vue, tu peux la modifier aux endroits habituels où tu modifies l’état, et te passer complètement de la fermeture update. Super ! J’ai l’impression que je vais devoir apprendre encore et encore à éviter les boucles infinies. Si je n’utilise pas de fermeture update, j’ai moins de chances de tomber sur une. Je pense qu’il est temps de réunir Bolts et Sparky. Bolts a fini de travailler, il est temps pour lui de rencontrer Sparky ! Lorsque je prends Sparky pour l’amener, et que les deux robots se rapprochent, je souhaite faire jaillir des étincelles en fonction de la distance qui les sépare. J’utilise notre nouvelle API Unified Coordinate Conversion pour cela. Sparky se trouve dans une vue Model3D SwiftUI, et Bolts est une entité dans la serre RealityKit. Je dois obtenir la distance absolue entre ces deux robots, même s’ils sont dans des espaces de coordonnées différents. Pour remédier à cela, le framework Spatial définit un protocole CoordinateSpace3D qui représente un espace de coordonnées abstrait. Vous pouvez convertir des valeurs entre deux types conformes à CoordinateSpace3D, même s’ils proviennent de frameworks différents. Les types Entity et Scene de RealityKit sont conformes à CoordinateSpace3D. Dans SwiftUI, GeometryProxy3D dispose d’une fonction .coordinateSpace3D() qui fournit son espace de coordonnées. Plusieurs types Gesture peuvent fournir leurs valeurs par rapport à n’importe quel CoordinateSpace3D. Le protocole CoordinateSpace3D convertit une valeur dans l’espace de coordonnées de Sparky en un espace de coordonnées partagé par RealityKit et SwiftUI. Il convertit ensuite le Shared Space en espace de coordonnées Bolt, tout en tenant compte des détails tels que la conversion des points en mètres et la direction des axes. Dans la vue Model3D de Sparky, chaque fois que la géométrie de la vue change, le système appelle ma fonction onGeometryChange3D. Il transmet un GeometryProxy3D que j’utilise pour obtenir son espace de coordonnées. Ensuite, je peux convertir la position de ma vue en un point dans l’espace de l’entité afin de connaître la distance qui sépare mes deux robots. Maintenant, quand Amanda rapproche Bolts et Sparky, les étincelles se multiplient. Lorsqu’elle les éloigne, les étincelles diminuent.

    Ensuite, je vais apprendre à ces robots à se déplacer ensemble et à coordonner leurs actions. J’utiliserai l’animation SwiftUI pour les composants RealityKit. SwiftUI est déjà doté d’excellentes API d’animation pour animer implicitement les modifications apportées aux propriétés de votre vue. Ici, j’anime la vue Model3D dans laquelle se trouve Sparky. Je l’envoie vers la gauche lorsque je bascule, puis il revient à sa position d’origine lorsque je bascule à nouveau. J’ajoute une animation à ma liaison isOffset et je précise que je souhaite une animation rebondissante supplémentaire. Dans visionOS 26, vous pouvez utiliser l’animation SwiftUI pour animer implicitement les modifications apportées aux composants RealityKit. Il vous suffit de définir un composant pris en charge sur votre entité dans un bloc d’animation RealityKit et le framework s’occupe du reste. Il existe deux manières d’associer une animation à un changement d’état. À partir d’une RealityView, vous pouvez utiliser content.animate() pour définir de nouvelles valeurs pour vos composants dans le bloc d’animation. RealityKit utilisera l’animation associée à la transaction SwiftUI qui a déclenché la fermeture update, qui, dans ce cas, est une animation rebondissante supplémentaire.

    L’autre méthode est d’appeler la nouvelle fonction Entity.animate(), en passant une animation SwiftUI et une fermeture qui définit de nouvelles valeurs pour vos composants. Chaque fois que la propriété isOffset change, j’envoie Sparky vers la gauche ou vers la droite en utilisant la position de l’entité. Définir la position à l’intérieur du bloc animate lance une animation implicite du composant Transform, ce qui fait que l’entité se déplace en douceur vers la nouvelle position. La puissance de l’animation implicite est mise en valeur lorsque je la combine avec l’API Object Manipulation présentée par Amanda. Je peux utiliser une animation SwiftUI pour appliquer un comportement personnalisé à Bolts. Je vais désactiver le comportement par défaut lorsqu’on relâche les objets manipulés en le définissant sur .stay. Ensuite, je m’abonnerai à l’événement WillRelease pour l’interaction de manipulation. Lorsque l’objet est sur le point d’être relâché, je ramène Sparky à sa position initiale en définissant sa transformation sur identity, ce qui réinitialise l’échelle, la translation et la rotation de l’entité. Comme je modifie la transformation de Sparky à l’intérieur du bloc animate, Sparky revient à sa position par défaut. Maintenant, l’animation de Sparky revenant à sa position d’origine est bien plus sympa. Tous ces composants RealityKit intégrés prennent en charge les animations implicites, y compris les composants Transform, Audio, Model et Light, qui ont des propriétés de couleur. Sparky et Bolts ont fait un sacré voyage. C’est formidable de voir la puissance combinée de SwiftUI et RealityKit. Grâce à cette connexion, vous pouvez développer des apps spatiales fantastiques, qui favorisent une véritable connexion entre les mondes virtuel et physique. Imaginez les possibilités en intégrant des composants SwiftUI dans vos scènes RealityKit et que des entités modifient dynamiquement l’état de votre SwiftUI. Comme Sparky et Bolts, nous espérons que vous souhaiterez connecter SwiftUI et RealityKit d’une manière inédite. Bâtissons l’avenir ensemble !

    • 1:42 - Sparky in Model3D

      struct ContentView: View {
        var body: some View {
          Model3D(named: "sparky")
        }
      }
    • 1:52 - Sparky in Model3D with a name sign

      struct ContentView: View {
        var body: some View {
          HStack {
            NameSign()
            Model3D(named: "sparky")
          }
        }
      }
    • 3:18 - Display a model asset in a Model3D and present playback controls​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotView: View {
        @State private var asset: Model3DAsset?
        var body: some View {
          if asset == nil {
            ProgressView().task { asset = try? await Model3DAsset(named: "sparky") }
          }
        }
      }
    • 3:34 - Display a model asset in a Model3D and present playback controls​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotView: View {
        @State private var asset: Model3DAsset?
        var body: some View {
          if asset == nil {
            ProgressView().task { asset = try? await Model3DAsset(named: "sparky") }
          } else if let asset {
            VStack {
              Model3D(asset: asset)
              AnimationPicker(asset: asset)
            }
          }
        }
      }
    • 4:03 - Display a model asset in a Model3D and present playback controls​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotView: View {
        @State private var asset: Model3DAsset?
        var body: some View {
          if asset == nil {
            ProgressView().task { asset = try? await Model3DAsset(named: "sparky") }
          } else if let asset {
            VStack {
              Model3D(asset: asset)
              AnimationPicker(asset: asset)
              if let animationController = asset.animationPlaybackController {
                RobotAnimationControls(playbackController: animationController)
              }
            }
          }
        }
      }
    • 4:32 - Pause, resume, stop, and change the move the play head in the animation​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotAnimationControls: View {
        @Bindable var controller: AnimationPlaybackController
      
        var body: some View {
          HStack {
            Button(controller.isPlaying ? "Pause" : "Play") {
              if controller.isPlaying { controller.pause() }
              else { controller.resume() }
            }
      
            Slider(
              value: $controller.time,
              in: 0...controller.duration
            ).id(controller)
          }
        }
      }
    • 5:41 - Load a Model3D using a ConfigurationCatalog​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct ConfigCatalogExample: View {
        @State private var configCatalog: Entity.ConfigurationCatalog?
        @State private var configurations = [String: String]()
        @State private var showConfig = false
        var body: some View {
          if let configCatalog {
            Model3D(from: configCatalog, configurations: configurations)
              .popover(isPresented: $showConfig, arrowEdge: .leading) {
                ConfigPicker(
                  name: "outfits",
                  configCatalog: configCatalog,
                  chosenConfig: $configurations["outfits"])
              }
          } else {
            ProgressView()
              .task {
                await loadConfigurationCatalog()
              }
          }
        }
      }
    • 6:51 - Switching from Model3D to RealityView

      struct RobotView: View {
        let url: URL = Bundle.main.url(forResource: "sparky", withExtension: "reality")!
      
        var body: some View {
          HStack {
            NameSign()
            RealityView { content in
              if let sparky = try? await Entity(contentsOf: url) {
                content.add(sparky)
              }
            }
          }
        }
      }
    • 7:25 - Switching from Model3D to RealityView with layout behavior

      struct RobotView: View {
        let url: URL = Bundle.main.url(forResource: "sparky", withExtension: "reality")!
      
        var body: some View {
          HStack {
            NameSign()
            RealityView { content in
              if let sparky = try? await Entity(contentsOf: url) {
                content.add(sparky)
              }
            }
            .realityViewLayoutBehavior(.fixedSize)
          }
        }
      }
    • 8:48 - Switching from Model3D to RealityView with layout behavior and RealityKit animation

      struct RobotView: View {
        let url: URL = Bundle.main.url(forResource: "sparky", withExtension: "reality")!
      
        var body: some View {
          HStack {
            NameSign()
            RealityView { content in
              if let sparky = try? await Entity(contentsOf: url) {
                content.add(sparky)
                sparky.playAnimation(getAnimation())
              }
            }
            .realityViewLayoutBehavior(.fixedSize)
          }
        }
      }
    • 10:34 - Add 2 particle emitters; one to each side of the robot's head

      func setupSparks(robotHead: Entity) {
        let leftSparks = Entity()
        let rightSparks = Entity()
      
        robotHead.addChild(leftSparks)
        robotHead.addChild(rightSparks)
      
        rightSparks.components.set(sparksComponent())
        leftSparks.components.set(sparksComponent())
      
        leftSparks.transform.rotation = simd_quatf(Rotation3D(
          angle: .degrees(180),
          axis: .y))
      
        leftSparks.transform.translation = leftEarOffset()
        rightSparks.transform.translation = rightEarOffset()
      }
      
      // Create and configure the ParticleEmitterComponent
      func sparksComponent() -> ParticleEmitterComponent { ... }
    • 12:30 - Apply the manipulable view modifier

      struct RobotView: View {
        let url: URL
        var body: some View {
          HStack {
            NameSign()
            Model3D(url: url)
              .manipulable()
          }
        }
      }
    • 12:33 - Allow translate, 1- and 2-handed rotation, but not scaling

      struct RobotView: View {
        let url: URL
        var body: some View {
          HStack {
            NameSign()
            Model3D(url: url)
              .manipulable(
                operations: [.translation,
                             .primaryRotation,
                             .secondaryRotation]
             )
          }
        }
      }
    • 12:41 - The model feels heavy with high inertia

      struct RobotView: View {
        let url: URL
        var body: some View {
          HStack {
            NameSign()
            Model3D(url: url)
              .manipulable(inertia: .high)
          }
        }
      }
    • 13:18 - Add a ManipulationComponent to an entity​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      RealityView { content in
        let sparky = await loadSparky()
        content.add(sparky)
        ManipulationComponent.configureEntity(sparky)
      }
    • 13:52 - Add a ManipulationComponent to an entity​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ with configuration

      RealityView { content in
        let sparky = await loadSparky()
        content.add(sparky)
        ManipulationComponent.configureEntity(
          sparky,
          hoverEffect: .spotlight(.init(color: .purple)),
          allowedInputTypes: .all,
          collisionShapes: myCollisionShapes()
        )
      }
    • 14:08 - Manipulation interaction events

      public enum ManipulationEvents {
      
        /// When an interaction is about to begin on a ManipulationComponent's entity
        public struct WillBegin: Event { }
        
        /// When an entity's transform was updated during a ManipulationComponent
        public struct DidUpdateTransform: Event { }
      
        /// When an entity was released
        public struct WillRelease: Event { }
      
        /// When the object has reached its destination and will no longer be updated
        public struct WillEnd: Event { }
      
        /// When the object is directly handed off from one hand to another
        public struct DidHandOff: Event { }
      }
    • 14:32 - Replace the standard sounds with custom ones

      RealityView { content in
        let sparky = await loadSparky()
        content.add(sparky)
      
        var manipulation = ManipulationComponent()
        manipulation.audioConfiguration = .none
        sparky.components.set(manipulation)
      
        didHandOff = content.subscribe(to: ManipulationEvents.DidHandOff.self) { event in
          sparky.playAudio(handoffSound)
        }
      }
    • 16:19 - Builder based attachments

      struct RealityViewAttachments: View {
        var body: some View {
          RealityView { content, attachments in
            let bolts = await loadAndSetupBolts()
            if let nameSign = attachments.entity(
              for: "name-sign"
            ) {
              content.add(nameSign)
              place(nameSign, above: bolts)
            }
            content.add(bolts)
          } attachments: {
            Attachment(id: "name-sign") {
              NameSign("Bolts")
            }
          }
          .realityViewLayoutBehavior(.centered)
        }
      }
    • 16:37 - Attachments created with ViewAttachmentComponent

      struct AttachmentComponentAttachments: View {
        var body: some View {
          RealityView { content in
            let bolts = await loadAndSetupBolts()
            let attachment = ViewAttachmentComponent(
                rootView: NameSign("Bolts"))
            let nameSign = Entity(components: attachment)
            place(nameSign, above: bolts)
            content.add(bolts)
            content.add(nameSign)
          }
          .realityViewLayoutBehavior(.centered)
        }
      }
    • 17:04 - Targeted to entity gesture API

      struct AttachmentComponentAttachments: View {
        @State private var bolts = Entity()
        @State private var nameSign = Entity()
      
        var body: some View {
          RealityView { ... }
          .realityViewLayoutBehavior(.centered)
          .gesture(
            TapGesture()
              .targetedToEntity(bolts)
              .onEnded { value in
                nameSign.isEnabled.toggle()
              }
          )
        }
      }
    • 17:10 - Gestures with GestureComponent

      struct AttachmentComponentAttachments: View {
        var body: some View {
          RealityView { content in
            let bolts = await loadAndSetupBolts()
            let attachment = ViewAttachmentComponent(
                rootView: NameSign("Bolts"))
            let nameSign = Entity(components: attachment)
            place(nameSign, above: bolts)
            bolts.components.set(GestureComponent(
              TapGesture().onEnded {
                nameSign.isEnabled.toggle()
              }
            ))
            content.add(bolts)
            content.add(nameSign)
          }
          .realityViewLayoutBehavior(.centered)
        }
      }
    • 0:00 - Introduction
    • Découvrez les améliorations de RealityKit et SwiftUI. Elles permettent une intégration transparente de l’interface utilisateur traditionnelle et du contenu 3D interactif. Parmi les principales mises à jour figurent les améliorations apportées à Model3D et RealityView, l’introduction de l’API Object Manipulation, de nouveaux types de composants, le flux de données bidirectionnel entre SwiftUI et RealityKit, une conversion plus facile de l’espace de coordonnées et des animations pilotées par SwiftUI pour les composants RealityKit.

    • 1:24 - Améliorations de Model3D
    • Dans visionOS 26, Model3D permet d’afficher des modèles 3D avec des animations et de les charger à partir d’un « ConfigurationCatalog ». Vous pouvez désormais créer des expériences 3D interactives avec seulement quelques lignes de code. Utilisez le nouveau type « Model3DAsset » pour charger et contrôler les animations, et « ConfigurationCatalog » pour basculer entre les différentes représentations d’une entité, telles que des tenues ou des types de corps. L’exemple d’un robot nommé Sparky, que vous pouvez animer, habiller de différentes tenues et contrôler à l’aide des vues et des sélecteurs SwiftUI, afin qu’il soit prêt à interagir avec d’autres robots dans une serre virtuelle, présente ces fonctionnalités.

    • 6:13 - Transition vers RealityView
    • Pour ajouter un émetteur de particules à un modèle 3D au moment de l’exécution, vous devez passer de l’utilisation de « Model3D » à « RealityView » dans RealityKit, car Model3D ne prend pas en charge l’ajout de composants. RealityView est chargé avec l’entité modèle, mais cela pose des problèmes de mise en forme, car il occupe par défaut tout l’espace disponible. Le modificateur « realityViewLayoutBehavior » est appliqué avec « fixedSize » pour résoudre ce problème, afin que RealityView s’adapte aux limites initiales du modèle. Il existe trois options « realityViewLayoutBehavior » différentes : « flexible », « centered » et « fixedSize », chacune affectant le point d’origine de RealityView sans repositionner les entités. Utilisez Reality Composer Pro pour concevoir des émetteurs de particules configurables dans le code. Les émetteurs de particules sont ajoutés aux entités en tant que composants. RealityKit est basé sur le paradigme « Entity Component System ». L’exemple ajoute des émetteurs de particules pour créer des effets d’étincelles, en utilisant des entités invisibles comme conteneurs pour les étincelles. RealityView est idéal pour la création détaillée et le contrôle fin du comportement du contenu 3D, tandis que Model3D convient à l’affichage de ressources 3D autonomes. Vous pouvez passer en douceur de l’un à l’autre, selon vos besoins.

    • 11:52 - Manipulation d’objets
    • La nouvelle API Object Manipulation de visionOS 26 permet d’interagir avec des objets virtuels dans des apps qui utilisent SwiftUI et RealityKit. Il est possible de déplacer, faire pivoter et mettre à l’échelle des objets avec les mains, et même de passer des objets d’une main à l’autre. Pour les vues SwiftUI, vous pouvez appliquer le modificateur « manipulable », ce qui permet de personnaliser les opérations prises en charge et l’inertie des objets. Dans RealityKit, ajoutez le « ManipulationComponent » aux entités via la fonction statique « configureEntity », qui ajoute également des composants de type collision, cible d’entrée et effet de survol. La fonction a plusieurs paramètres pour la personnalisation du comportement. Abonnez-vous à des « ManipulationEvents » déclenchés lors d’interactions, telles que les démarrages, les arrêts, les mises à jour, les versions et les transferts, afin de mettre à jour l’état de l’app et de personnaliser l’expérience, notamment en remplaçant les sons par défaut par des ressources audio personnalisées.

    • 15:35 - Composants SwiftUI
    • Les nouveaux composants SwiftUI et RealityKit améliorent les interactions et la connexion de l’utilisateur au sein des scènes RealityKit. Ces composants vous permettent d’intégrer en toute transparence les vues SwiftUI dans des environnements 3D. Parmi les principales fonctionnalités, citons le « ViewAttachmentComponent », pour ajouter des vues SwiftUI directement aux entités ; le « GestureComponent », pour rendre les entités réactives au toucher et aux gestes ; et le « PresentationComponent », pour présenter des vues SwiftUI, telles que des popovers, au sein de la scène. Le paramètre de configuration de « PresentationComponent » correspond au type de présentation à afficher. Ces améliorations simplifient le processus de développement, vous permettant de créer des expériences plus dynamiques et plus captivantes. Dans l’exemple, un robot nommé Bolts peut avoir un nom qui s’allume et s’éteint d’un simple geste, et il est possible de choisir la tenue de Bolts dans un menu contextuel, le tout dans l’environnement immersif de RealityKit.

    • 19:08 - Flux d’informations
    • Dans visionOS 26, les entités sont désormais observables, ce qui permet aux entités, telles que le robot Bolts, de notifier d’autres codes lorsque leurs propriétés changent. Cela est particulièrement utile pour suivre les mouvements de Bolts lorsqu’il arrose les plantes dans la serre. L’exemple utilise SwiftUI pour implémenter une mini-carte qui affiche la position de Bolts en temps réel. En accédant à la propriété de position observable de Bolts, SwiftUI met automatiquement à jour la mini-carte chaque fois que Bolts se déplace. Ce flux de données bidirectionnel entre SwiftUI et RealityKit, rendu possible par des entités observables, constitue un nouvel outil important. Cette nouvelle fonctionnalité d’observation crée aussi des boucles infinies si elle n’est pas gérée avec soin. Pour éviter ces boucles, faites attention à l’endroit où vous modifiez l’état observé. Ne modifiez pas l’état observé lors de la fermeture « update » de « RealityView », au risque de déclencher un cycle de mise à jour récursif. Au lieu de cela, effectuez des modifications dans des endroits spécifiques, tels que les systèmes personnalisés, les fermetures gestuelles ou la fermeture « make », qui ne sont pas couverts par l’évaluation du corps de la vue de SwiftUI. En décomposant les grandes vues en éléments plus petits et autonomes, et en faisant attention à l’endroit où l’état est modifié, vous pouvez garantir un flux de données fluide et efficace entre SwiftUI et RealityKit, en évitant les boucles infinies et en améliorant les performances globales de l’app.

    • 24:56 - Conversion de coordonnées unifiées
    • La nouvelle API Unified Coordinate Conversion comble le fossé entre RealityKit et SwiftUI. En implémentant le protocole « CoordinateSpace3D », vous pouvez convertir des valeurs de manière fluide entre les deux frameworks. Ainsi, vous calculez la distance absolue entre Bolts, une entité dans RealityKit, et Sparky, un Model3D dans SwiftUI, à mesure qu’ils se rapprochent les uns des autres, générant dynamiquement des étincelles en fonction de leur proximité.

    • 27:01 - Animation
    • Dans visionOS 26, vous pouvez désormais exploiter les API d’animation de SwiftUI pour animer implicitement les modifications apportées aux composants RealityKit. Cela permet d’obtenir des mouvements fluides et rebondissants des entités par simple définition des composants pris en charge dans un bloc d’animation. Pour y parvenir, il existe deux méthodes : en utilisant « content.animate() » dans un RealityView ou en appelant directement « Entity.animate() ». Cette intégration permet de personnaliser les comportements de relâchement lors de la manipulation des entités, ce qui rend les interactions avec les objets 3D plus attrayantes et plus amusantes. Différents composants RealityKit, dont Transform, Audio, Model et Light, prennent en charge ces animations implicites.

    • 29:41 - Étapes suivantes
    • Utilisez cette nouvelle connexion entre SwiftUI et RealityKit pour créer des apps spatiales innovantes en intégrant des composants SwiftUI aux scènes RealityKit, en permettant des interactions dynamiques entre les mondes virtuels et physiques, et en faisant émerger de nouvelles potentialités dans le développement d’apps.

Developer Footer

  • Vidéos
  • WWDC25
  • Mieux ensemble : SwiftUI et RealityKit
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines