日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何编写和精灵宝可梦一样的 app?

發布時間:2023/12/20 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何编写和精灵宝可梦一样的 app? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文:How To Make An App Like Pokemon Go
作者:Jean-Pierre Distler
譯者:kmyhy

如今最流行的一個手機游戲就是精靈寶可夢。它使用增強現實技術將游戲帶入到“真實世界”,讓玩家做一些對健康有益的事情。

在本教程中,我們將編寫自己的增強現實精靈捕獲游戲。這個游戲會顯示一張包含有你的位置和敵人的位置的地圖,用一個 3D SceneKit 視圖呈現后置攝像頭中拍攝的圖像和敵人的 3D 模型。

如果你第一次接觸增強現實,你可以先看一下我們的基于地理位置的 RA 教程。對于要介紹如何編寫精靈寶可夢 app 的本教程來說,它不是必須的,但它里面包含了大量本教程未涉及的關于數學和 RA 的有用知識。

開始

本教程的開始項目在此處下載。項目包含了兩個 view controller 和一個 art.scnassets 文件夾,這個文件夾中包括了必須的 3D 模型和貼圖。

ViewController.swift 是一個 UIViewController 子類,用于顯示 app 的 AR 內容。MapViewController 用于顯示一張地圖,地圖上會包含你的當前位置以及附近敵人的位置。一些基本的東西,比如約束和出口,都是已經建好的了,你只需要關注本教程的核心內容,即怎樣讓 app 長得像精靈寶可夢。

在地圖上添加敵人

在你能夠和敵人戰斗之前,需要知道敵人在哪。新建一個 Swift 文件,叫做 ARItem.swift。

在文件的 ARItem.swift 的 import Foundation 一行后添加:

import CoreLocationstruct ARItem {let itemDescription: Stringlet location: CLLocation }

ARItem 有一個描述字段和一個坐標。這樣我們就能夠知道是什么樣的敵人,以及它在哪里。

打開 MapViewController.swift 添加一個 impor CoreLocation 語句以及一個屬性:

var targets = [ARItem]()

添加如下方法:

func setupLocations() {let firstTarget = ARItem(itemDescription: "wolf", location: CLLocation(latitude: 0, longitude: 0))targets.append(firstTarget)let secondTarget = ARItem(itemDescription: "wolf", location: CLLocation(latitude: 0, longitude: 0))targets.append(secondTarget)let thirdTarget = ARItem(itemDescription: "dragon", location: CLLocation(latitude: 0, longitude: 0))targets.append(thirdTarget) }

我們通過硬編碼的方式創建了 3 個敵人。我們會將坐標(0,0) 替換成靠近你物理坐標附近的坐標。

有許多查找坐標的方法。比如,可以在你當前位置附近創建一些隨機的坐標,使用我們在上一篇教程的 PlacesLoader 或者 Xcode 模擬當前位置。當然,我們不想讓隨機坐標出現在你鄰居的臥室里。那就尷尬了。

簡單點的方法,就是使用 Google 地圖。打開 https://www.google.com/maps/ 查找你當前的位置。當你點擊地圖,會顯示一個大頭釘,底部彈出一個氣泡。

在氣泡中會顯示你的經緯度。我建議你從你的位置或你所在的街道附近創建出一些硬編碼的位置,這樣你就沒有必要去敲鄰居家門,告訴他你需要去他的臥室抓一條龍。

選擇 3 個位置,將上面代碼中的 0 替換成你選擇的坐標。

在地圖上標出敵人

我們已經設定了敵人的坐標,應該在地圖上將它們顯示出來。新增一個 Swift 文件,取名為 MapAnnotation.swift。在這個文件中編寫如下代碼:

import MapKitclass MapAnnotation: NSObject, MKAnnotation {//1let coordinate: CLLocationCoordinate2Dlet title: String?//2let item: ARItem//3init(location: CLLocationCoordinate2D, item: ARItem) {self.coordinate = locationself.item = itemself.title = item.itemDescriptionsuper.init()} }

我們創建了一個 MapAnnotation 類并實現了 MKAnnotation 協議。

  • 這個協議需要實現一個 coordinate 屬性和 title 屬性。
  • item 屬性保存了和大頭釘相關的 ARItem。
  • 實現一個便利初始化方法,在方法中對所有屬性進行賦值。
  • 回到 MapViewController.swift 在 setupLocations() 方法最后一句添加:

    for item in targets { let annotation = MapAnnotation(location: item.location.coordinate, item: item)self.mapView.addAnnotation(annotation) }

    循環遍歷 targets 數組,每個 target 都會添加一個大頭釘到地圖上。

    在 viewDidLoad() 方法最后調用 setupLocations():

    override func viewDidLoad() {super.viewDidLoad()mapView.userTrackingMode = MKUserTrackingMode.followWithHeadingsetupLocations() }

    在定位之前,我們必須獲得權限。

    在 MapViewController 中添加一個新屬性:

    let locationManager = CLLocationManager()

    在 viewDidLoad() 最后一句,添加請求權限的代碼:

    if CLLocationManager.authorizationStatus() == .notDetermined {locationManager.requestWhenInUseAuthorization() }

    注意:如果不進行權限請求,map view 無法加載用戶位置。而且不會提示任何錯誤信息。每當你調用位置服務時,你都無法獲得位置信息,要排除錯誤請首先從這個地方開始。

    運行 app,等一會地圖將縮放到你的當前位置并顯示出一些紅色的大頭釘,它們表示了敵人的位置。

    添加增強現實效果

    我們有一個看起來不錯的 app,但我們還需要添加一些 AR 元素。在下一節,我們將添加一個攝像窗口并添加一個簡單的方塊來代表敵人。

    首先我們需要跟蹤用戶位置。在 MapViewController 聲明屬性:

    var userLocation: CLLocation?

    然后添加一個擴展:

    extension MapViewController: MKMapViewDelegate {func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {self.userLocation = userLocation.location} }

    每次設備的位置發生改變,這個方法會被調用。這個方法中,我們簡單地保存了用戶位置,以便在另一個方法中使用。

    在擴展中添加委托方法:

    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {//1let coordinate = view.annotation!.coordinate//2if let userCoordinate = userLocation {//3if userCoordinate.distance(from: CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)) < 50 {//4let storyboard = UIStoryboard(name: "Main", bundle: nil)if let viewController = storyboard.instantiateViewController(withIdentifier: "ARViewController") as? ViewController {// more code later//5if let mapAnnotation = view.annotation as? MapAnnotation {//6self.present(viewController, animated: true, completion: nil)}}}} }

    當用戶點擊到一個距離你不超過 50 米的敵人時,顯示一個攝像畫面:

  • 獲取所選中的大頭釘的坐標。
  • 去報 uerLocation 不為空。
  • 確認所點的大頭釘在用戶位置 50 米范圍內。
  • 從故事版中實例化一個 ARViewController 實例。
  • 檢查被點擊到的大頭釘類型是 MapAnnotation。
  • 顯示 viewController。
  • 運行 app,點擊你位置附近的任意大頭釘,會顯示一個空白的 view controller:

    添加攝像畫面

    打開 ViewController.swift,在 import SceneKit 后面添加 import AVFoundation:

    import UIKit import SceneKit import AVFoundationclass ViewController: UIViewController { ...

    添加兩個屬性用于保存一個 AVCaptureSession 對象和一個 AVCaptureVideoPreviewLayer 對象:

    var cameraSession: AVCaptureSession? var cameraLayer: AVCaptureVideoPreviewLayer?

    我們會用 capture session 來訪問視頻輸入(比如鏡頭)和輸出(比如取景框)。

    添加一個方法:

    func createCaptureSession() -> (session: AVCaptureSession?, error: NSError?) {//1var error: NSError?var captureSession: AVCaptureSession?//2let backVideoDevice = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .back)//3if backVideoDevice != nil {var videoInput: AVCaptureDeviceInput!do {videoInput = try AVCaptureDeviceInput(device: backVideoDevice)} catch let error1 as NSError {error = error1videoInput = nil}//4if error == nil {captureSession = AVCaptureSession()//5if captureSession!.canAddInput(videoInput) {captureSession!.addInput(videoInput)} else {error = NSError(domain: "", code: 0, userInfo: ["description": "Error adding video input."])}} else {error = NSError(domain: "", code: 1, userInfo: ["description": "Error creating capture device input."])}} else {error = NSError(domain: "", code: 2, userInfo: ["description": "Back video device not found."])}//6return (session: captureSession, error: error) }

    這個方法負責這些事情:

  • 創建一些變量,用于返回一些值。
  • 獲得后置攝像頭。
  • 如果攝像頭有效,獲取它的輸入。
  • 創建 AVCaptureSession 對象。
  • 將后置攝像頭輸入添加到 capture session。
  • 返回一個元組,包含 captureSession 和 error。
  • 現在我們已經從攝像頭拿到輸入了,就可以把它添加到視圖中:

    func loadCamera() {//1let captureSessionResult = createCaptureSession()//2 guard captureSessionResult.error == nil, let session = captureSessionResult.session else {print("Error creating capture session.")return}//3self.cameraSession = session//4if let cameraLayer = AVCaptureVideoPreviewLayer(session: self.cameraSession) {cameraLayer.videoGravity = AVLayerVideoGravityResizeAspectFillcameraLayer.frame = self.view.bounds//5self.view.layer.insertSublayer(cameraLayer, at: 0)self.cameraLayer = cameraLayer} }

    代碼解釋如下:

    • 首先調用前面的方法獲得一個 capture session。
    • 判斷是否有錯誤發生,或者 capture session 為空,如果是立即 return,和 AR 說 bye-bye 吧!
    • 否則,將 capture session 保存到 cameraSession 變量。
    • 創建攝像預覽圖層,如果創建成功,設置它的 videoGravity 屬性和 frame 屬性,讓它占據整個屏幕。
    • 將攝像預覽圖層(取景框)添加到 sublayers 中并保存到 cameraLayer 變量。

    然后,在 viewDidLoad() 加入:

    loadCamera()self.cameraSession?.startRunning()

    這里只做了兩件事情:首先調用前面編寫的方法,然后打開鏡頭取景框。這個取景框立馬會顯示到預覽圖層上。

    運行 app,點擊你身邊的任何一個位置,你會看到一個全新的鏡頭預覽界面:

    添加方塊

    干得不錯,但這還不算真正的 RA。在這一節,我們將添加一個簡單的方塊來表示敵人,并根據用戶的位置和朝向來移動它。

    這個游戲會有兩種敵人:狼和龍。

    因此,我們需要知道敵人的種類以及應該在哪里顯示它們。

    在 ViewController 中添加如下屬性(用于保存敵人的信息):

    var target: ARItem!

    打開 MapViewController.swift, 找到 mapView(_:, didSelect:) 將最后一個 if 語句修改為:

    if let mapAnnotation = view.annotation as? MapAnnotation {//1viewController.target = mapAnnotation.itemself.present(viewController, animated: true, completion: nil) }

    在顯示 viewController 之前,將一個 ARItem(它是被點擊的大頭釘的 item 屬性)賦給它。這樣,viewController 就能夠知道當前敵人的種類。

    現在 ViewController 已經獲得了 target 的信息了。

    打開 ARItem.swift 導入 SceneKit。

    import Foundation import SceneKitstruct ARItem { ... }

    添加一個屬性,用于保存一個 SCNNode 對象:

    var itemNode: SCNNode?

    確保這個屬性聲明在 ARItem 結構的其它屬性之后,因為在隱式的初始化方法將使用相同的順序來定義參數。

    Xcode 會提示 MapViewController.swift 中有一個錯誤。要解決這個錯誤,請打開這個文件,找到 setupLocations() 方法。

    我們需要修改在編輯器左邊標有一個紅點的代碼。

    對于這些代碼,我們都需要將缺少的 itemNode 參數用 nil 來補上。

    例如,這一行:

    let firstTarget = ARItem(itemDescription: "wolf", location: CLLocation(latitude: 50.5184, longitude: 8.3902))

    應當改為:

    let firstTarget = ARItem(itemDescription: "wolf", location: CLLocation(latitude: 50.5184, longitude: 8.3902), itemNode: nil)

    我們知道了敵人的種類,以及它們的位置,但我們還需要知道設備當前朝向。

    打開 ViewController.swift ,導入 CoreLocation:

    import UIKit import SceneKit import AVFoundation import CoreLocation

    然后,增加屬性聲明:

    //1 var locationManager = CLLocationManager() var heading: Double = 0 var userLocation = CLLocation() //2 let scene = SCNScene() let cameraNode = SCNNode() let targetNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))

    代碼解釋如下:

  • 我們用一個 CLLocationManager 去監聽設備的朝向。heading 的單位為度,表示正北方或者磁北極偏轉角度。
  • 創建一個 SCNode() 和一個 SCNode 對象。targetNode 將用來放入一個立方體。
  • 在 viewDidLoad() 最后一句添加:

    //1 self.locationManager.delegate = self //2 self.locationManager.startUpdatingHeading()//3 sceneView.scene = scene cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 10) scene.rootNode.addChildNode(cameraNode)

    代碼解釋如下:

  • 將 ViewController 設置為 CLLocationManager 委托。
  • 通過調用 startUpdatingHeading 方法,我們可以接收方向通知。默認,當方向改變超過 1 度時,委托方法會被調用。
    This sets ViewController as the delegate for the CLLocationManager.
  • 設置 SCNView。首先創建了一個空的 scene,然后將相機添加到其中。
  • 添加一個擴展,實現 CLLocationManagerDelegate 協議:

    extension ViewController: CLLocationManagerDelegate {func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {//1self.heading = fmod(newHeading.trueHeading, 360.0)repositionTarget()} }

    當收到新的方向通知,CLLocationManager 會調用這個委托方法。fmod 對 double 進行取模運算,確保方向的取值位于 0-359 之間。

    在 ViewController.swift 中添加一個 repostionTarget()方法,注意是放在類實現而不是 CLLocationManagerDelegate 擴展中:

    func repositionTarget() {//1let heading = getHeadingForDirectionFromCoordinate(from: userLocation, to: target.location)//2let delta = heading - self.headingif delta < -15.0 {leftIndicator.isHidden = falserightIndicator.isHidden = true} else if delta > 15 {leftIndicator.isHidden = truerightIndicator.isHidden = false} else {leftIndicator.isHidden = truerightIndicator.isHidden = true}//3let distance = userLocation.distance(from: target.location)//4if let node = target.itemNode {//5if node.parent == nil {node.position = SCNVector3(x: Float(delta), y: 0, z: Float(-distance))scene.rootNode.addChildNode(node)} else {//6node.removeAllActions()node.runAction(SCNAction.move(to: SCNVector3(x: Float(delta), y: 0, z: Float(-distance)), duration: 0.2))}} }

    代碼解釋如下:

  • getHeadingForDirectionFromCoordinate 這個方法用于計算從當前位置到目標的方向,具體實現后面介紹。
  • 計算設備當前方向和目標方向之間的偏轉角度(即 delta)。如果 delta 小于 -15,顯示左箭頭。如果大于 15,顯示右箭頭。如果在 -15 到 15 之間,兩個箭頭都隱藏,表示敵人就在屏幕中。
  • 計算從設備位置到敵人之間的距離。
  • 如果 itemNode 不為空……
  • 同時 node 沒有父節點,將 itemNode 的位置設置為 distance 并將 node 放到屏幕上。
  • 否則,刪除所有 action 并創建一個新的 action。
  • 如果你懂 SceneKit 或者 SpriteKit,則最后一句代碼你懂的。否則,這里會進行更詳細的介紹。

    SCNAction.move(to:, duration:) 方法創建一個 action,將節點以指定時間移動到指定的位置。runAction(_:) 也是 SCNNode 方法,用于執行一個 action。我們還可以創建 action 組/序列。要了解更多內容,請閱讀我們的這本書3D Apple Games by Tutorials。

    繼續實現前面未實現的方法。在 ViewController.swift 中添加這幾個方法:

    func radiansToDegrees(_ radians: Double) -> Double {return (radians) * (180.0 / M_PI) }func degreesToRadians(_ degrees: Double) -> Double {return (degrees) * (M_PI / 180.0) }func getHeadingForDirectionFromCoordinate(from: CLLocation, to: CLLocation) -> Double { //1let fLat = degreesToRadians(from.coordinate.latitude)let fLng = degreesToRadians(from.coordinate.longitude)let tLat = degreesToRadians(to.coordinate.latitude)let tLng = degreesToRadians(to.coordinate.longitude) //2let degree = radiansToDegrees(atan2(sin(tLng-fLng)*cos(tLat), cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(tLng-fLng))) //3if degree >= 0 {return degree} else {return degree + 360} }

    radiansToDegrees(_:) 和 degreesToRadians(_:) 方法用于將弧度和角度互轉。

    getHeadingForDirectionFromCoordinate(from:to:) 方法代碼解釋如下:

  • 首先,將角度轉換為弧度。
  • 然后用轉換后的弧度計算出方向在轉成角度。
  • 如果 degree 是負數,將之加上 360 度讓數據更一致。這是可以的,因為 -90 度就等于 270 度。
  • 還需要幾個步驟才能運行你的 app。

    首先,必須將用戶的坐標傳遞給 viewController。打開 MapViewController.swift 找到 mapView(_:, didSelect:) 的最后一個 if 語句,在顯示 view controller 之前加上這句:

    viewController.userLocation = mapView.userLocation.location!

    然后在 ViewController.swift 中添加這個方法:

    func setupTarget() {targetNode.name = "enemy"self.target.itemNode = targetNode }

    這個方法為 targetNode 設置一個名字,然后將它賦給 target。

    現在可以在 viewDidLoad() 方法最后來調用這個方法了。在添加完攝像頭之后添加:

    scene.rootNode.addChildNode(cameraNode) setupTarget()

    運行 app,可以看到方塊在移動:

    美化我們的 app

    在開發 app 初期用方塊或者圓球是一種簡單的處理方法,因為這樣省去了大量 3D 建模的時間——但 3D 模型看起來畢竟要漂亮得多。在這一節,我們將繼續美化我們的 app ,為敵人加入 3D 模型,以及賦予玩家扔出火球的能力。

    打開 art.scnassets 文件夾,里面有兩個 .dae 文件。它們包含了敵人的模型:狼和龍。

    接下來修改 ViewController.swift 中的 setupTarget() 方法,在其中加載這些 3D 模型并賦給目標的 itemNode 屬性。

    將 setupTarget() 方法修改為:

    func setupTarget() {//1let scene = SCNScene(named: "art.scnassets/\(target.itemDescription).dae")//2let enemy = scene?.rootNode.childNode(withName: target.itemDescription, recursively: true)//3 if target.itemDescription == "dragon" {enemy?.position = SCNVector3(x: 0, y: -15, z: 0)} else {enemy?.position = SCNVector3(x: 0, y: 0, z: 0)}//4 let node = SCNNode()node.addChildNode(enemy!)node.name = "enemy"self.target.itemNode = node }

    代碼解釋如下:

  • 首先將模型加載到場景中。目標的 itemDescription 屬性名和 .dae 文件名對應。
  • 然后遍歷場景,查找其中和 itemDescription 名字相同的節點。這只會有一個節點,即模型的根節點。
  • 調整模型放置的位置,以便兩個模型都會在同一地方出現。如果兩個模型都出自同一個設計師之手,可能這一步是不必要的。但是我的這兩個模型分別來自不同的設計師:狼來自于 3dwarehouse.sketchup.com ,龍來自于 https://clara.io。
  • 將模型添加到空節點,然后將節點賦給當前目標的 itemNode 屬性。還剩下一個小問題,即觸摸的處理,放在后面介紹。
  • 運行 app,你會看到一只立體的狼,這可比一個便宜的方塊要嚇人多了!

    事實上,這只狼足以讓你嚇得遠遠拋開了,但作為勇敢主角的你,逃跑從來不是你的選擇!接下來你應該加上幾個火球,這樣你就能在成為狼的點心之前戰勝它了。

    拋出火球的最好時機是用戶的觸摸結束事件,因此在 ViewController.swift 中實現這個方法:

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {//1let touch = touches.first!let location = touch.location(in: sceneView)//2let hitResult = sceneView.hitTest(location, options: nil)//3let fireBall = SCNParticleSystem(named: "Fireball.scnp", inDirectory: nil)//4let emitterNode = SCNNode()emitterNode.position = SCNVector3(x: 0, y: -5, z: 10)emitterNode.addParticleSystem(fireBall!)scene.rootNode.addChildNode(emitterNode)//5 if hitResult.first != nil {//6target.itemNode?.runAction(SCNAction.sequence([SCNAction.wait(duration: 0.5), SCNAction.removeFromParentNode(), SCNAction.hide()]))let moveAction = SCNAction.move(to: target.itemNode!.position, duration: 0.5)emitterNode.runAction(moveAction)} else {//7emitterNode.runAction(SCNAction.move(to: SCNVector3(x: 0, y: 0, z: -30), duration: 0.5))} }

    代碼解釋如下:

  • 將觸摸轉換成場景坐標。
  • hitTest(_, options:) 方法向指定的位置發射射線,返回一個 SCNHitTestResult 數組,表示該射線所穿過的所有節點。
  • 從 SceneKit 粒子文件中加載粒子系統,用于發射火球。
  • 將粒子系統加到一個空節點身上,然后將它放到屏幕下方以外。這使得火球看起來是從玩家位置發射的。
  • 判斷是否有碰撞發生……
  • 等待 0.5 秒,然后移除敵人所對應的 itemNode。同時將粒子發射器節點移動到敵人的位置。
  • 如果沒有碰撞發生,火球移動到一個固定的位置。
  • 運行 app,讓惡餓狼在火焰中焚燒吧!

    收尾工作

    要完成 app,我們還需要將敵人從列表中刪除,關閉 AR 視圖并回到地圖,以便找到下一個敵人。

    移除敵人應當在 MapViewController 中進行,因為敵人列表就在那里。我們可以說明只有一個方法的委托協議,當目標被擊中后調用這個方法。

    在 ViewController.swift 的類聲明之前,添加如下協議:

    protocol ARControllerDelegate {func viewController(controller: ViewController, tappedTarget: ARItem) }

    同時為 ViewController 聲明一個屬性:

    var delegate: ARControllerDelegate?

    委托方法會告訴委托對象說明時候發生了碰撞事件,然后委托對象就可以進行下一步的處理。

    在 ViewController.swift 中找到 touchesEnded(_:with:) 方法,將if 語句中的代碼塊修改為:

    if hitResult.first != nil {target.itemNode?.runAction(SCNAction.sequence([SCNAction.wait(duration: 0.5), SCNAction.removeFromParentNode(), SCNAction.hide()]))//1let sequence = SCNAction.sequence([SCNAction.move(to: target.itemNode!.position, duration: 0.5),//2SCNAction.wait(duration: 3.5), //3SCNAction.run({_ inself.delegate?.viewController(controller: self, tappedTarget: self.target)})])emitterNode.runAction(sequence) } else {... }

    解釋如下:

  • 將粒子發射器節點的 action 改成一個 action 序列,其中 move 動作仍然保留。
  • move 動作之后,暫停 3.5 秒。
  • 通知委托對象,target 被擊中。
  • 打開 MapViewController.swift 聲明一個屬性,用于保存 選中的大頭釘:

    var selectedAnnotation: MKAnnotation?

    這個屬性用于待會將它從地圖上移出。修改它的 viewController 的初始化和條件綁定(if let)部分的代碼:

    if let viewController = storyboard.instantiateViewController(withIdentifier: "ARViewController") as? ViewController {//1viewController.delegate = selfif let mapAnnotation = view.annotation as? MapAnnotation {viewController.target = mapAnnotation.itemviewController.userLocation = mapView.userLocation.location!//2selectedAnnotation = view.annotationself.present(viewController, animated: true, completion: nil)} }

    非常簡單:

  • 將 viewController 的委托設置為 MapViewController。
  • 保存用戶點中的大頭釘對象。
  • 在 MKMapViewDelegate 擴展下面添加:

    extension MapViewController: ARControllerDelegate {func viewController(controller: ViewController, tappedTarget: ARItem) {//1self.dismiss(animated: true, completion: nil)//2let index = self.targets.index(where: {$0.itemDescription == tappedTarget.itemDescription})self.targets.remove(at: index!)if selectedAnnotation != nil {//3mapView.removeAnnotation(selectedAnnotation!)}} }

    代碼解釋如下:

  • 解散 AR 視圖。
  • 從 targets 數組中刪除 target。
  • 從地圖上刪除大頭釘。
  • 運行 app,你將看到最終效果:

    結束

    最終完成的項目在這里下載。

    如果你想盡可能地學習如何編寫這個 app,請參考下列教程:

    • 關于 MapKit 和位置服務,請參考我們的 MapKit Swift 入門。
    • 關于視頻捕捉,請參考我們的 AVFoundation 系列。
    • 關于 SceneKit,請參考我們的 SceneKit 系列教程。
    • 要避免對敵人位置進行硬編碼,則需要后臺數據的支持,請參考如何編寫一個簡單的 PHP/MySQL 服務 以及 如何用 Vapor 進行服務端編程。

    希望你喜歡本教程。如果有任何問題和建議,請在下面留言。

    總結

    以上是生活随笔為你收集整理的如何编写和精灵宝可梦一样的 app?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。