Задержка между SKAction и SKAudioNode, когда SKScene приостанавливается и возобновляется.IOS

Программируем под IOS
Ответить Пред. темаСлед. тема
Anonymous
 Задержка между SKAction и SKAudioNode, когда SKScene приостанавливается и возобновляется.

Сообщение Anonymous »

Обзор
В моем SKScene у меня есть SKAudioNode, воспроизводящий аудиофайл, объявленный следующим образом:
var backgroundMusic: SKAudioNode?

и инициализируется следующим образом:
guard let musicURL = Bundle.main.url(forResource: "MY_AUDIO_FILENAME", withExtension: "mp3") else { fatalError() }
backgroundMusic = SKAudioNode(url: musicURL)
backgroundMusic?.isPositional = false

Для простоты представьте, что воспроизводимый звук — это голос, считающий вслух от 1 до 1000:

"one , перерыв, два, перерыв, три, перерыв, -- [cut]"

Теперь моя цель — < Strong>воспроизводить звук и выполнять действия в моем коде, когда звук достигает определенная часть,, например. когда голос насчитывает число 45.
Для этого я установил таймер с помощью SKAction следующим образом:
let wait = SKAction.wait(forDuration: 48)
let playAlarmSound = SKAction.run {
// perform stuff
}
let timer = SKAction.sequence([wait, playAlarmSound])

Я добавляю их обоих в сцену одновременно, так что теоретически, когда звук достигает этой конкретной точки, срабатывает таймер.
run(timer, withKey: "Timer")
addChild(backgroundMusic!)

Проблемы
  • Каждый раз, когда SKScene приостанавливается, а затем возобновляется, SKAudioNode будет пропущена небольшая часть звука, в данном примере будет пропущено примерно одно число.
  • После каждой паузы и возобновления таймер сработает немного позже. Это очевидно в примере проекта, который я привожу ниже: если вы сделаете паузу и возобновите работу примерно 10 раз, таймер сработает около 50, а не 45.
Попытки решения
  • Приостановка SKAudioNode отдельно каждый раз, когда SKScene приостанавливается, говоря backgroundMusic?.run(SKAction.pause()).
    Хотя эта проблема 1. устранена и значительно сокращает задержку проблемы 2., это все равно не оптимальное решение, поскольку таймер все равно будет работать. не синхронизироваться со звуком.
    На этот раз он сработает примерно на секунду раньше, чем предполагалось, а не на несколько секунд позже.
  • Использование таймера< /код>. Это НЕ жизнеспособное решение по двум причинам: 1) оно игнорирует состояние паузы узла, сцены или представления, как указано в нескольких ответах здесь [источники: 1. 2] 2 ) Я попробовал, и это вносит лагаи в мою игру - по опыту, на мой взгляд, при использовании SpriteKit лучше не пробовать гибридные решения.
Я понятия не имею, почему происходит такое отставание, поэтому, если кто-нибудь сможет пролить свет на этот вопрос, я буду признателен.
Более того, Я ищу последовательное и оптимальное решение описанных проблем. Я хочу иметь возможность выполнять действия в коде точно в определенное время воспроизведения звука (которое не является завершением).
< h2>Пример проекта
Это минималистичный образец проекта, который подчеркивает проблемы, на которые я указал. Никаких файлов .sks не требуется, просто скопируйте, вставьте, создайте и запустите.
Проект запускает аудиофайл и таймер автоматически, как только отображается GameScene. Отсюда вы можете приостановить/возобновить сцену, коснувшись любого места красного экрана, чтобы воспроизвести проблемы. С моими настройками и предоставленным мной звуком таймер должен срабатывать, когда счетчик достигает числа 45.
Каждый раз, когда вы приостанавливаете/возобновляете сцену и когда срабатывает таймер, приложение будет печатать console.
Обратите внимание, что я использовал это решение из другого вопроса, чтобы сохранить согласованное состояние паузы, когда приложение переходит в фоновый режим.
Для этого примера я предлагаю скачать и использовать этот аудиофайл (Dropbox ссылка) парня, считающего числа, поскольку это делает проблемы очевидными.
final class GameViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let skView = SKView(frame: view.frame)
view = skView
skView.ignoresSiblingOrder = true

let gameScene = GameScene()
skView.presentScene(gameScene)
}
}

import SpriteKit

final class GameScene: SKScene {

var backgroundMusic: SKAudioNode?

var isGamePaused: Bool = false {
didSet {
self.isPaused = isGamePaused

// Uncomment the following lines to test my solution, which is not optimal. After multiple pause/resume, the timer will still not be synchronized with the audio and will fire just around a second earlier than when it is supposed to.

//if isGamePaused {
// self.backgroundMusic?.run(SKAction.pause())
//} else {
// self.backgroundMusic?.run(SKAction.play())
//}
}
}
override var isPaused: Bool {
didSet {
if (self.isPaused == false && self.isGamePaused == true) {
self.isPaused = true

// Uncomment the following line to test my solution, which is not optimal. After multiple pause/resume, the timer will still not be synchronized with the audio and will fire just around a second earlier than when it is supposed to.

//self.backgroundMusic?.run(SKAction.pause())
}
}
}

override func didMove(to view: SKView) {
super.didMove(to: view)
view.isMultipleTouchEnabled = false
backgroundColor = .red

startAudioAndTimer()
}

func startAudioAndTimer() {

// ISSUE: On pause&resume, especially after multiple times, the timer gets unsynchronized with the audio and will fire many seconds later than when it is supposed to.

guard let musicURL = Bundle.main.url(forResource: "Test_Song", withExtension: "mp3") else {
fatalError("Error: add a mp3 audio file to the project")
}
backgroundMusic = SKAudioNode(url: musicURL)
backgroundMusic?.isPositional = false

let wait = SKAction.wait(forDuration: 48) // With this value for duration, if you're using the audio file I provided, the alarm should ring at the end of '45' in the song
let playAlarmSound = SKAction.run {
print("\n--- The alarm rings ---\n")
}
let timer = SKAction.sequence([wait, playAlarmSound])

run(timer, withKey: "Timer")
addChild(backgroundMusic!)
}

override func touchesBegan(_ touches: Set, with event: UIEvent?) {
isGamePaused = !isGamePaused
let statusString = isGamePaused ? "paused" : "resumed"
print("\nScene \(statusString)\n")
}

override func touchesMoved(_ touches: Set, with event: UIEvent?) { }

override func touchesEnded(_ touches: Set, with event: UIEvent?) { }

override func touchesCancelled(_ touches: Set, with event: UIEvent?) { }
}


Подробнее здесь: https://stackoverflow.com/questions/793 ... nd-resumed
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Skscene в Skscene
    Anonymous » » в форуме IOS
    0 Ответы
    2 Просмотры
    Последнее сообщение Anonymous
  • Spritekit время от времени вылетает на SKAction.move
    Anonymous » » в форуме IOS
    0 Ответы
    18 Просмотры
    Последнее сообщение Anonymous
  • SKAction playSoundFileNamed не работает после получения двух последовательных телефонных звонков
    Anonymous » » в форуме IOS
    0 Ответы
    18 Просмотры
    Последнее сообщение Anonymous
  • Swift Pause endless Skaction
    Anonymous » » в форуме IOS
    0 Ответы
    4 Просмотры
    Последнее сообщение Anonymous
  • Добавить кнопку «Назад» на панель навигации после перехода из SKScene.
    Anonymous » » в форуме IOS
    0 Ответы
    23 Просмотры
    Последнее сообщение Anonymous

Вернуться в «IOS»