По нажатию кнопки игра уже начисляет очки и расходует энергию. Теперь вынесем расчёт награды в отдельную функцию, чтобы проверять его без многократных нажатий. Сегодня функции получат входные данные через параметры и вернут ответ через return. Правила готовой игры сохранятся.
Сыграй одну партию прямо здесь: собирай кристаллы, отдыхай и при желании покупай улучшение. Правила остались такими же, как в предыдущем уроке; теперь разберём, как функции с параметрами и return делают правила понятнее.
Возьми final/ урока об условиях и победе, он же start/ в комплекте. Ты уже умеешь передавать обработчик события и использовать пустой return; для выхода.
Открыть эту версию в отдельной вкладке
Выбирай стартовую версию, промежуточный этап или финал в просмотрщике. Во вкладке «Инструкция» — как запустить проект и чем отличаются версии. Решения самостоятельных заданий открывай после своей попытки.
Скачать ZIPРаспакуй архив целиком. У каждой версии своя папка с index.html, style.css и game.js. Открывай HTML внутри выбранной папки двойным щелчком; после правки сохраняй файл и обновляй браузер. Установка Node.js, аккаунт и сервер здесь не нужны. CSS уже знаком по первому модулю и дан как готовое оформление.
Считаем награду по силе и количеству кристаллов
В game.js после поиска элементов, перед render, добавь:
function calculateReward(power, amount) {
const reward = power * amount;
return reward;
}
power и amount — параметры, имена для значений, которые поступят при вызове. В скобках вызова calculateReward(2, 3) стоят аргументы: конкретные 2 и 3. Первый попадёт в power, второй — в amount. Формула умножает силу на число собранных порций. В нашей кнопке за действие собирается одна порция, но сам расчёт умеет работать и с другим количеством.
return reward завершает функцию и передаёт вычисленное число к месту вызова. Можно сохранить его в переменную, вывести или прибавить к счёту. Код после return в этом вызове не выполняется. На входе здесь ожидаем числа; функция не превращает произвольный пользовательский текст в корректные данные.
console.log(calculateReward(1, 1)); // 1
console.log(calculateReward(2, 3)); // 6
console.log(calculateReward(2, 0)); // 0
Эти вызовы введи в Console после загрузки страницы. Они ничего не меняют в игре. Функция читает лишь параметры и возвращает ответ, а не трогает DOM или общий score. Так удобно проверять формулу с разными значениями параметров.
Используем новую функцию при сборе кристалла
Внутри handleGather замени одну строку:
score = score + calculateReward(clickPower, 1);
Сначала вызов возвращает награду, затем она складывается со старым счётом, после чего результат присваивается score. При силе 1 это прибавка 1; после покупки силы 2 — прибавка 2. Остальные строки расхода энергии, победы и render оставь на месте. Это step-01/: ты изменил устройство расчёта, но не правила игры.
Проверка: сравни первый клик и клик после покупки с предыдущей версией. Если появляется NaN (нечисловой результат), проверь, что функция возвращает число и вызвана с двумя аргументами. Если пропустить amount, значение параметра будет undefined, а умножение не даст нужной награды.
Где доступны переменные функции
Имя reward, объявленное через const внутри фигурных скобок функции, доступно только в этой области. Оно не является новой переменной всей игры. После calculateReward(2, 3) выражение reward в Console снаружи вызова даст ошибку имени. Получить результат снаружи можно так:
const previewReward = calculateReward(2, 3);
console.log(previewReward);
Названия параметров тоже локальны. Они не обязаны совпадать с именем clickPower, которое мы передаём. Вызов передаёт его текущее числовое значение, а не название переменной. Здесь функция вообще не меняет power; изменение локального числового параметра также не переписало бы clickPower.
Не путай три действия: return отдаёт значение вызывающему коду, console.log показывает диагностическое сообщение, textContent обновляет страницу. Если заменить return на console.log, в Console число появится, но результат вызова окажется undefined. Если убрать вызов render из действия, состояние изменится, а надпись отстанет.
Возвращаем ответ «можно или нельзя»
Рядом с функцией награды добавь:
function canBuyUpgrade(points, price) {
return points >= price;
}
Сравнение уже даёт true или false — логическое значение типа boolean. Поэтому дополнительный if здесь не нужен. Эта функция проверяет только достаточность очков. А обработчик отдельно проверит, что улучшение ещё не куплено и игра не закончена.
В handleUpgrade замени условие выхода на:
if (won || upgraded || !canBuyUpgrade(score, upgradePrice)) {
return;
}
В render замени строку доступности покупки:
upgradeButton.disabled = won || upgraded || !canBuyUpgrade(score, upgradePrice);
Теперь и обработчик, и render проверяют наличие очков через одну функцию. ! по-прежнему меняет разрешение на запрет. Это final урока. Сначала проверь вызовы ниже, затем весь сценарий покупки:
console.log(canBuyUpgrade(9, 10)); // false
console.log(canBuyUpgrade(10, 10)); // true
console.log(canBuyUpgrade(11, 10)); // true
Самостоятельно: остаток после покупки
В отдельной копии напиши remainingAfterPurchase(points, price). Вызывай эту функцию только тогда, когда очков хватает на покупку. Она должна вычесть цену из количества очков и вернуть остаток. Она не меняет общий score, не выводит сообщение и не ищет кнопку. Проверь случаи (10,10) → 0, (13,10) → 3, (20,5) → 15. Перед каждым вызовом запиши ожидаемое значение.
После проверки попробуй использовать её в своём обработчике покупки вместо прямого вычитания. Условие, запрещающее покупку при недостатке, оставь до присваивания. Для следующего урока сохрани базовый final без этой дополнительной функции.
Подсказка 1
Параметры перечисляются в объявлении. Ответ должен попасть на место вызова, поэтому console.log внутри не заменяет нужную инструкцию.
Подсказка 2
Возвращай разность параметров; снаружи можно присвоить результат переменной. Самостоятельно реши, какая сторона вычитания соответствует остатку.
Готово, когда: игра ведёт себя как до изменения, ты различаешь параметры и аргументы, можешь получить число из функции и проверить формулу с тремя разными наборами чисел. Объясни, почему canBuyUpgrade не должна списывать очки: render тоже вызывает её.
Разбор самостоятельного задания
Сначала попробуй решить задание сам. Затем сравни с примером ту функцию или условие, которые вызвали затруднение. Разбор самостоятельной задачи доступен во вкладке «Решение» в просмотрщике исходников.