Ceci est la deuxième partie de notre série d’introduction sur SceneKit. Dans ce tutoriel, je suppose que vous connaissez les concepts expliqués dans la première partie, notamment la configuration d’une scène avec des lumières, des ombres, des caméras, des nœuds et des matériaux..
Dans ce didacticiel, je vais vous expliquer certaines des fonctionnalités les plus compliquées, mais aussi les plus utiles, de SceneKit, telles que l'animation, l'interaction utilisateur, les systèmes de particules et la physique. En implémentant ces fonctionnalités, vous pouvez créer un contenu 3D interactif et dynamique plutôt que des objets statiques comme vous l'aviez fait dans le précédent tutoriel..
Créez un nouveau projet Xcode basé sur le iOS> Application> Application à vue unique modèle.
Nommez le projet, définissez La langue à Rapide, et Dispositifs à Universel.
Ouvrir ViewController.swift et importer le framework SceneKit.
importer un UIKit importer SceneKit
Ensuite, déclarez les propriétés suivantes dans le ViewController
classe.
var sceneView: SCNView! var camera: SCNNode! masse var: SCNNode! var light: SCNNode! bouton var: SCNNode! var sphere1: SCNNode! var sphere2: SCNNode!
Nous montons la scène dans le viewDidLoad
méthode comme indiqué ci-dessous.
remplacer func viewDidLoad () super.viewDidLoad () sceneView = SCNView (cadre: self.view.frame) sceneView.scene = SCNScene () self.view.addSubview (sceneView) let groundGeometry = SCNFloor () groundGeometry.reflectivity = 0 let groundMaterial = SCNMaterial () groundMaterial.diffuse.contents = UIColor.blueColor () groundGeometry.materials = [groundMaterial] ground = SCNNode (geometry: groundGeometry) laissez camera = SCNCamera () camera.zFar = 10000 auto.camera = SCNN .camera.camera = camera self.camera.position = SCNVector3 (x: -20, y: 15, z: 20) let constraint = SCNLookAtConstraint (cible: masse) constraint.gimbalLockEnabled = true self.camera.constraints = [constraint] laissez ambientLight = SCNLight () ambientLight.color = UIColor.darkGrayColor () ambientLight.type = SCNLightTypeAmbient self.camera.light = ambientLight laissez spotLight = SCNLight () spotLight.type = SCNLightTypeSpot spotLight.hall spotOuterAngle = 90.0 spotLight.zFar = 500 l ight = SCNNode () light.light = spotLight light.position = SCNVector3 (x: 0, y: 25, z: 25) light.constraints = [contrainte] let sphereGeometry = SCNSphere (rayon: 1.5) laissez sphereMaterial = SCNMaterial () sphereMaterial.diffuse.contents = UIColor.greenColor () sphereGeometry.materials = [sphereMaterial] sphere1 = SCNNode (geometry: sphereGeometry) sphere1.position = SCNVector3 (x: -15, y: 1.5, z: 0) sphere2 = SCNNode (geometry : sphereGeometry) sphere2.position = SCNVector3 (x: 15, y: 1.5, z: 0) let buttonGeometry = SCNBox (largeur: 4, hauteur: 1, longueur: 4, chanfrein: 0) let buttonMaterial = SCNMaterial () buttonMaterial. diffuse.contents = UIColor.redColor () buttonGeometry.materials = [buttonMaterial] button = SCNNode (geometry: buttonGeometry) button.position = SCNVector3 (x: 0, y: 0.5, z: 15) sceneView.scene? .rootNode.addildNode (self.camera) sceneView.scene? .rootNode.addChildNode (sol) sceneView.scene? .rootNode.addChildNode (light) sceneView.scene? .rootNode.addChildNode (bouton) sceneView.scen e? .rootNode.addChildNode (sphere1) sceneView.scene? .rootNode.addChildNode (sphere2)
L'implémentation de viewDidLoad
devrait sembler familier si vous avez lu la première partie de cette série. Nous ne faisons que configurer la scène que nous allons utiliser dans ce tutoriel. Les seules nouveautés incluent le SCNFloor
la classe et la zFar
propriété.
Comme son nom l'indique, le SCNFloor
La classe est utilisée pour créer un sol ou un sol pour la scène. C’est beaucoup plus facile que de créer et de faire pivoter un SCNPlane
comme nous l'avons fait dans le précédent tutoriel.
le zFar
propriété détermine à quelle distance une caméra peut voir ou à quelle distance la lumière d'une source particulière peut atteindre. Construisez et exécutez votre application. Votre scène devrait ressembler à ceci:
L’interaction de l’utilisateur est gérée dans SceneKit par une combinaison des éléments suivants: UIGestureRecognizer
classe et hit tests. Pour détecter un tap, par exemple, vous ajoutez d’abord un UITapGestureRecognizer
à un SCNView
, déterminer la position du tap dans la vue et voir s'il est en contact avec ou touche l'un des nœuds.
Pour mieux comprendre comment cela fonctionne, nous allons utiliser un exemple. Chaque fois qu'un nœud est exploité, nous le retirons de la scène. Ajoutez l'extrait de code suivant au viewDidLoad
méthode du ViewController
classe:
Remplacez func viewDidLoad () super.viewDidLoad () sceneView = SCNView (cadre: self.view.frame) sceneView.scene = SCNScene () self.view.addSubview (sceneView) laissez tapRecognizer = UITapGestureRecognizer () tapRecognizer.numberOfTapsRedired, une valeur .numberOfTouchesRequired = 1 tapRecognizer.addTarget (self, action: "sceneTapped:") sceneView.gestureRecognizers = [tapRecognizer]…
Ensuite, ajoutez la méthode suivante à la ViewController
classe:
func sceneTapped (Recognizer: UITapGestureRecognizer) let location = Recognizer.locationInView (sceneView) Let hitResults = sceneView.hitTest (location, options: nil) si hitResults? .count> 0 laissez résultat = hitResults! [0] comme! SCNHitTestResult let node = result.node node.removeFromParentNode ()
Dans cette méthode, vous obtenez d’abord l’emplacement du tap sous forme de CGPoint
. Ensuite, vous utilisez ce point pour effectuer un test de hit sur le sceneView
objet et stocker le SCNHitTestResult
objets dans un tableau appelé hitResults
. le options
Le paramètre de cette méthode peut contenir un dictionnaire de clés et de valeurs, que vous pouvez lire dans la documentation de Apple. Nous vérifions ensuite si le test d’atteinte a renvoyé au moins un résultat et, s’il le faisait, nous supprimons le premier élément du tableau de son nœud parent..
Si le test d'impact a donné plusieurs résultats, les objets sont triés en fonction de leur position z, c'est-à-dire de l'ordre dans lequel ils apparaissent du point de vue de la caméra en cours. Par exemple, dans la scène actuelle, si vous tapez sur l'une des deux sphères ou sur le bouton, le nœud que vous avez tapé formera le premier élément du tableau retourné. Étant donné que le sol apparaît directement derrière ces objets du point de vue de la caméra, le nœud de sol sera un autre élément du tableau de résultats, le deuxième dans ce cas. Cela se produit car un tap au même endroit toucherait le nœud de sol si les sphères et le bouton n'étaient pas là.
Générez et exécutez votre application, puis appuyez sur les objets de la scène. Ils devraient disparaître lorsque vous appuyez sur chacun d'eux.
Maintenant que nous pouvons déterminer quand un nœud est exploité, nous pouvons commencer à ajouter des animations au mélange.
Deux classes peuvent être utilisées pour exécuter des animations dans SceneKit:
SCNAction
SCNTransaction
SCNAction
Les objets sont très utiles pour les animations simples et réutilisables, telles que le mouvement, la rotation et l’échelle. Vous pouvez combiner un nombre illimité d'actions dans un objet d'action personnalisé..
le SCNTransaction
La classe peut exécuter les mêmes animations, mais elle est plus polyvalente à certains égards, comme l’animation de matériaux. Cette polyvalence supplémentaire se fait toutefois au détriment de SCNTransaction
les animations ayant uniquement la même capacité de réutilisation en tant que fonction et la configuration étant effectuée via des méthodes de classe.
Pour votre première animation, je vais vous montrer le code en utilisant les deux SCNAction
et SCNTransaction
Des classes. L'exemple déplacera votre bouton vers le bas et le rendra blanc lorsqu'il sera tapé. Mettre à jour la mise en œuvre de la sceneTapped (_ :)
méthode comme indiqué ci-dessous.
func sceneTapped (Recognizer: UITapGestureRecognizer) let location = Recognizer.locationInView (sceneView) Let hitResults = sceneView.hitTest (location, options: nil) si hitResults? .count> 0 laissez résultat = hitResults! [0] comme! SCNHitTestResult let node = result.node si node == bouton SCNTransaction.begin () SCNTransaction.setAnimationDuration (0.5) laisse materials = node.geometry? .Materials as! [SCNMaterial] let matériau = matériaux [0] matériau.diffuse.contents = UIColor.whiteColor () SCNTransaction.commit () let action = SCNAction.moveByX (0, y: -0,8, z: 0, durée: 0,5) noeud. runAction (action)
dans le sceneTapped (_ :)
méthode, nous obtenons une référence au nœud que l'utilisateur a tapé et vérifions s'il s'agit du bouton de la scène. Si c’est le cas, nous animons son matériau du rouge au blanc, en utilisant le SCNTransaction
classe et déplacez-le le long de l’axe des y dans une direction négative en utilisant un SCNAction
exemple. La durée de l'animation est fixée à 0,5 seconde..
Générez et exécutez à nouveau votre application, puis appuyez sur le bouton. Il devrait descendre et changer sa couleur en blanc comme indiqué dans la capture d'écran ci-dessous.
L'installation de simulations physiques réalistes est facile avec le framework SceneKit. Les fonctionnalités offertes par les simulations physiques de SceneKit sont très étendues, allant des vitesses, accélérations et forces de base aux champs gravitationnels et électriques, voire à la détection de collision..
Dans la scène actuelle, appliquez un champ gravitationnel à l’une des sphères de manière à ce que la seconde sphère soit tirée vers la première par la gravité. Cette force de gravité devient active lorsque vous appuyez sur le bouton.
La configuration de cette simulation est très simple. Utiliser un SCNPhysicsBody
objet pour chaque nœud que vous voulez être affecté par la simulation physique et un SCNPhysicsField
objet pour chaque nœud que vous voulez être la source d'un champ. Mettre à jour le viewDidLoad
méthode comme indiqué ci-dessous.
func viewDidLoad () … buttonGeometry.materials = [buttonMaterial] button = SCNNode (geometry: buttonGeometry) button.position = SCNVector3 (x: 0, y: 0.5, z: 15) // Physique a laissé groundShape = SCNPhysicsShape (geometry: groundGeometry, options: nil) let groundBody = SCNPhysicsBody (type: .Kinematic, shape: groundShape) ground.physicsBody = groundBody laisse gravityField = SCNPhysicsField.radialGravityField () gravityField.strength = 0 sphere1.physics sphereGeometry, options: nil) let sphère1Body = SCNPhysicsBody (type: .Kinématique, forme: forme) sphere1.physicsBody = sphère1Body let sphère2Body = SCNPhysicsBody (type: .Dynamique, forme: forme) sphere2.physicsBody = sphere2Body scene. .addChildNode (self.camera) sceneView.scene? .rootNode.addChildNode (sol) sceneView.scene? .rootNode.addChildNode (light)…
Nous commençons par créer un SCNPhysicsShape
instance qui spécifie la forme réelle de l'objet qui participe à la simulation physique. Pour les formes de base que vous utilisez dans cette scène, les objets géométriques sont parfaitement adaptés. Pour les modèles 3D complexes, cependant, il est préférable de combiner plusieurs formes primitives afin de créer une forme approximative de votre objet pour la simulation physique..
A partir de cette forme, vous créez ensuite un SCNPhysicsBody
exemple et l'ajouter au sol de la scène. Cela est nécessaire car chaque scène SceneKit a par défaut un champ de gravité existant qui tire chaque objet vers le bas. le Cinématique
tapez ce que vous donnez à cette SCNPhysicsBody
signifie que l'objet prendra part aux collisions, mais n'est pas affecté par les forces (et ne tombera pas à cause de la gravité).
Ensuite, vous créez le champ gravitationnel et l'affectez au premier nœud de la sphère. En suivant le même processus que pour le terrain, vous créez ensuite un corps physique pour chacune des deux sphères. Vous spécifiez la deuxième sphère en tant que Dynamique
corps physique cependant, parce que vous voulez qu'il soit affecté et déplacé par le champ gravitationnel que vous avez créé.
Enfin, vous devez définir la force de ce champ pour l'activer lorsque vous appuyez sur le bouton. Ajouter la ligne suivante au sceneTapped (_ :)
méthode:
func sceneTapped (reconnaissance: UITapGestureRecognizer) … si noeud == bouton … sphere1.physicsField? .strength = 750
Construisez et exécutez votre application, appuyez sur le bouton et observez la seconde accélération s’accélérer progressivement vers la première. Notez que cela peut prendre quelques secondes avant que la deuxième sphère ne commence à bouger.
Cependant, il ne reste plus qu’une chose à faire: faire exploser les sphères lorsqu’elles se heurtent.
Pour créer l'effet d'une explosion, nous allons tirer parti de la SCNParticleSystem
classe. Un système de particules peut être créé par un programme 3D externe, un code source ou, comme je vais vous le montrer, l'éditeur de système de particules de Xcode. Créez un nouveau fichier en appuyant sur Commande + N et choisir Système de particules SceneKit du iOS> Ressource section.
Définissez le modèle de système de particules sur Réacteur.Cliquez sur Suivant, nommer le fichier Explosion, et enregistrez-le dans votre dossier de projet.
dans le Navigateur de projet, vous verrez maintenant deux nouveaux fichiers, Explosion.scnp et spark.png. le spark.png image est une ressource utilisée par le système de particules, ajoutée automatiquement à votre projet. Si vous ouvrez Explosion.scnp, vous verrez qu'il est animé et rendu en temps réel dans Xcode. L'éditeur de système de particules est un outil très puissant dans Xcode et vous permet de personnaliser un système de particules sans avoir à le faire par programme..
Avec le système de particules ouvert, allez à la Inspecteur d'attributs à droite et changez les attributs suivants dans le Émetteur section:
Changer les attributs suivants dans la Simulation section:
Et enfin, changez les attributs suivants dans le Cycle de la vie section:
Votre système de particules devrait maintenant sortir dans toutes les directions et ressembler à la capture d'écran suivante:
Ouvrir ViewController.swift et faites votre ViewController
classe conforme à la SCNPhysicsContactDelegate
protocole. L'adoption de ce protocole est nécessaire pour détecter une collision entre deux nœuds.
Classe ViewController: UIViewController, SCNPhysicsContactDelegate
Ensuite, assigner le courant ViewController
par exemple comme contactDélégué
de votre monde physique
objet dans le viewDidLoad
méthode.
remplacez func viewDidLoad () super.viewDidLoad () sceneView = SCNView (cadre: self.view.frame) sceneView.scene = SCNScene () sceneView.scene? .physicsWorld.contactDelegate = self self.view.addSubview (sceneView)…
Enfin, implémentez le physicsWorld (_: didUpdateContact :)
méthode dans le ViewController
classe:
func physicsWorld (world: SCNPhysicsWorld, didUpdateContact contact: SCNPhysicsContact) if (contact.nodeA == sphère1 || contact.nodeA == sphère2) && (contact.nodeB == sphère1 || contact.nodeB == sphère2) = SCNParticleSystem (nommé: "Explosion", inDirectory: nil) let systemNode = SCNNode () systemNode.addParticleSystem (particuleSystem) systemNode.position = contact.nodeA.position sceneView.scene? .RootNode.addChildNode (systemNode) contact.nodeA. () contact.nodeB.removeFromParentNode ()
Nous vérifions d’abord si les deux nœuds impliqués dans la collision sont les deux sphères. Si tel est le cas, nous chargeons le système de particules à partir du fichier que nous avons créé il y a un instant et l'ajoutons à un nouveau nœud. Enfin, nous retirons les deux sphères impliquées dans la collision..
Générez et exécutez à nouveau votre application, puis appuyez sur le bouton. Lorsque les sphères entrent en contact, elles doivent toutes les deux disparaître et votre système de particules doit apparaître et s’animer..
Dans ce tutoriel, je vous ai montré comment implémenter des interactions utilisateur, des animations, des simulations physiques et des systèmes de particules à l'aide du framework SceneKit. Les techniques que vous avez apprises dans cette série peuvent être appliquées à n’importe quel projet avec un nombre illimité d’animations, de simulations physiques, etc..
Vous devriez maintenant être à l'aise pour créer une scène simple et y ajouter des éléments dynamiques, tels que des systèmes d'animation et de particules. Les concepts que vous avez appris dans cette série s’appliquent à la plus petite scène avec un seul objet jusqu’à un jeu à grande échelle.