Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Poke Your Own Mongo

Poke Your Own Mongo

360iDev Min presentation on creating a basic augmented reality app with SpriteKit using iOS 10 and Swift 3.

Sarah E. Olson

October 28, 2016
Tweet

More Decks by Sarah E. Olson

Other Decks in Technology

Transcript

  1. SARAH E. OLSON iOS Engineer at Trello Director of Women

    Who Code Twin Cities Twitter: @saraheolson
  2. WHAT IS AUGMENTED REALITY (AR)? Augmented reality (AR) is a

    live direct or indirect view of a physical, real-world environment whose elements are augmented (or supplemented) by computer-generated sensory input such as sound, video, graphics or GPS data. https://en.wikipedia.org/wiki/Augmented_reality
  3. WHAT IS VIRTUAL REALITY (VR)? Virtual reality (VR) typically refers

    to computer technologies that use software to generate realistic images, sounds and other sensations that replicate a real environment (or create an imaginary setting), and simulate a user's physical presence in this environment, by enabling the user to interact with this space and any objects depicted therein using specialized display screens or projectors and other devices. https://en.wikipedia.org/wiki/Virtual_reality
  4. BUILDING OUR OWN APP Let’s start simple using built-in frameworks.

    ➤ SpriteKit — to create our 2D game. ➤ Camera — to augment our reality ➤ No location-based functionality.
  5. CREATING OUR MONSTER let monster = SKSpriteNode(imageNamed: "PurpleMonster1") monster.position =

    CGPoint(x: 0, y: frame.height/2) monster.zPosition = 2 addChild(monster)
  6. MOVING OUR MONSTER // Moves the monster to the right

    let moveRight = SKAction.move(to: CGPoint(x: self.size.width/2, y: self.size.height/2), duration: 1.0) // Moves the monster to the left let moveLeft = SKAction.move(to: CGPoint(x: -(self.size.width/2), y: self.size.height/2), duration: 1.0) // Groups the left and right actions together let moveSequence = SKAction.sequence([moveLeft, moveRight]) // Repeats the group of actions forever let repeatMovesForever = SKAction.repeatForever(moveSequence) // Runs the repeated group of actions monster.run(repeatMovesForever)
  7. ANIMATING NODES // Create the monster from an atlas instead

    of an image let textureAtlas = SKTextureAtlas(named: “PurpleMonster.atlas") let spriteArray = [textureAtlas.textureNamed("PurpleMonster1"), textureAtlas.textureNamed("PurpleMonster2")] monster = SKSpriteNode(texture: spriteArray[0]) // Animate our monster let animateAction = SKAction.animate(with: spriteArray, timePerFrame: 0.2) let repeatAnimation = SKAction.repeatForever(animateAction) monster.run(repeatAnimation)
  8. CREATING OUR BALL // Create the ball node let ball

    = SKSpriteNode(imageNamed: "Ball") // Set position and scale ball.position = CGPoint(x: 0, y: 100) ball.zPosition = 2 ball.scale(to: CGSize(width: 50, height: 50)) // Add to the scene addChild(ball)
  9. MOVING OUR BALL // Defines where user touched var startTouchLocation:

    CGPoint = CGPoint.zero var endTouchLocation: CGPoint = CGPoint.zero /** * Called when a touch event is initiated. */ override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // Record the beginning of the touch event startTouchLocation = touches.first!.location(in: self) } /** * Called when a touch event moves. */ override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { // Move the ball along with the user's touch gesture let currentTouchLocation = touches.first!.location(in: self) if let ball = ball { ball.position = currentTouchLocation } } /** * Called when a touch event is ended. */ override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { // Record the end of the touch event endTouchLocation = touches.first!.location(in: self) }
  10. PHYSICS // Add physics to our ball when created ball.physicsBody

    = SKPhysicsBody(circleOfRadius: ball.frame.size.width/2) ball.physicsBody?.isDynamic = true ball.physicsBody?.affectedByGravity = false ball.physicsBody?.allowsRotation = false ball.physicsBody?.mass = 50 // Update touches ended to “throw” the ball override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { … // Continue the ball's movement along the same path as the touch gesture let factor: CGFloat = 50 let vector = CGVector(dx: factor * (endTouchLocation.x - startTouchLocation.x), dy: factor * (endTouchLocation.y - startTouchLocation.y)) ball?.physicsBody?.applyImpulse(vector) }
  11. RESETTING THE BALL /** * Called before each frame is

    rendered. */ override func update(_ currentTime: TimeInterval) { guard let ball = self.ball else { return } // Check to see if the ball node has left the scene bounds if (ball.position.x > self.size.width/2 + ball.size.width/2 || ball.position.x < -(self.size.width/2 + ball.size.width/2) || ball.position.y > self.size.height + ball.size.height) { // The ball is outside the bounds of the visible view resetBall() } } /** * Reset the ball to the default position. */ func resetBall() { // Remove the current ball from the scene ball?.removeAllActions() ball?.removeFromParent() ball = nil // Reset touch locations startTouchLocation = CGPoint.zero endTouchLocation = CGPoint.zero // Create a new ball and add to the scene createBall() }
  12. DETECTING COLLISIONS func checkCollisions() { guard let ball = self.ball,

    let monster = self.monster else { return } if ball.frame.insetBy(dx: 20, dy: 20).intersects(monster.frame) { monsterHit() } } func monsterHit() { guard let monster = self.monster else { return } // Remove monster from scene monster.removeAllActions() monster.removeFromParent() self.monster = nil }
  13. RESETTING MONSTER // Create a wait action let waitAction =

    SKAction.wait(forDuration: 1) // Run the wait action and pass in a completion block monster?.run(waitAction) { // Create a new monster node createMonster() }
  14. ADD SPARK ON COLLISION // Explode our monster let spark:

    SKEmitterNode = SKEmitterNode(fileNamed: "SparkParticle")! spark.position = monster.position spark.particleColor = UIColor.purple addChild(spark)
  15. LAYOUT UPDATES Views and UIButton @IBOutlet weak var cameraView: UIView!

    @IBOutlet weak var backgroundView: UIImageView! @IBOutlet weak var arModeButton: UIButton!
  16. ASK FOR PERMISSION // MARK: - Camera permissions func askForCameraPermissions()

    { // We need to ask the user for permission to use the camera. switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) { case .authorized: print("Authorized") self.displayCameraView() break case .denied: print("Denied") case .notDetermined: print("Ask for permission") AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { granted in if granted { print("Show camera view") self.displayCameraView() } else { print("Denied") } } case .restricted: // We don't have access to the camera. Nothing we can do here. print("No camera access") } }
  17. CAMERA PERMISSIONS Don’t forget to update your plist! <key>NSCameraUsageDescription</key> <string>Camera

    will be used to display live camera view background behind monsters (for Augmented Reality).</ string>
  18. CONFIGURE CAMERA CAPTURE import AVFoundation // True if camera has

    been configured var isCameraConfigured = false func configureCameraView() { // Configure AV Capture Session do { let videoCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) let videoInput: AVCaptureDeviceInput videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) if avCaptureSession.canAddInput(videoInput) { avCaptureSession.addInput(videoInput) } else { throw AVFoundationError.ConfigurationFailed } } catch { debugPrint("Something went wrong") debugPrint(error) return } // Set up our layer previewLayer.frame = cameraView.bounds if let videoPreviewView = cameraView { videoPreviewView.layer.addSublayer(previewLayer) } // Camera has been configured isCameraConfigured = true }
  19. DISPLAY CAMERA FEED func displayCameraView() { // Return if we're

    not authorized guard AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) == .authorized else { return } // Configure camera if not done already if !isCameraConfigured { configureCameraView() } // Start capturing data if avCaptureSession.isRunning == false { avCaptureSession.startRunning() } }
  20. TOGGLE BETWEEN VIEWS // True if we're viewing in AR

    Mode var isARMode = false @IBAction func tappedARModeButton(_ sender: AnyObject) { isARMode = !isARMode if isARMode { // Ask for permission to use the camera askForCameraPermissions() arModeButton.setTitle("Image Mode", for: .normal) } else { arModeButton.setTitle("AR Mode", for: .normal) // Stop the camera session if avCaptureSession.isRunning == true { avCaptureSession.stopRunning() } } // Hide/display background and camera view self.backgroundView.isHidden = isARMode self.cameraView.isHidden = !isARMode }
  21. RECAP ➤ Created an animated, moving monster ➤ Created a

    ball we can "throw" ➤ Monster explodes when hit ➤ Camera view for augmented reality