Чтобы хоть как-то разбавить тенденцию к 30строчникам решил написать достаточно завершенную и, в сравнении с 30lines, объемную реализацию классической игры Asteroids.
Я не буду меряться числом строк или символов кода, т.к. в нем есть и достаточно нормальное оформление и даже комментарии.
Мир игры отрисовывается на canvas, все объекты мира унифицированы, а детектор коллизий использует попиксельный тест. Есть простая озвучка, жизни, godmod на первые секунды после появления, очки, растущая вместе с очками сложность и, конечно, разваливающиеся на куски астероиды :)
Попробовать можно тут. Очень советую Chrome или хотя бы FF.
Для уменьшения объема логики все объекты мира сделаны универсальными, с минимальным разделением в зависимости от конкретного типа. Геометрия объектов хранится в списках вершин, прямо как раньше делали через glBegin(GL_LINES). Для корабля отводится две геометрии (обычная и с пламенем при разгоне) ради все той же унификации логики. Если поломать код, то можно сделать астероиды или пули в форме корабля, ну и т.п. Все точки в геометрии задаются в координатах [-1..1], а конкретные размеры получаются масштабированием на уровне отрисовки в canvas. И для любого размера астероида можно применить любую геометрию (вариантов астероидов несколько).
Каждый объект обладает группой характеристик:
- Тип объекта: корабль, астероид, пуля. НЛО пока нет;
- Текущей геометрией;
- Координатами центра геометрии в СК экрана;
- Размер объекта, как множитель координат в геометрии;
- Проекции вектора скорости на координатные оси;
- Коэффициент торможения (>1 для корабля, что приводит к торможению после ускорения);
- Угол поворота и «угловая скорость», применяемые для астероидов и пуль;
- Время жизни объекта, если это время ограничено (применяется для пуль).
Унификация значительно упростила обработку особых ситуаций, вроде заворачивания объекта при его уходе за границы экрана.
[{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). Выглядело забавно, но мне показалось совсем не каноничным. Может стоит вернуть?
Дифференциация по типам выполняется лишь в логике обработки коллизий, чтобы понимать, что вообще с чем столкнулось. Сейчас учитываются столкновения астероидов в кораблем и пулями. Есть мысли о столкновении астероидов между собой, ну и, конечно, введение в игру НЛО.
Первоначальный вариант коллизий по оболочкам (bounding box через описанные окружности) показал несостоятельность, когда в случае, как на картинке справа, срабатывала коллизия. Это было недопустимо, так как лишала всего «драйва». Придумал два варианта решения: математический обсчет пересечения геометрии (любой объект представляется замкнутым набором отрезков) и попиксельное тестирование именно так, как отрисовывает движок браузера. Первый вариант, теоретически, менее ресурсоемок, но выбрал я второй.
Создается вторая canvas, на которую (только если тест оболочек подтвердил возможное пересечение) отрисовываются два проверяемых объекта, но в красно-зеленой цветовой гамме с активным блендингом на 50%. В итоге, в месте реального пересечения у нас появятся пиксели, имеющие и красную и зеленую компоненты. Очень просто, хоть и не эффективно.
Скриншот немного кривой из-за отсутствия синхронизации отрисовки со скриншотилкой
Ну и некоторое уже более-менее «мясцо» напоследок:
Видно, что астероиды выводятся wireframe. Опять же ради толики каноничности.
Автор: AterCattus