Как обработать событие касания в очереди рендеринга в SceneKit

#ios #scenekit #grand-central-dispatch

Вопрос:

Я только что узнал, что рендеринг SceneKit происходит в очереди рендеринга, которая не является основной последовательной очередью. Это отличается от SpriteKit, который обрабатывает рендеринг в основном потоке.

Сейчас у меня из-за этого состояние гонки. Мое сенсорное управление находится в основном потоке, а функция обновления для каждого кадра-в потоке рендеринга. Оба будут задавать положение для SCNNode.

Похоже, очередь рендеринга закрыта (я могу ошибаться), поэтому я не могу отправлять в нее сенсорные вызовы. Поэтому мне интересно, как обрабатывать касание в потоке рендеринга?

Ответ №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)
    }
}
 

Таймеры находятся в игровом управлении — перемещают объекты, устанавливают узлы и т. Д.

Согласно комментариям, вот дополнительная информация:

SceneKit автоматически создает транзакцию всякий раз, когда вы изменяете объекты на графике сцены. Эта транзакция группирует любые дополнительные изменения, которые вы вносите в один и тот же поток во время текущей итерации цикла выполнения этого потока. Когда цикл выполнения повторяется в следующем цикле, SceneKit автоматически фиксирует транзакцию, атомарно применяя все изменения, внесенные во время транзакции, к графику сцены презентации (то есть версии графика сцены, отображаемой в данный момент).

Этот пост [31700071] дает представление о том, что вы можете/должны делать в основном потоке и как это влияет на цикл визуализации.

Комментарии:

1. Это будет иметь состояние гонки, потому renderer что и handleTap выполняются в разных потоках

2. Всегда возможно, что я мог сделать это неправильно… но я использовал эту структуру для нескольких игр. Я построил симулятор, чтобы играть в игры, и проводил тесты без остановки в течение целой недели без остановки. Я также отслеживал сбои в реальном времени (нет) и статистику кадров в секунду. Я использую DispatchQueue.main.async (для включения таймеров), чтобы таймеры запускались в основном потоке. GameControl {} выполняет все манипуляции с узлами.

3. это небезопасно для потоков. он открыт для гоночных условий и может даже разбиться.

4. Это не мешает гонке… если вы не обновляете в основном потоке с помощью API закрытия (что-то вроде update(_ oldPos: CGPoint) ), что не так. в противном случае прямое чтение этого сообщения определенно вызовет состояние гонки

5. Ладно, извини, что не смог тебе помочь. Удачи.