Tower Defence. Часть 2
Tower Defence. Часть 2: Постановка башен
В этой части появляется первый осознанный выбор игрока: куда поставить башню и почему это место подходит или не подходит.
Технологии именно этого урока (lesson-11-7)
- Валидация входных данных через набор маленьких функций.
- Булева логика и guard-условия.
- Работа с коллекциями (
Array.some,Set.has). - Перевод пикселей курсора в координаты клетки (
Math.floor). - Создание объекта башни с предсказуемой структурой.
- Разделение ответственности: отдельные функции для проверки и для изменения
state.
Сквозной прогресс в Practice Preview
С этого этапа линия Tower Defence работает как единый проект:
- После успешной проверки задачи её функция синхронизируется в общий workspace.
- Во вкладке
Previewвpracticeможно сразу увидеть, как изменения влияют на поле. - Для части
placementдоступен milestone-checkpoint: цель модуля считается выполненной, когда все ключевые функции постановки башен проходят checkpoint-assertions.
Фундамент: как игрок кликом выбирает клетку
Клик мыши приходит в пикселях, но игра работает в клетках.
Нужно:
- Взять позицию курсора внутри canvas.
- Разделить на размер клетки (
cellSize). - Округлить вниз через
Math.floor.
const cellX = Math.floor(px / cellSize);
const cellY = Math.floor(py / cellSize);
Так мы получаем адрес клетки, например { x: 4, y: 2 }.
Смотри, что важно: если клик пришел «вне поля» и px/py отрицательные, Math.floor даст отрицательный индекс. Это нормально: дальше это отфильтруется проверкой isInsideGrid.
console.log(Math.floor(-1 / 64)); // -1
Фундамент: почему проверки разбиваем на функции
Вместо одной огромной функции делаем простые правила:
canAffordTower— хватает ли денег.isInsideGrid— внутри ли поле.isPathCell— не занята ли клетка маршрутом.hasTowerAt— нет ли уже башни в этой клетке.
Потом объединяем в canPlaceTower.
Каждая такая функция должна быть простой и предсказуемой: вход -> true/false, без побочных эффектов и без мутации state.
Так код легче читать, тестировать и отлаживать.
Что именно ты реализуешь в задачах
canAffordTower(state, towerCost).isInsideGrid(cellX, cellY, cols, rows).isPathCell(cellX, cellY, pathCells).hasTowerAt(towers, cellX, cellY).createTower(cellX, cellY, cellSize).getCellFromPixels(px, py, cellSize).canPlaceTower(...)как общий валидатор.placeTower(...)со списанием денег иpushвstate.towers.placeTowerFromClick(...)как связка клика и постановки.describeTowerProgress(state, towerCost)для понятной строки прогресса.
Пример композиции проверок
function canPlaceTower(state, cellX, cellY, cols, rows, pathCells, towerCost) {
if (!isInsideGrid(cellX, cellY, cols, rows)) return false;
if (isPathCell(cellX, cellY, pathCells)) return false;
if (hasTowerAt(state.towers, cellX, cellY)) return false;
if (!canAffordTower(state, towerCost)) return false;
return true;
}
Зачем это новичку
Ты учишься проектировать поведение как «набор правил», а не как хаотичный код. Это базовый навык для любой бизнес-логики: от игр до форм, корзин и CRM.
Результат после Part 2
Игрок ставит башни только в корректные клетки, экономика не ломается, а поведение системы остается предсказуемым.