Реализация Asteroids на javascript

от автора

Чтобы хоть как-то разбавить тенденцию к 30строчникам решил написать достаточно завершенную и, в сравнении с 30lines, объемную реализацию классической игры Asteroids.
screenshot
Я не буду меряться числом строк или символов кода, т.к. в нем есть и достаточно нормальное оформление и даже комментарии.
Мир игры отрисовывается на canvas, все объекты мира унифицированы, а детектор коллизий использует попиксельный тест. Есть простая озвучка, жизни, godmod на первые секунды после появления, очки, растущая вместе с очками сложность и, конечно, разваливающиеся на куски астероиды 🙂
Попробовать можно тут. Очень советую Chrome или хотя бы FF.

Для уменьшения объема логики все объекты мира сделаны универсальными, с минимальным разделением в зависимости от конкретного типа. Геометрия объектов хранится в списках вершин, прямо как раньше делали через glBegin(GL_LINES). Для корабля отводится две геометрии (обычная и с пламенем при разгоне) ради все той же унификации логики. Если поломать код, то можно сделать астероиды или пули в форме корабля, ну и т.п. Все точки в геометрии задаются в координатах [-1..1], а конкретные размеры получаются масштабированием на уровне отрисовки в canvas. И для любого размера астероида можно применить любую геометрию (вариантов астероидов несколько).

Каждый объект обладает группой характеристик:

  • Тип объекта: корабль, астероид, пуля. НЛО пока нет;
  • Текущей геометрией;
  • Координатами центра геометрии в СК экрана;
  • Размер объекта, как множитель координат в геометрии;
  • Проекции вектора скорости на координатные оси;
  • Коэффициент торможения (>1 для корабля, что приводит к торможению после ускорения);
  • Угол поворота и «угловая скорость», применяемые для астероидов и пуль;
  • Время жизни объекта, если это время ограничено (применяется для пуль).

Унификация значительно упростила обработку особых ситуаций, вроде заворачивания объекта при его уходе за границы экрана.

Вся логика заворачивания для обеих осей

S — данные объекта, W — данные мира

[{x:'x', W:W.w}, {x:'y', W:W.h}].forEach(function(_) {     var limit = (_.W>>1)+S.r, diff = _.W+2*S.r;     if      (S[_.x] < -limit) {S[_.x] += diff}     else if (S[_.x] >  limit) {S[_.x] -= diff} }); 

Код можно еще сократить. Но имхо от станет менее понятным.

В одной из первых версий пробовал вариант с отображением объектов в две части, при частичном заходе за края (этакий Portal). Выглядело забавно, но мне показалось совсем не каноничным. Может стоит вернуть?

Дифференциация по типам выполняется лишь в логике обработки коллизий, чтобы понимать, что вообще с чем столкнулось. Сейчас учитываются столкновения астероидов в кораблем и пулями. Есть мысли о столкновении астероидов между собой, ну и, конечно, введение в игру НЛО.

imageПервоначальный вариант коллизий по оболочкам (bounding box через описанные окружности) показал несостоятельность, когда в случае, как на картинке справа, срабатывала коллизия. Это было недопустимо, так как лишала всего «драйва». Придумал два варианта решения: математический обсчет пересечения геометрии (любой объект представляется замкнутым набором отрезков) и попиксельное тестирование именно так, как отрисовывает движок браузера. Первый вариант, теоретически, менее ресурсоемок, но выбрал я второй.
Создается вторая canvas, на которую (только если тест оболочек подтвердил возможное пересечение) отрисовываются два проверяемых объекта, но в красно-зеленой цветовой гамме с активным блендингом на 50%. В итоге, в месте реального пересечения у нас появятся пиксели, имеющие и красную и зеленую компоненты. Очень просто, хоть и не эффективно.

Тест на второй canvas выглядит примерно так

image
Скриншот немного кривой из-за отсутствия синхронизации отрисовки со скриншотилкой

Ну и некоторое уже более-менее «мясцо» напоследок:
image
Видно, что астероиды выводятся wireframe. Опять же ради толики каноничности.

ссылка на оригинал статьи http://habrahabr.ru/post/202888/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *