Урок 21. Улучшаем нашу игру (часть 2)

Методы

Как позже вы сами увидите, с помощью методов мы структурируем наш код и предотвращаем его от разрастания:

Напишем метод setQuestion, который будет подготавливать вопрос с определённой сложностью. Напишем метод updateScoreAndLevel, который будет обновлять очки и текущий уровень сложности. К тому же напишем метод isCorrect, которым будем пользоваться другой метод для определения верности ответа. После чего разберёмся, как и в каком порядке вызывать эти методы. Будем выполнять эти задачи и объяснять код постепенно.

 

Метод setQuestion()

После каждого выбора ответа, предоставляемого тремя нашими кнопками необходимо будет задать следующий вопрос пользователю – этим и будем заниматься метод setQuestion. Он будет генерировать значения для переменных partA и partB, а также отображать их в наших TextView, которые ссылаются на объекты textObjectPartA и textObjectPartB. Вдобавок методу нужно будет назначить новый правильный ответ переменной correctAnswer, который будет использован для расчета подходящих неправильных ответов. В итоге метод будет выводить все эти значения на наши кнопки. Ну и напоследок метод setQuestion должен учитывать значение, хранящееся в переменной currentLevel для определения сложности вопроса, который необходимо задать.

Начнём писать код. Разместите каретку после закрывающейся фигурной скобки метода onClick, но перед закрывающейся фигурной скобкой класса GameActivity:

  • Для начала объявим метод:
    void setQuestion(){
  • Присвоили методу возвращаемый тип void – это означает, что ему не требуется возвращать (return) какое-либо значение коду, который его вызовет. Также мы не присвоили никаких параметров, не нужно будет передавать значения при вызове метода. Теперь введем код для генерации двух частей нашего вопроса:
    //генерируем части вопроса
    int numberRange = currentLevel * 3;
    Random randInt = new Random();
    int partA = randInt.nextInt(numberRange);
    partA++;//для того, чтобы не получился 0
    int partB = randInt.nextInt(numberRange);
    partB++;//для того, чтобы не получился 0
  • На предыдущем шаге мы объявили новую переменную типа int – numberRange и инициализировали её умножив переменную currentLevel на 3. Затем мы создаём объект класса Random – присваиваем ему имя randInt и используем его для генерации новых значений на основании переменной numberRange. Делаем это для двух переменных – partA и partB. С ростом значения переменной currentLevel будет возрастать и сложность вопроса. После чего пишем следующие строки:
    correctAnswer = partA * partB;
    int wrongAnswer1 = correctAnswer-2;
    int wrongAnswer2 = correctAnswer+2;
    textObjectPartA.setText(""+partA);
    textObjectPartB.setText(""+partB);
  • Мы присвоили переменной correctAnswer значение, которое будет получено от умножения двух переменных – partA и partB. Затем мы объявили две переменные типа int wrongAnswer1 и wrongAnswer2 и присвоили им значения. Также использовали метод setText для наших TextView объектов для отображения информации, которую несут в себе переменные partA и partB (вопрос пользователю). Но заметьте, что мы еще не отобразили ответы на кнопках. А вот и они:
    //устанавливаем числа в кнопки
    //генерируем случайное число между 0 и 2
    int buttonLayout = randInt.nextInt(3);
    switch (buttonLayout){
            case 0:
                buttonObjectChoice1.setText(""+correctAnswer);
                buttonObjectChoice2.setText(""+wrongAnswer1);
                buttonObjectChoice3.setText(""+wrongAnswer2);
                break;
            case 1:
                buttonObjectChoice2.setText(""+correctAnswer);
                buttonObjectChoice3.setText(""+wrongAnswer1);
                buttonObjectChoice1.setText(""+wrongAnswer2);
                break;
            case 2:
                buttonObjectChoice3.setText(""+correctAnswer);
                buttonObjectChoice1.setText(""+wrongAnswer1);
                buttonObjectChoice2.setText(""+wrongAnswer2);
                break;
            }
        }
  • В этом коде мы использовали наш объект randInt класса Random для генерации числа от 0 до 2 и присвоили получившееся значение переменно типа int – buttonLayout. Затем мы использовали переменную buttonLayout в качестве параметра нашего оператора switch — три возможных варианта: 0, 1 и 2. Каждый из case’ов устанавливает правильный и неправильные ответы в наши кнопки в разном порядке так, чтобы пользователь не мог нажимать на одну и ту же кнопку и постоянно отвечать правильно. Обращаю внимание на вторую закрывающуюся фигурную скобку – она обозначает конец метода setQuestion. Разберём код подробнее:

Сначала мы объявили метод с возвращаемый типом void и без параметров. После мы сгенерировали случайные числа в определённом диапазоне. Мы объявили переменную типа int numberRange и присвоили ей значение:

int numberRange = currentLevel * 3;

Таким образом если игрок находится на первом вопросе, переменная currentLevel равна 1, а numberRange будет равна 3. Затем создали новый объект класса Random и написали:

int partA = randInt.nextInt(numberRange);

Метод nextInt, который мы вызвали для объекта randInt класса Random вернёт значение 0, 1 или 2, потому что в данный момент значение переменной numberRange = 3. Мы не хотим, чтобы получился ноль, потому что результат умножения на ноль слишком прост и поэтому пишем далее:

partA++;//для того, чтобы не получился 0

Оператором ++ мы увеличиваем значение переменной partA на одну единицу. Эти же операции проворачиваем для переменной partB. Если наш игрок находится на уровне 1, то все возможные вопросы, которые будут ему заданы: 1×1, 1×2, 1×3, 2×1, 2×2, 2×3, 3×1, 3×2, 3×3.

С увеличением уровня увеличивается и диапазон возможных чисел для нашего вопроса. Так, на уровне 2 каждая часть вопроса сможет принять значение от 1 до 6, на уровне 3 – от 1 до 9 и т.д. Отметим, что появление легкого вопроса на высоком уровне присутствует, но вероятность этого события слишком мала.

После мы вычислили и присвоили получившееся значение переменной correctAnswer, а также объявили и присвоили значения переменным wrongAnswer1 и wrongAnswer2. Этим переменным мы отнимаем и добавляем 2 соответственно. Это не сильно усложняет процесс отгадки, возможно вы придумаете лучше. Далее мы случайным образом раскидываем значения правильного и неправильных ответов по кнопкам.

Метод updateScoreAndLevel()

Имя метода говорит само за себя. Следуем по пунктам:

  • Код этого метода можно расположить в любом месте внутри нашего класса GameActivity (то есть в пределах между открывающейся и закрывающейся скобками GameActivity). Но хорошим тоном будет расположить код в порядке его использования. Так почему бы нам не начать писать код после закрывающейся фигурной скобки метода setQuestion, но перед закрывающейся скобкой класса GameActivity. Объявляем метод:
    void updateScoreAndLevel(int answerGiven){
  • Мы видим, что метод не будет возвращать значение, вызвавшему его коду, но будет принимать параметр типа int. Имя параметра подобрано по смысловой нагрузке, которую оно должно нести. Следующий кусочек кода может немного сбить вас с толку, но объяснение будет чуть позже. Добавьте его за тем, что вы только что написали:
    if(isCorrect(answerGiven)){
        for(int i = 1; i <= currentLevel; i++){
            currentScore = currentScore + i;
            }
        currentLevel++;
    }
  • Пока мы видим, что в условии if вызывается метод isCorrect(), который мы напишем чуть позже. И если ответ выдаст значение true то мы входим в цикл for и добавляем очко игроку. После чего добавляем к значению переменой currentLevel единицу. Далее пишем часть кода, если же ответ примет значение false:
    else{
        currentScore = 0;
        currentLevel = 1;
    }
  • Если метод isCorrect примет значение false, а этому будет соответствовать неверно данный ответ пользователя, то мы обнуляем значение переменной currentScore и присваиваем значение 1 переменной currentLevel. Наконец, в этом методе мы будем выводить значения переменных currentScore и currentLevel:
        //Отображаем текущие значения в наших TextView
        textObjectScore.setText("Score: " + currentScore);
        textObjectLevel.setText("Level: " + currentLevel);
    }
  • На предыдущем шаге, мы отображаем информацию в наших TextView с учетом их значений на данный момент. Далее метод заканчивается закрывающейся фигурной скобкой и программа возвращается к коду, который вызвал метод updateScoreAndLevel. Сохраните проект.

Теперь копнём чуть глубже в код, который мы написали. Сначала мы объявили метод с возвращаемым типом void и параметром типа int. После идет оператор if:

if(isCorrect(answerGiven)){

Мы уже видели использование if в такой конструкции. Здесь происходит следующее: для проверки значения (true/false) в данном случае в условии используется не переменная и не выражение, а метод (будет создан позже, поэтому сейчас он выделен красным цветом у вас в Android Studio). Метод isCorrect в свою очередь в качестве параметра принимает переменную answerGiven. И на основании значения, которое примет метод isCorrect (если ответ верный – true, если неправильно дан ответ — false) будет принято решение о дальнейших действиях. Если оператор if примет значение true, то выполнится следующая часть кода:

for(int i = 1; i <= currentLevel; i++){
    currentScore = currentScore + i;
}
currentLevel++;

Программа входит в цикл for, где мы объявляем и инициализируем переменную i типа int со значением 1. Кроме того, цикл будет продолжаться до тех пор, пока эта переменная i меньше или равна значению переменной currentLevel. Затем, уже внутри цикла for мы добавляем значение нашей i к текущему значению переменной currentScore и присваиваем получившееся число переменной currentScore. В качестве примера, предположим, что игрок ответил на вопрос правильно и мы входим в цикл for со значением currentLevel = 1, а currentScore = 0:

На первом проходе цикла мы получаем следующее:

  • i = 1. Получается, что оно ровно значению currentLevel (оно сейчас тоже равно 1). Значит мы входим в цикл;
  • i = 1. currentScore = 0;
  • Мы складываем значения i и currentScore (1 + 0) и присваиваем значению currentScore;
  • Теперь currentScore = 1

На втором проходе цикла мы получаем следующее:

  • i++, теперь i = 2 и это больше чем значение в currentLevel (1);
  • результатом вычисления условия цикла for становится false, мы не входим в цикл и код продолжается с места после закрывающейся скобки цикла for;
  • currentLevel++ — здесь переменная currentLevel примет значение 2.

А теперь посмотрим на действия в цикле for учитывая, что пользователь второй раз дал верный ответ на вопрос и мы опять возвращаемся к методу updateScoreAndLevel. И снова isCorrect принял значение true, и мы снова вошли в цикл for, но на этот раз с другими данными.

Что произойдёт на первом проходе:

  • i = 1, меньше чем currentLevel (2) – заходим в цикл for;
  • i = 1, currentScore = 1;
  • добавляем i (1) к currentScore (1);
  • теперь значение нашей переменной currentScore = 2.

На втором проходе цикла:

  • i инкрементируется (увеличивается на единицу) до 2-х и становиться эквивалентно значению переменной currentLevel (2);
  • i = 2, currentScore = 2;
  • добавляем i (2) к currentScore (2);
  • значение переменной currentScore = 4.

На третьем проходе цикла:

  • i инкрементируется до 3-х, что больше значения переменной currentLevel (2) на данный момент;
  • условие в операторе if примет значение false и мы не зайдем внутрь цикла for, а код продолжится наследующей строчке, идущей после for-цикла;
  • currentLevel++ — увеличиваем переменную на единицу и она примет значение 3.

Получается, что с каждым новым уровнем увеличивается количество проходов цикла for и, соответственно, увеличивается получаемое количество очков (логично, что за более сложные вопросы пользователь получит более высокое вознаграждение). Вот таблица, которая проиллюстрирует первые пять уровней и значения переменных:

currentLevel  Добавление к currentScore currentScore после цикла for
1 1 1
2 3(1+2) 4
3 6(1+2+3) 10
4 10(1+2+3+4) 20
5 15(1+2+3+4+5) 35

Конечно, мы могли бы обойтись без цикла for и написать код для подсчета очков в более простом виде, но наша цель – это попрактиковаться в программировании.

Если if(isCorrect(answerGiven)) примет значение false, то счетчик очков (currentScore) сбрасывается до 0, а уровень (currentLevel) до 1.

Нам осталось написать еще один метод и, как вы уже, наверное, догадались это будет isCorrect().

 

Метод isCorrect()

Код этого метода достаточно просто, потому что мы уже знакомы со всеми его составляющими. Новенькое будет только в объявлении метода:

  • Вводите код на следующей строке после закрывающейся фигурной скобки метода updateScoreAndLevel(), но перед закрывающейся скобкой класса GameActivity. Объявляем метод:
    boolean isCorrect(int answerGiven){
  • Мы видим, что возвращаемый тип этого метода – boolean. Если метод не вернёт значение true или false наше приложение попросту нельзя будет компилировать и Android Studio выдаст ошибку. Значит нам нужно будет внутри метода вернуть значение (return). Также мы видим, что метод должен принять параметр типа int. Пишем далее:
    boolean correctTrueOrFalse;
    if(answerGiven == correctAnswer){
        Toast.makeText(getApplicationContext(), "Well done!", Toast.LENGTH_LONG).show();
        correctTrueOrFalse=true;
        } else {
        Toast.makeText(getApplicationContext(), "Sorry", Toast.LENGTH_LONG).show();
        correctTrueOrFalse=false;
        }
  • Мы уже видели бОльшую часть кода ранее. Разница лишь в том, что мы объявили переменную типа boolean – correctTrueOrFalse, которой мы присвоим значение true если пользователь верно ответит на вопрос и false в случае неверного ответа. А эту информацию мы узнаем путём сравнения двух переменных: if (answerGiven == correctAnswer). Еще мы должны были импортировать класс Toast сочетанием клавиш Alt+Enter по напоминанию от Android Studio. В конце мы пишем:
        return correctTrueOrFalse;
    }

Этой строкой мы возвращаем коду, который вызвал метод isCorrect значение, которое хранится в переменной correctTrueOrFalse. Отсюда оператор if в методе updateScoreAndLevel, с которым мы уже разобрались узнает, что ему нужно делать.

Давайте последовательно разберем метод isCorrect:

Для начала мы объявили метод – присвоили ему имя, возвращаемый тип, параметр. Далее объявили переменную типа booleancorrectTrueOrFalse для хранения значения, которое в итоге вернём. Затем мы проверяем правильность ответа – if (answerGiven == correctAnswer). Если оба значения этих переменных совпадают – выдаём поздравительное сообщение и присваиваем значению переменной correctTrueOrFalse – true. В противном случае выдаём сочувствующее сообщение и присваиваем false переменной correctTrueOrFalse. И наконец мы возвращаем (return) true или false, чтобы метод updateScoreAndLevel мог выполнить свою часть работы.

Мы реализовали все необходимые нам методы. Теперь требуется ввести их в работу. В следующем уроке будем вызывать наши методы. Код в этом уроке и то, как должны выглядеть наши java-файлы на данный момент доступны для скачивания ниже.

Скачать исходники