【问题标题】:How to handle touch event in render queue in SceneKit如何在 SceneKit 的渲染队列中处理触摸事件
【发布时间】:2021-11-06 13:41:30
【问题描述】:

我刚刚发现 SceneKit 的渲染发生在渲染队列中,这是一个非主串行队列。这与在主线程中处理渲染的 SpriteKit 不同。

因此,我现在处于竞争状态。我的触摸控制在主线程中,每帧更新功能在渲染线程中。两者都将为 SCNNode 设置位置。

看起来渲染队列是私有的(我可能是错的)所以我不能向它发送触摸调用。所以我想知道如何处理渲染线程中的触摸?

【问题讨论】:

    标签: ios scenekit grand-central-dispatch


    【解决方案1】:

    Touch 来自 UIViewController,GameControl 是我使用计时器移动对象的类。我没有只使用 SpriteKit,所以我无法对此发表评论,但听起来一些示例代码会有所帮助......

    类 GameViewController: UIViewController { ...

    func update(vTime: TimeInterval)
        {
            switch data.gameState
            {
            case .staging:
                    timersOn()
                    setMode(vState: .run)
                    gameStateManager.run()
                    gameStateManager.isStagingActive = true
                }
                break
            case .nextWave:
                if(gameStateManager.isNextWaveActive == false)
                {
                    if(gameControl.nextWave() == false)
                    {
                        gameStateManager.endGame()
                    }
                    else
                    {
                        gameControl.resumeGame()
                        setMode(vState: .run)
                        gameStateManager.run()
                    }
                    gameStateManager.isNextWaveActive = true
                }
                break
            case .run, .defenseAdd, .defenseUpgrade:
                gameControl.updateTime(vTime: vTime)
                break
    }
    
    @objc func handleTap(recognizer: UITapGestureRecognizer)
        {
            
            let location: CGPoint = recognizer.location(in: gameScene)
            
            if(data.isAirStrikeModeOn == true)
            {
                let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
                let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
                gameControl.airStrike(position: scenePoint)
            }
            else
            {
                let hitResults = gameScene.hitTest(location, options: hitTestOptions)
                for vHit in hitResults
                {
                    if(vHit.node.name?.prefix(5) == "Panel")
                    {
                        // May have selected an invalid panel or auto upgrade was on
                        if(gameControl.selectPanel(vPanel: vHit.node.name!) == false) { return }
                        return
                    }
                }
            }
        }
    
    extension GameViewController: SCNSceneRendererDelegate
    {
        func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
        {
            update(vTime: time)
        }
    }
    

    计时器在 GameControl 中 - 移动对象、设置节点等。

    每个 cmets,以下是附加信息:

    每当您修改场景图中的对象时,SceneKit 都会自动创建一个事务。此事务将您在该线程的运行循环的当前迭代期间从同一线程所做的任何其他更改分组。当运行循环下一次迭代时,SceneKit 会自动提交事务,自动将事务期间所做的所有更改应用到演示场景图(即当前正在显示的场景图的版本)。

    这篇博文 [31700071] 深入探讨了您可以/应该在主线程中做什么以及它如何影响渲染循环的问题。

    【讨论】:

    • 这将有竞争条件,因为 rendererhandleTap 在不同的线程上运行
    • 我总是有可能做错了......但我已经在几场比赛中使用了这种结构。我建立了一个模拟器来玩游戏,并且不间断地运行测试长达整整一周。我还跟踪了实时崩溃(无)和 FPS 统计数据。我使用 DispatchQueue.main.async (打开计时器),以便计时器在主线程中运行。 GameControl {} 执行所有节点操作。
    • 这不是线程安全的。它对竞争条件开放,甚至可能崩溃。
    • 这并不能阻止比赛......除非您在主线程中使用闭包 API(类似于 update(_ oldPos: CGPoint))进行更新,但事实并非如此。否则直接阅读肯定会造成竞态
    • 好的,对不起,我帮不了你。祝你好运。
    猜你喜欢
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    • 2010-12-02
    相关资源
    最近更新 更多