Swift基於ARKit的仿抖音潛水艇小遊戲

抖音的潛水艇小遊戲只能玩一會兒,不盡興,於是想着自己開發一個。

ARKit的各種入門介紹這裏就不說了,網上一堆都是,自己注意甄別。

第一步,創建一個具有增強現實功能AR的項目:

選擇語言Swift, SpriteKit是2D遊戲引擎開發框架,考慮到遊戲還是以2D畫面爲主,所以選擇了SpriteKit,SceneKit是3D開發引擎。

第二步,在ViewController中可以開打已經默認導入了ARKit和SpriteKit兩個框架,而且SB中也添加了一個ARSKView實例sceneView,ARSKView可以渲染攝像頭捕捉到的畫面和畫面中添加的每一個節點node,它把ARSession和Spritekit結合了起來,具體代碼:

override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView.delegate = self
        sceneView.showsPhysics = true
     
        
        sceneView.showsFPS = true
        sceneView.showsNodeCount = true
        
        //檢測手機能不能用人臉追蹤功能
        guard ARFaceTrackingConfiguration.isSupported else {
            fatalError("Face tracking is not supported on this device")
        }
        
        if let view = self.view as! SKView? {
            //通過代碼創建一個GameScene類的實例對象 不用項目中自帶的
            scene.size = view.bounds.size
            sceneView.presentScene(scene)
        }
    }

在視圖即將出現和即將消失的時候打開和關閉ARSession

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        let configuration = ARFaceTrackingConfiguration()
        sceneView.session.delegate = self
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }

sceneView.delegate = self 代理方法提供了添加節點、節點即將更新、已經更新的方法:

這裏我們先添加一個簡單的圖片上去看下效果

// MARK: - ARSKViewDelegate
    func view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor) {
        let image = SKSpriteNode(imageNamed: "速摳圖")
        image.size = CGSize(width: 20, height: 20)
        image.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "速摳圖"), size: CGSize(width: 20, height: 20))
        image.position = CGPoint(x: -60, y: 0)
        node.addChild(image)
    }

運行可以看到攝像頭識別人臉並讓圖片跟隨人臉移動,當前image默認是以識別到的人臉中心位置爲錨點,所以image.position = CGPoint(x: -60, y: 0)修改了當前節點的位置,偏臉人臉到左邊

到這裏人臉識別追蹤的簡單功能就完成了,接下來就是添加各種2D的模型,潛水艇和柱子,潛水艇的位置跟隨識別到的人臉的位置,再給模型添加物理屬性。

第三步,在Scene中添加潛水艇和柱子模型

        image = SKSpriteNode(imageNamed: "速摳圖")
        image.size = CGSize(width: 60, height: 60)
        image.physicsBody = SKPhysicsBody(rectangleOf:image.size)
        image.physicsBody?.affectedByGravity = false
        
        image.physicsBody?.categoryBitMask = birdCategory
        image.physicsBody?.contactTestBitMask = pipeCategory
        image.position = CGPoint(x: 50, y: 50)

        addChild(image)

image位置隨便指一個,我喜歡先看效果再處理

創建柱子節點,柱子節點是多個而且成對出現,上下各一個,隔一段時間出現一對,隔一段時間再出現一對,如此循環

所以這可以看做是一個創建+等待+創建+等待的循環過程

於是:

    //MARK:開始重複創建水管
    func startCreateRandomPipesAction() {
        //創建一個等待的action,等待時間的平均值爲秒,變化範圍爲1秒
        let waitAct = SKAction.wait(forDuration: 1.5)
        //創建一個產生隨機水管的action,這個action實際上就是我們下面新添加的那個createRandomPipes()方法
        let generatePipeAct = SKAction.run {
            self.createRandomPipes()
        }
        //讓場景開始重複循環執行“等待->創建->等待->創建...”
        //並且給這個循環的動作設置一個叫做createPipe的key類標識它
        run(SKAction.repeatForever(SKAction.sequence([waitAct,generatePipeAct])), withKey: "createPipe")
    }
    //MARK:具體某一次創建一對水管
    func createRandomPipes() {
        let height = self.size.height
        let pipeGap = CGFloat(arc4random_uniform(60)) + image.size.width*2
        let pipeWidth:CGFloat = 60
        let topHeight = CGFloat(arc4random_uniform(UInt32(height/2))) + height/4
        let bottomPipeHeight = height - pipeGap - topHeight
        addPipes(topSize: CGSize(width: pipeWidth, height: topHeight), bottomSize: CGSize(width: pipeWidth, height: bottomPipeHeight))
    }


    //MARK:添加水管到場景裏
    func addPipes(topSize:CGSize,bottomSize:CGSize) {
        //創建上水管
        guard let image = UIImage(named: "topPipe") else { return }
        let topTextture = SKTexture(image: image)
        //利用上水管圖片創建一個上水管紋理對象
        let topPipe = SKSpriteNode(texture: topTextture, size: topSize)
        //利用上水管紋理對象和傳入的上水管大小參數創建一個上水管對象
        topPipe.name = "pipe" //給這個水管取個名字叫pipe
        topPipe.position = CGPoint(x: self.size.width + topPipe.size.width * 0.5, y: self.size.height - topPipe.size.height * 0.5)
        
        //創建下水管
        let bottomTexture = SKTexture(imageNamed: "bottomPipe")
        let bottomPipe = SKSpriteNode(texture: bottomTexture, size: bottomSize)
        bottomPipe.name = "pipe"
        bottomPipe.position = CGPoint(x: self.size.width + bottomPipe.size.width * 0.5, y: bottomPipe.size.height * 0.5)
        
        
        //將上下水管天驕到場景中
        addChild(topPipe)
        addChild(bottomPipe)
    }

讓柱子移動:

    //MARK:移動和移除
    func moveScene() {
        //make pipe move
        for pipeNode in self.children where pipeNode.name == "pipe" {
            //因爲我們要用到水管的size,但是SKNode沒有size屬性,所以我們要把它轉成SKSpriteNode
            if let pipeSprite = pipeNode as? SKSpriteNode {
                //將水管左移2
                pipeSprite.position = CGPoint(x: pipeSprite.position.x - 2, y: pipeSprite.position.y)
                //檢查水管是否完全超出屏幕左側了,如果是則將它從場景裏移除掉
                if pipeSprite.position.x < -pipeSprite.size.width * 0.5 {
                    pipeSprite.removeFromParent()
                }
            }
        }
    }

添加物理屬性:

self.physicsWorld.contactDelegate = self

各個節點添加物理屬性

        image.physicsBody = SKPhysicsBody(rectangleOf:image.size)
        image.physicsBody?.affectedByGravity = false
        
        image.physicsBody?.categoryBitMask = imageCategory
        image.physicsBody?.contactTestBitMask = pipeCategory

實現節點碰撞的代理事件:func didBegin(_ contact: SKPhysicsContact) 

    func didBegin(_ contact: SKPhysicsContact) {
        print("發生碰撞")
    }

到這裏,還需要把潛水艇節點的位置和人臉識別位置聯繫起來,這裏我在ARSKViewDelegate裏面獲取到node的識別位置,然後用閉包回調,暫時沒想到其他好方法。

//Scene中添加閉包
var positionCallBack:((_ point:CGPoint)->())?


//didMove方法中處理閉包返回的位置
positionCallBack = {
            point in
            
            self.image.position = CGPoint(x: point.x-60, y: point.y)
       
        }

 

//ViewController中 回調位置

func view(_ view: ARSKView, didUpdate node: SKNode, for anchor: ARAnchor) {
        scene.positionCallBack?(node.position)
    }

運行可以看到image節點跟隨人臉移動,與移動的柱子接觸後出發碰撞方法,在此方法中做遊戲結束的處理。

源碼地址:地址

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章