Я думаю, все знают игру «Змейка». Где нужно собирать какую нибудь точку (или что там ест эта змейка?), которая появляется в случайном месте. Змея становится длиннее с каждой съеденной точкой. Змейка движется не останавливаясь и можно лишь сменить направление. Если голова змеи сталкивается с хвостом или с препятствием, то игра заканчивается.
Так игра выглядит со стороны игрока, но давайте подробно разберем логику игры со стороны геймдева.
Статья написана на основе примера из clickteam — скачать пример.
Логика создания игры «Змейка»
Если вы играли в «Змейку», то знаете, что она движется, как бы прерывисто, с шагом в какой-то момент времени. Так же, наверняка понятно, что хвост змеи состоит из отдельных объектов и когда собираешь точку, хвост становится длиннее на один такой объект.
С каждым перемещением (шагом) головы змеи, на её месте создается «кусочек хвоста», а последний «кусочек» удаляется. Создается впечатление, что змея ползет. Но на самом деле перемешается только голова змеи, а объекты из которых состоит хвост просто создаются в нужном месте и остаются нужное время, и всё!
Игровое поле, как бы, состоит из клеток. На одной из клеток всегда находится съедобная точка, а змейка проходит одну клетку за один шаг. Но для создания игры нам не нужно строить клеточное поле, нам лишь нужно определиться с размером одной клетки. Это важный момент, который нужно продумать в самом начале. Размер этой воображаемой клетки будет являться расстоянием шага змеи и размером объекта съедобной точки ( ну и размеры объектов головы и хвоста змеи тоже не должны превышать эти размеры клетки). К тому же размер сцены должен быть таким, что бы делился на размер клетки без остатка.
В нашем случае размер клетки составляет 32×32 при стандартных размерах сцены 640×480 получается 20 клеток в ширину и 15 клеток в высоту сцены.
Змейка будет перемещаться с шагом 32, а размер точки-вкусняшки будет 32х32. Если нарушить это правило и сделать размер точки отличным от расстояния шага змеи, произойдет баг когда змея и точка находятся на разных линиях. Типа того:
Ширина хвоста змеи должна быть равна шагу змеи в нашем случае — 32. Иначе, возникнет баг «пунктирной змейки»:
Логика станет понятнее в процессе. Теперь давайте делать «Змейку»!
Подготовка объектов.
Вам понадобится как минимум 3 активных объекта: голова змеи, хвост змеи и точка которую нужно собирать. Важно, что бы голова змеи имела 4 базовых направления в статической анимации.
В отношении объекта хвоста змеи это не обязательно особенно если у вас этот объект квадратный.
Внимание: Точку привязки координат у всех трех объектов нужно сделать слева вверху. Формулы и логика представленные в данном примере будут работать корректно только с таким положением точки привязки координат для всех трех объектов.
Ширина хвоста змеи должна быть равна шагу змеи в нашем случае — 32.
Активный объект являющейся головой змеи должен быть статического типа.
Голова змеи. Управление и движение.
По кнопке вверх, вниз, влево и вправо соответственно меняется свойство направления (Direction) объекта головы змеи. Создаем 4 события нажатия кнопок для 4-х сторон движения.
Лично мне нравиться использовать кнопки W, S, A, D.
В следующем событии устанавливается событие таймера для регулярного отслеживания какого-то временного промежутка для одного шага, скажем, каждые 30 миллисекунд. И еще устанавливается второе событие с направлением головы змеи. Этим событиям дается действие, которое изменяет координату головы змеи в зависимости от направления. Так, например, если движение вправо, то координата X устанавливается в значение: текущая координата X головы змеи плюс шаг( 32).
Общий алгоритм движения и управления выглядит так:
Создание хвоста.
В каждом из последних четырех событий добавляем еще одно действие — создание части хвоста в координатах головы. Голова змеи перемещается на один шаг, а на её месте появляется хвост. Так же можно добавить изменение свойства направления у объекта хвоста, если у вас он не квадратный, а прямоугольный как в данном случае.
Теперь если вы запустите игру, то увидите, что за змеёй тянется хвост.
Единственной загвоздкой остается сохранение нужного количества частей хвоста.
Сохранение длинны хвоста.
Что бы хвост сохранял нужную длину надо считать сколько шагов часть хвоста находится в игре, и если это количество превышает общее количество частей хвоста то удалять эту часть.
Для этого создается переменная у объекта хвоста. Назвать переменную можно например «Продолжительность».
Далее создается событие отслеживания такого же временного интервала как и интервал шага, в нашем случае — 30 миллисекунд. В нем создается действие прибавления единицы к переменной «Продолжительность». А в следующем событии происходит сравнение переменной «Продолжительность» с числом которое мы будем считать количеством длины хвоста, например 3. И если «Продолжительность» хвоста превышает это число то удалять этот хвост.
После очередного шага появляется новый объект хвоста и с каждым шагом его «Продолжительность» увеличивается на 1. Каждый объект хвоста будет существовать столько шагов сколько мы установим как число для сравнения, которое в свою очередь должно пониматься как количество частей хвоста. По этому очень важно, что бы переменная «Продолжительность» была переменной именно объекта хвоста.
Так как каждая следующая часть хвоста создается с разницей в один ход, то и исчезать они тоже будут друг за другом с каждым ходом и длинна змейки будет сохраняться.
Съел вкусняшку — стал длиннее
Следующий шаг сделать увеличение змеи когда собирается съедобная точка. Для этого нужно использовать изменяемое значение для сравнения с переменной «Продолжительность». В этом качестве мы можем взять счетчик. В настройках счетчика указываем значение по умолчанию 3.
И в событии сравнения заменяем число 3 на значение счетчика.
Я думаю вы уже догадались, что теперь по событию подбора точки надо просто прибавлять счетчик на 1.
Разместите где нибудь объект точки и создайте событие столкновения головы с точкой. И не забудьте удалять точку.
Теперь змейка растет!
Появление точки в случайном месте
Диапазоном возможных координат у нас будет вся сцена. Так минимальная координата находится в левом верхнем углу и равна нулю (помните, точка привязки координат у объекта слева вверху).
А максимальная координата будет справа внизу. Вычислить значение максимальной координаты очень просто надо от размеров сцены отнять размеры объекта.
X координата: 640 (ширина Сцены) — 32 (ширина точки) = 608,
Y координата: 480 (высота сцены) — 32 (высота точки) = 448.
Таким образом нам нужно генерировать возможные координаты в диапазоне от 0 до 608 по X и от 0 до 448 по Y. И вы также должны помнить, что точка может появляться только на воображаемой клетке поля, про которые шла речь в начале статьи. Получается, что для точки есть 20 возможных координат по ширине и 15 возможных координат по высоте сцены из всего возможного диапазона.
Для генерации случайных координат появления точки нужно использовать генератор случайных чисел — это функция Random(n). Где n — это количественное значение начинающееся с нуля. Допустим выражение Random(3) будет генерировать числа: 0, 1, 2, но не 3!
Итак, что бы сгенерировать координаты в диапазоне всей сцены нужна следующая формула:
Random (Размер сцены / Размер Объекта) * Размер Объекта
Таким образом формула для X-координаты:
Random (Ширина сцены / Ширина Объекта) * Ширина Объекта
или
Random (20) * 32
640/32 получается 20, значит максимум Random может выдать 19, что в свою очередь означает что максимально мы сможем получить значение 608 (т.к. 19*32=608), а 608 и является максимально допустимым значением. Если же Random выдаст минимальное значение-0, то вся формула будет 0, это минимальная координата. В остальных вариантах (от 0 до 19) мы будем получать координату в пределах диапазона с шагом в 32. И в итоге получим 20 ПРАВИЛЬНЫХ случайных варианта для создания съедобной точки в координате X.
Формула для Y-координаты:
Random (Высота сцены / Высота Объекта) * Высота Объекта
или
Random (15) * 32
Если в вашем варианте минимальная координата начинается не с нуля, то формула корректируется простым прибавлением минимального значения координаты:
Минимальная координата + (Random (Размер сцены / Размер Объекта) * Размер Объекта)
Теперь код.
Нам нужно событие для проверки отсутствия точки на сцене. Я буду использовать событие «Количество точек = 0». В этом событии создается новая точка и генерируются случайные координаты.
Что бы событие работало нужно удалить объект точки из области редактирования кадра, даже если объект находится не на самой сцене. Объект останется в игре благодаря событию Create Object, только сначала создайте его.
Либо можете сделать проще — удалять точку при запуске игры:
Теперь можете запустить игру и пособирать точки. Если у вас происходит баг того, что точка и змея на разных линиях,
значит голова змеи изначально находится в не соответствующих координатах, поставьте объект в координату кратную 32 (например 160х96).
Но это еще не все! Точка может появится в недопустимых местах: на хвосте змеи или на препятствии.
Нужно создать событие переопределения координат если такое происходит.
Условие OR (logical) означает, что для выполнения достаточно произойти какому-то одному из двух событий.
Игрок может увидеть момент когда точка на какой-то миг появляется на хвосте или на декорации, а это все таки баг. Нужно сделать точку невидимой пока она не найдет правильное место.
При создании точки задаем действие — сделать точку невидимой:
Делаем событие обратное событию столкновений точки с хвостом либо препятствием, и возвращаем видимость:
Negate — означает противоположное действие (есть не у всех событий). Иными словами, если не происходит столкновения точки с чем либо, то сделать её видимой.
Укусил за хвост или врезался в препятствие — проиграл
Ну тут все просто. Создаете событие столкновения головы с хвостом или с препятствием и включаете флаг №1 у объекта головы, а затем ставите условие, что змея движется только при отключенном флаге.
Столкновения головы с хвостом или с препятствием — включаем «флаг смерти»:
Вставляем условие в события движения, что они могут работать только если выключен «флаг смерти» (все флаги по умолчанию изначально выключены):
Что бы змейка не вертела головой после смерти, добавьте это условие в события управления:
Ну и когда змейка кусает себя или препятствие, меняем анимацию головы со статической на анимацию дохлой змеи. И удаляем хвост:
Препятствия
Далее стоит сделать препятствия, хотя бы для того, что бы создать границу уровня. Заранее предупреждаю, что событие ухода за сцену работать не будет.
Вам стоит делать препятствие размером 32×32 или какой там у вас размер клетки. Препятствие должно быть объектом backdrop и иметь тип Obstacle.
C препятствиями вы сможете строить различные уровни.
В игре осталось доделать что бы хвост на повороте сменял анимацию и выглядел красиво с изгибом, а не так:
И еще. Сейчас змея может повернуть направо из движения влево и наоборот, и так же может повернуть вниз из движения вверх и наоборот. Конечно, это баг и его надо исправить. Об этом пойдет речь во второй части. Продолжение следует…
Задавайте вопросы в комментариях).
Почему хвост создаётся прямо поверх головы?
В этом случае поедание объекта становится невозможным, т.к. при соприкосновении тела и еды запускается алгоритм создания новой еды в рандомном месте. Что за дела
Но хвост не создаётся поверх головы. Скачайте и проверьте исходник
он создаётся прямо на голове, а вот если установить координаты появления относительно головы, т.е. тело появляется на расстоянии 32 по Х или У с плюсом или минусом (смотря в какую сторону поворачивать), то всё будет норм))
Очень полезный и понятный урок.Спасибо вам большое за вашу работу.Жду новых уроков
Спасибо за подробность. Очень полезно, что бы разобраться в принципе работы программы.)
Seyanis, полностью согласен.
отличный гайд, не думал что будет на столько подробно,огромное спасибо!!! Очень ценный урок, которых очень мало по CTF2.5