Как приостановить цикл, чтобы дождаться нажатия кнопки

Я пытаюсь создать пошаговую игру, используя битву 1 на 1 для Android. Мой основной игровой цикл проверяет, мертвы ли два бойца, если нет, то проверяет, кто будет следующим. Если настала очередь игрока, то он должен дождаться нажатия кнопки атаки. Если настала очередь компьютера, то он выполнит случайную атаку. У меня возникли проблемы с тем, чтобы программа ждала ввода пользователя. Я попытался настроить прослушиватель кнопок здесь, но это не так. [править] Определение того, какой персонаж пойдет, основано на целочисленном значении восстановления. Каждая атака имеет значение восстановления (50-100), которое добавляется к восстановлению персонажа. Метод nextMove() проверяет, какой из них ближе к 0, и вычитает разницу из обоих символов. Это позволяет игре требовать больше стратегии, потому что вы не атакуете только один раз за ход.

Что я могу сделать, чтобы игра остановилась в этот момент?

Вот код

public void battle(){
    boolean playerGo;
    while(!checkDead()){
        playerGo=nextMove();  //returns true if its the players turn to go
        if(playerGo){
            //The game should wait here for the user input

            moveButton1.setOnClickListener(this);

        }
        else{
            randomMove();  //game automatically goes
        }
    }

}

person willmer    schedule 09.08.2012    source источник


Ответы (2)


Когда ваше приложение запускается, есть один поток, в котором все выполняется, включая обработчики событий. После того, как вы выполнили настройку и вызвали battle(), этот поток сидит там, ходит по кругу и по кругу. Он так занят циклом, что не замечает, что есть событие щелчка, ожидающее обработки!

Есть несколько вариантов:

  1. Реструктурируйте свой код. Похоже, основная структура такова, что игрок двигается, а затем движется игра. Вы можете полностью удалить этот цикл и вместо этого вызывать randomMove() после каждой обработки хода игрока. Обработайте ход игрока в OnClickListener за moveButton1. Таким образом, все просто происходит на событиях. В целом это было бы проще, и, вероятно, это правильно.
  2. Внесите минимально возможные изменения в свой код, чтобы он заработал. Это, вероятно, будет означать перенос содержимого вашего цикла while в Runnable, который вы запланируете, вызвав Handler.post. Первая строка вызывает checkDead и возвращает значение, если оно истинно. Последняя строка переназначает Runnable. Между ними находится тело цикла while. Результатом этого является то, что ваше тело цикла запускается, затем обработчик события получает ход, затем ваше тело цикла запускается, затем запускается обработчик события. Это, наверное, плохая идея.
  3. Запустите battle() в другом потоке. Это, наверное, плохая идея.

Почему 2. и 3. плохие идеи? На мобильном устройстве время автономной работы очень ценно, и выполнение проверки, чтобы увидеть, нужно ли вам что-то делать снова и снова, заставит ЦП перегружать время работы от батареи. Гораздо лучше сидеть без дела, пока вам не нужно что-то делать — это то, чего достигает вариант 1.

Итак, если 2. и 3. — плохие идеи, зачем их упоминать? Wellllllll, 2. Я упоминаю, потому что это самое близкое, что у меня есть, к ответу на вопрос, который вы действительно задали. Я упомянул 3. потому что в некотором смысле ваш текущий код является довольно четким воплощением игровой логики. Вы можете переработать его так, чтобы он выполнялся в отдельном потоке, и вместо того, чтобы nextMove() возвращал true, nextMove() ожидает, пока игрок не сделает ход (это может включать семафоры, мьютексы или промисы). Но это будет явно многопоточная программа, и поэтому ее будет сложно правильно написать. Я рекомендую вам не пытаться делать это на данном этапе вашей карьеры программиста — наиболее вероятным результатом будет программа, которая остановится и будет ждать вечно, или которая искажает свои структуры данных способом, который чрезвычайно трудно диагностировать.

person Iain    schedule 09.08.2012
comment
Ян, спасибо за информативный пост. Я думаю, что смогу прояснить некоторые вещи, которые облегчат решение. Битва не в реальном времени, я использую статистику восстановления, чтобы выяснить, кто идет дальше. Каждый раз, когда производится атака, значение (50-100) добавляется к характеристике восстановления персонажа. Функция nextMove() ищет, кто ближе всего к 0, и вычитает это количество из обоих. Это позволяет мне вести более динамичную битву, так что, если вы используете атаки с более низким восстановлением, вы можете атаковать чаще, чем A идет, затем B, повторяйте до смерти. Могу ли я сделать это, не используя батарею - person willmer; 09.08.2012
comment
Что заставляет делать следующий ход? Всякий раз, когда это происходит, звоните, чтобы решить, кто будет следующим. Если настала очередь игрока, просто вернитесь. В противном случае сделайте следующий случайный ход. - person Iain; 11.08.2012
comment
Вы можете отключить кнопки, когда это не ход игрока, и снова включить их, когда вы ожидаете, что они что-то выберут. Однако это зависит от того, сколько времени вы тратите и какую обратную связь вы даете им, когда делаются случайные ходы. - person Iain; 11.08.2012
comment
Это именно то, что я придумал за несколько часов до вашего сообщения. Большое спасибо за твою помощь. - person willmer; 13.08.2012

Функция Button.SetOnClickListener() будет запущена только тогда, когда пользователь нажмет кнопку. Таким образом, он не ждет\блокируется до ввода пользователя. Это заложено в Android так, что у вас не может быть блокирующего окна, ожидающего ввода данных пользователем. Вместо этого измените свой дизайн, чтобы отобразить подсказку о том, что «теперь это ход пользователя».

  1. Пользователь делает первый ход, нажимая на кнопку.
  2. Будет вызван SetOnclickListener(). Имейте код действия пользователя внутри него.
  3. В конце SetOnclickListener() есть код действия компьютера.

С помощью этого цикла вы можете связать перемещение пользователя и перемещение компьютера.

person Rahul Sundar    schedule 09.08.2012