Kotlin — задержка между обновлениями пользовательского интерфейса

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

вот что я пробовал в своем коде:

    // bot functionality
private fun botTurn(botDifficulty: Int) {
    var botMove = 100
    when (botDifficulty) {
        1 -> botMove = easyBot(confirmedMoves)
        2 -> botMove = mediumBot(activePlayer, confirmedMoves, maxPlayers)
        3 -> botMove = hardBot(activePlayer, confirmedMoves, maxPlayers)
        else -> Toast.makeText(this, "What sort of bot is this??", Toast.LENGTH_SHORT).show()
    }
    
    trueCellID = botMove
    // colour chosen segment
    setSegmentColor(trueCellID, -1, activePlayer)
    // TODO add 1-2 second delay after confirming move?
    Log.d("Wait", "start of wait $activePlayer")
    Thread.sleep(1000)
    Log.d("Wait", "end of wait $activePlayer")
    
    confirmMove()
}

использование Thread.sleep, похоже, просто задерживает все обновления пользовательского интерфейса до тех пор, пока не произойдет все сны botPlayer. Я также пытался использовать Handler.postDelayed, GlobalScope.launch с блоком delay и runOnUiThread с SystemClock.sleep(1000) - у них та же проблема: выполнение всего ожидания бота, а затем обновление пользовательского интерфейса.

Я даже попытался адаптировать это решение, чтобы попробовать - как дождаться Android runOnUiThread должен быть закончен? но получил тот же результат - большая задержка, чем все обновления пользовательского интерфейса.

Есть ли решение для этого или я пропустил что-то простое?


person KinectDeveloper23    schedule 25.05.2021    source источник
comment
Я знаю, что это не имеет значения, но с перечислением вместо Int для уровня бота вам не пришлось бы иметь дело с дополнительной веткой when (если это не имеет смысла в вашем коде). Кроме того, вместо выражения можно использовать when: val botMove = when(botDifficulty) {...}   -  person Joffrey    schedule 25.05.2021
comment
Как вы звоните botTurn? Вы вызываете его последовательно для каждого бота? Или вы используете сопрограммы для их параллельного вызова?   -  person Joffrey    schedule 25.05.2021
comment
Кроме того, что именно вы хотите, чтобы произошло? Вы хотите, чтобы боты играли последовательно, по одному каждую секунду? Как насчет этой части цвета сегмента?   -  person Joffrey    schedule 25.05.2021
comment
спасибо, перечисление, безусловно, было бы полезно, чтобы сделать код более читабельным! confirmMove() увеличивает activePlayer, а затем проверяет, является ли активный игрок ботом или человеком. если это бот, то вызывается botTurn.   -  person KinectDeveloper23    schedule 25.05.2021
comment
Я хочу, чтобы каждый бот либо setSegmentColor-wait-confirmMove, либо ждал между ботами. В тот момент, когда приложение ожидает, пользовательский интерфейс показывает движения botA и botB, а не botA-wait-botB.   -  person KinectDeveloper23    schedule 25.05.2021
comment
Есть ли какая-то проверка, которую я могу сделать, чтобы убедиться, что сегмент был окрашен на экране, прежде чем вызывать режим сна и подтверждать перемещение?   -  person KinectDeveloper23    schedule 25.05.2021
comment
Подождите, ваше описание звучит так, будто вы выполняете последующие ходы, углубляясь в стек вызовов. Вы имеете в виду, что confrimMove() вызывает botTurn() и наоборот? Это не очень хорошая идея ;-) Было бы намного чище, если бы botTurn() выполнял решение/движение только ботом, а другой код определял бы ход бота или человека, вызывал бы botTurn() и ждал между ходами.   -  person broot    schedule 25.05.2021
comment
И возвращаясь к вашему вопросу: и Handler.postDelayed(), и GlobalScope.launch() должны сработать, так что я думаю, вы сделали что-то не так. Например, вы можете сделать botTurn() приостанавливаемым, вызвать его внутри GlobalScope.laucnh() и заменить Thread.sleep() на delay() — это должно работать без блокировки пользовательского интерфейса.   -  person broot    schedule 25.05.2021
comment
Хорошо, спасибо, я повторю попытку Handler.postDelayed(). Ничто не блокирует пользовательский интерфейс — повороты ботов действительно слишком быстрые, они появляются мгновенно, когда это делают не люди. У меня есть функция waitYourTurn(), которая проверяет, чья сейчас очередь, и если это бот, если это бот, то botTurn() — это должно заполнить сегмент (пользовательский интерфейс), затем confirmMove() — это сохраняет ход и увеличивает activePlayer и снова вызывает waitYourTurn().   -  person KinectDeveloper23    schedule 25.05.2021
comment
Вот github github.com/Kovah101/Tri-Taptical/blob/master/app/src/main/java/ - если это поможет   -  person KinectDeveloper23    schedule 25.05.2021
comment
Код довольно сложный, но я думаю, что был прав: вы делаете повороты по такому шаблону: botTurn() -> confirmMove() -> checkForWinner() -> waitForBots() -> botTurn(), так что с каждым поворотом вы все глубже и глубже погружаетесь в стек вызовов. Думаю, у вас нет проблем только потому, что в крестиках-ноликах очень ограниченное количество ходов.   -  person broot    schedule 25.05.2021
comment
Что касается вашей проблемы: попробуйте еще раз с вашим решением postDelayed() (закомментировано в коде), но на этот раз поместите setSegmentColor() и confirmMove() в блок postDelayed(). Но это полностью изменит поток кода, так что вы можете столкнуться с сбоями или другими ошибками.   -  person broot    schedule 25.05.2021
comment
Отвечает ли это на ваш вопрос? Повторить задачу с временной задержкой?   -  person Sebi    schedule 25.05.2021
comment
@broot - ваше предложение поместить обе функции в блок postDelayed() сработало отлично! спасибо - никаких других сбоев или ошибок, что всегда приятно! Если вы хотите официально ответить, я подтвержу это, в противном случае я отложу ваш ответ в конце дня, еще раз спасибо   -  person KinectDeveloper23    schedule 26.05.2021


Ответы (1)


Как предложил брут в комментариях, размещение setSegmentColor() и confirmMove() внутри блока postDelayed() позволило добиться желаемой задержки между botTurn()

val botDelay = 1500L

        Handler().postDelayed(
            {
                // colour chosen segment then save move
                setSegmentColor(botMove, -1, activePlayer)
                confirmMove(botMove)
            },
            botDelay
        ) 
person KinectDeveloper23    schedule 28.05.2021