Демо по мотивам одного видео:
Пример 1 — Вы большой гриб, а повсюду таблетки (как на кислотной вечеринке)
Пример 2 — Человек, управляет грузовиком
Пример 3 — Кидание бракованных кирпичей в колодец
Пример 4 — Битва роботов (PvP)
Дублирующий сервер: 1, 2, 3, 4
Привет ! Это третья статья про игровой движок StalinGrad. В прошлых статьях мы с вами уже насиловали DHTML и чутка разобрали архитектуру. Эта статья будет из двух частей — практика и теория. Если вы не web разработчик, можете сразу пролистать к теории (возможно, она вам будет интересна или совет какой-нибудь напишите).
Что рассмотрели в прошлых статьях:
Доводка до продакшена
JS -> EXE -> CHM -> APK
Об архитектуре
Разбор оружия
Разбор камеры
Пишем первую игру
Пример создания игры:
<div id="TV"><div>
var S = StalinGrad, // Сократили слово, чтобы меньше писать
Russia = S.world(), // Создали мир
ORT = S.camera(Russia, "TV"), // Создали камеру
Vova = S.people("robot1"); // Создали персонажа
S.map(Russia, "Moscow"); // Загрузили карту для мира
S.module.controller(Vova); // Привязали управление к клавиатуре
ORT.bind(Vova); // Привязали камеру к персонажу
Russia.add(Vova); // Добавили персонажа в мир
Демо того, что получится, можно посмотреть тут.
Ожидали большего? Зато ближайшие 10 тысяч букв мы будем разбирать то, что тут написано. Вы можете посмотреть код демо-примеров в этой и прошлых статьях. Скрипты практически везде укладываются менее чем в 20 строк. Возможно, некоторые из вас спросят, почему название статьи не такое: «— Как написать игру на JavaScript за минуту?». Потому что мы все (ну или 95%) прекрасно понимаем, что это просто демо. Чтобы игра вышла в продакшен, нужно ещё подключить кучу модулей, игровой интерфейс, менюшки, миссии, ролики между уровнями, заставки, сообщения, продумать сюжет, продумать и нарисовать карты уровней, создать кучу ботов, обсудить все с разными менеджерами и дизайнерами, получить выговор от заказчика, исправить 100 магических багов, написать кучу «макаронного кода» для всяких «свистелок и перделок». А в день релиза заказчик попросит ещё внести маленькую правку и переделать полпроекта (притом исключительно костылями, т.к. по-другому не успеть). Можно сказать, что StalinGrad не для тех, кто пишет игру на HTML5 за 20 минут, а для тех, кто издевается над JavaScript в течение нескольких дней/недель/месяцев.
Общую логику работы мы с вами разобрали в прошлой статье, а в этой давайте рассмотрим внимательно каждую строку и API.
Как создать мир
StalinGrad.world();
У каждого мира есть набор стандартных параметров, например: длина, высота, время суток, гравитация, сила трения воздуха и т.д. Здесь, как и при создании объектов, вы можете задать свои параметры следующим способом:
var temp = StalinGrad.world({
frictionOfTheAir: 0.01, // трение воздуха
gravitation: 88.2, // гравитация
jiffy: 0.05, // время "мига" для физ. расчетов
meter: 10, // длина "метра" для физ. расчетов
max: {
x: 3300, // длина мира
y: 400, // высота мира
speed: {
x: 20, // ограничение по скорости для X
y: 20 // ограничение по скорости для Y
}
},
respawn: { // координаты точки респауна, для объектов с нулевыми координатами
x: 0, // Если вы загрузите в мир карту — она изменит точку респауна для персонажей
y: 0
}
... и т.д.
});
У каждого объекта мира есть свое API. Самые важные функции в нем: add() и remove(), для добавления объектов в мир и их удаления соответственно.
Как создать объект
Общая схема создания объектов:
StalinGrad.класс_объекта("тип_объекта");
Например персонажи:
StalinGrad.people("nyancat");
StalinGrad.people("ninja");
StalinGrad.people("mushroom");
Или что-то другое:
StalinGrad.block("brick");
StalinGrad.material("floor");
StalinGrad.weapon("portal");
При таком создании каждый объект получает координаты x = 0 и y = 0. Поэтому при создании объекта желательно указывать координаты, в которых он будет находиться. Если вы этого не сделаете и начнете добавлять объект в мир, то он выдаст в консоль ошибку: «Не могу добавить объект. Это место уже занято другим объектом.». Поэтому пишите так:
StalinGrad.класс_объекта(x, y, "тип_объекта");
Например:
StalinGrad.block(0, 25, "brick");
StalinGrad.material(0, 50, "floor");
Стандартная ширина/высота большинства объектов 25 на 25. Почему начальные координаты надо задавать в момент создания объекта, а не позже? Потому что объекты имеют начальные и конечные координаты. Если вы зададите начальные х и y при создании объекта, конечные х и y получатся автоматически (х максимальный = х минимальный + длина). Если решите задать их потом, то максимальные координаты будете считать сами. Например:
vav cat = StalinGrad.people("nyancat");
cat.x.min = 50;
cat.x.max = cat.x.min + cat.width;
А ещё лучше — вообще не лезть в координаты, т.к. скорее всего ваша задача может быть решена через API.
Если по каким-то причинам вам надо изменить ещё больше начальных свойств объекта, то можете использовать такой синтаксис:
var temp = StalinGrad.block({
x: 0, // начальная координата по X
y: 25, // начальная координата по Y
type: "brick", // тип
width: 400, // длина
height: 200 // высота
... и т.д.
});
Информацию обо всех доступных типах вы можете посмотреть в этой таблице.
В интернете широкую известность получил игровой движок Cocos2D. Мне он тоже очень понравился, поэтому я решил импортировать его основу в StalinGrad. К сожалению, после рефакторинга кода, суть кокоса сильно изменилась (осталось только название). Вы можете получить его следующей командой:
StalinGrad.grugs("cocos");
И добавить его в мир, например, так:
var cc = StalinGrad.grugs("cocos");
Russia = StalinGrad.world();
Russia.add(cc);
И что характерно, кокос тут тоже в 2D. Подробную информацию об остальных объектах класса drugs вы можете посмотреть в каталоге.
У многих из вас, наверное, возник следующий вопрос по API: «— Почему различные функции в API не разделены по их типу дополнительными словами?» Например, логичнее сделать следующие схемы:
StalinGrad.object.people("nyancat");
StalinGrad.object.grugs("cocos");
Тогда сразу становится понятно, что people и grugs это методы для работы с объектами. Но это для опытных очевидно, а вот джуниорам — наоборот. К тому же нам нужно запоминать дополнительное слово в конструкции, которое можно сократить. Прекрасно раскрывают суть jQuery и Backbone. В jQuery многие методы настолько интуитивно понятны, что догадаться об их наличии можно даже не читая документацию. А вот Backbone без документации совсем не очевиден.
Как обшманать перса
Разберем работу с личными вещами персонажа. Честно говоря, не придумал адекватного API, поэтому на данный момент схема такая:
персонаж.bag.одна_из_функций_API(персонаж);
Представьте себе мужика с рюкзаком и посмотрите на API:
left(cat); // взять предыдущую вещь из рюкзака
right(cat); // взять следующую вещь из рюкзака
show(cat); // достать вещь (активна, её можно использовать)
hide(cat); // спрятать вещь (если оружие, то оно в кабуре — использовать нельзя)
set(cat, index); // сделать вещь с номером ... активной
get(cat); // получить ссылку на выбранную вещь
add(cat, id); // добавить вещь с id ... в рюкзак (ниже написано про Реестр объектов)
remove(cat, index); // удалить вещь с номером ... из рюкзака
Например:
cat.bag.left(cat);
API кривое и мне не нравится. Так что буду переписывать в перспективе. Персонажа приходится прокидывать каждый раз, т.к. из-за вложенности я не могу из прототипа достать ссылку на оригинальный объект, с которого начиналась цепочка вызовов. Если у вас есть идеи, как можно реализовать API рюкзака более адекватным способом, пишите в комментариях.
Как создать камеру
Настал момент создать камеру и посмотреть на мир:
var NTV = StalinGrad.camera("id_div_элемента"); // эта камера просто заставляет DIV держать пропорцию сторон
var RTVi = StalinGrad.camera(экземпляр_мира, "id_div_элемента"); // эта камера смотрит на мир и выводит изображение в DIV
Как всегда, при создании объекта можно указать дополнительные настройки:
var MTV = StalinGrad.camera({
world: Russia, // мир, который будем показывать
maxRendering: { // дальность отрисовки
x: 400
y: 300
},
screenResolution: { // разрешение экрана
x: 400
y: 300
}
x: 0, // координата по Х
y: 0 // координата по Y
});
Также у каждой камеры есть API, как у джойстика: left(), right(), up() и т.д. Смотрите документацию в прошлом разделе. Например:
RTVi.left(); // сдвинем камеру влево
RTVi.resize(); // подгоним экран под размер окна
Ну и самый важный для нас метод bind(), который необходим, чтобы привязать камеру к персонажу. Например:
ORT.bind(Vova);
Привязывать камеру к персонажу, кстати говоря, можно, в том числе, и только по одной оси. Например:
ORT.bind({
object: Vova, // персонаж, к которому привязываем камеру
type: "x", // ось, к которой привязываем
correction: { // пропорции координат
x: 2,
y: 3
}
});
Вы, наверное, уже задали себе вопрос: «— Что ещё за пропорции координат?!» Дело в том, что если персонаж располагается идеально в центре кадра, это выглядит странно. Поэтому задается небольшое смещение. В данном примере персонаж будет смещен слева от начала кадра по X на пол-экрана, и снизу от начала кадра на одну треть. Можете поменять настройки, и убедиться, что позиция по центру режет глаза (коррекция х = 2, y = 2).
Демо со сбитыми настройками
Как создать джойстик
Как я уже писал в прошлой статье, чтобы управлять персонажем, нам нужен джойстик. Давайте его создадим:
var joystick = StalinGrad.controller(Vova);
У любого джойстика есть стандартное API: left(), right(), attack(), use(), turn(), up(), down(). Вот через это API вам и следует управлять персонажем, а в координаты не лезьте ;)
Но мы же не будем управлять персонажем из кода? Поэтому джойстик на самом деле нам не нужен. А нужен нам модуль котроллера. Мы закинем в него персонаж. Модуль сам создаст персонажу джойстик и привяжет джойстик к клавиатуре.
StalinGrad.module.controller(Vova);
Реестр объектов
Любая фабрика, из тех, что рассмотрели выше, перед тем как вернуть вам объект добавит его в единый реестр игровых объектов. Это нужно для того, чтобы убрать прямые ссылки друг на друга между объектами. Мы уже касались этой темы в прошлой статье. Ссылаться друг на друга — плохо. Это создает проблемы при масштабируемости, при удалении объектов, при попытках остановить утечки памяти и т.д. Кроме того, при вложенности объектов совершенно невозможно преобразовать их в строку через JSON.stringify. Поэтому вещи у персонажа — это только ID вещей, пассажиры внутри транспорта — это только ID пассажиров и т.д. Преобразовывать все в строку нужно для функций сохранить/загрузить. Но о них в другой раз.
Реестр объектов — это как УФМС, только виртуальный. Вы можете обратиться к нему так:
StalinGrad.registry.get("id_объекта");
Через него же работают все сторонние модули, которые не имеют права хранить ссылку на экземпляр у себя, т.к. это может создать неожиданные проблемы в виде утечки памяти и т.п.
Как работает модуль контроллера
Практически в любой игре, которую мы делаем, у нас есть главный герой, которым управляет игрок. Управление во всех играх стандартное, поэтому раз за разом нам предстоит решать одну и ту же задачу — привязывать джойстик к клавиатуре. Чтобы избавиться от этого, код вынесен в модуль, а модуль используется в 99% случаев, поэтому включен в ядро движка. Что же касается самого управления, то оно такое: AWSD или стрелки, E — использовать, Enter — стрелять, э — переключить режим (например, режим гравитационной пушки). Кроме того, привязана ещё куча дублирующих кнопок, чтобы игрок сразу мог взять и начать управлять, вне зависимости от того, куда ткнет пальцем. Непонимание управления было частой проблемой старых игр на JavaScript (да и новых тоже). Сидишь, смотришь демо, тыкаешь все подряд кнопки на клавиатуре, а персонаж так и стоит на месте. И игра вроде хорошая, но поиграть невозможно. Чем я руководствовался при выборе кнопок? Вот такой ситуацией:
Можете попробовать, это забавно. Ну и, конечно же, есть очевидные баги, которые надо решить. Например:
«Нажимаем влево, потом зажимаем вверх. Отпускаем вверх (кнопка влево по-прежнему зажата) и… все, персонаж больше не бежит влево»
Такая же проблема с комбинацией влево + вправо — вправо. Многие, конечно, посоветуют лучше бороться с keypress, но нет, не работает это в JavaScript или работает не так, как хочется по ощущениям.
Выход из ситуации подсказал алгоритм старой змейки, в которой использовалось залипание клавиш: при нажатии клавиши информация о направлении просто записалась в переменную, за которой следит таймер. По сути, между игрой и клавиатурой появился посредник в виде переменной, которая помнит последнее направление. А что делать в нашем случае? А если мы хотим, чтобы одновременно два игрока играли в игру? А что, если нужно, чтобы один человек мог управлять сразу целой армией ботов? Тогда начинаем думать…
На входе у нас есть джойстик с конфигами (или без них, тогда он получит дефолтные конфиги). Создаем карту клавиатуры и отмечаем кнопки, за которыми будем следить. Далее создаем стек событий и массив джойстиков. Если была зажата какая-либо кнопка, модуль смотрит, кто на неё был подписан, и заносит информацию о событии в нужные стеки. Параллельно этому, по таймеру, срабатывает проверка стеков. Специальная функция обходит все стеки, смотрит, какие последние события в них записаны. Далее для каждого стека перебирается массив джойстиков (которые числятся за этим стеком). И для каждого джойстика раздается принадлежащее ему событие. Если игрок отпускает кнопку клавиатуры, то модуль убирает её из всех стеков, которые на неё подписывались. А значит, когда к стекам придёт таймер, он возьмет предыдущее событие, которое работало по прошлой кнопке. Попробуйте в демке зажимать одновременно несколько кнопок.
Схема с одной стороны сложная, а с другой — дает отличные перспективы к масштабируемости. Мы можем задавать конфиги и перечислять список кнопок, на которые мы хотим подписаться. Так можно сделать игру для двоих и более игроков.
Немного демок:
- Игра на два игрока
- Управляем пачкой персонажей (ПЛ`ы и КЛ`ы оценят, особенно если они в несколько окон фармили замки)
Реестр веществ
Примеры веществ в GTA, Stalker`е и Fallout показали, что с ними игра может стать интересней. Встала задача добавить веществ в движок. Изначально все было просто — ты используешь препарат и камера добавляет дополнительный класс на дисплей. Трип создается средствами CSS3 (вращения, трансформации и т.д.). Но схема оказалась совершенно нерабочей в старых браузерах.
Тогда было решено уйти от CSS3 и написать эффекты другими методами и средствами. Эффекты всех трипов я разделил на два вида: наложение фильтра на дисплей и реверс управления. Чтобы наложить фильтр на изображение, создал дополнительный div (прозрачный экран трипов) с z-index выше, чем у всех остальных объектов. Экран трипов принимает класс с background-image, а там прозрачная png картинка с эффектами по типу инстаграмм`а. Далее у экрана трипов меняется прозрачность от 0 до 1 в зависимости от того упорот персонаж или нет. Кроме того, этот экран несет в себе ещё одну функцию — функцию защитного экрана для устройств с тачскрином. Если обладатель тачскрина будет упорно щелкать по картинке, у него может всплыть предложение от системы сохранить картинку. Если же поверх этой картинки будет прозрачный div, никаких сообщений не будет.
Задача с реверсом управления оказалась не столь очевидна. Кроме того, на каждый трип нужно было создавать таймер, чтобы потом отключить действие трипа и сделать экран трипов вновь прозрачным. При масштабировании (создании кучи миров, персонажей и одновременных трипов) такая схема оказалась совсем не эффективной. Мало того, что создавалась куча таймеров, так ещё и связи между объектами персонаж/камера/джойстик усиливались, что противоречило логике модульности. Выход был только один — написать дополнительный модуль ядра, который будет разруливать все, что связано с трипами (у нас тут свой ФСКН с таймером и базой данных).
Разберем схему модуля:
Есть n-е количество миров с m персонажами в них. Если кто-то где-то принимает вещество, то информация о нем отправляется в реестр веществ. Там она заносится в базу данных, а всем камерам, которые следили за персонажем, отправляется команда добавить трип. В реестре есть всего один таймер, который один раз в секунду обходит все записи и минусует время нахождения в реестре. Если у записи время стало равно нулю, она удаляется из реестра, а на все камеры, которые были связаны с этой записью, отправляется сообщение о том, что нужно отключить трип. Кроме того, реестр веществ может сообщать числится в нем персонаж или нет, а если числится, то в каком состоянии он находится на данный момент. Этим API пользуются, например, объекты джойстика, чтобы узнать информацию о своем клиенте. Если джойстик видит, что клиент упорот и упорот сильно, он включает реверс управления, либо накладывает другие эффекты.
Как спрятать труп
С тем, как убить персонажа вроде бы все понятно, а вот что делать с его трупом совсем неясно. Первое, что приходит в голову, тут же удалить тушку из мира. Но это не масштабируемая логика. А что, если мы делаем игру про зомби и вокруг одни трупы? Или у нас есть бессмертный, этакий Дункан Маклауд, который не может умереть? Или мы пишем игру про Чака Норриса, а Чак Норрис не должен умирать, даже если его жизнь на нуле. Поэтому удалять тушку нельзя. С другой стороны — обычно её удаляют и делают это красиво.
Но мы не можем знать наперед, какую анимацию смерти захочет реализовать разработчик.
Тогда, возможно, выходом будет просто оставить все как есть. Предположим, что каждый разработчик напишет цикл, который по таймеру будет проверять жизни персонажей. Если персонаж окочурился, разработчик сам сможет придумать действие с трупом. Но при такой логике рассуждений получается, что все разработчики будут снова и снова писать одну и ту же функцию поиска жмуриков. Тогда можно включить её в состав движка и избавиться от дублирования кода.
Дамы и Господа, встречаем — «функция смерти»:
world.dead();
Функция вызывается каждый раз, когда в нашем экземпляре мира кто-то внезапно «клеит ласты». В качестве аргумента функция ожидает callback-функцию, которая будет вызвана. В callback-функцию будет передан объект, который «сыграл в ящик». Например:
world.dead(function(man) {
alert("Мужик с ID " + man.id + " свое отыграл");
});
На всякий случай, уточню, что world — это мир, в котором мы будем следить за смертью.
Но мы немного отвлеклись. Вопрос «Как спрятать труп?» ещё не решен и тут есть несколько вариантов.
Классический вариант
Персонаж, который дал дуба, улетает вверх. Хороший вариант, но если у нас уровень состоит из бетонных перекрытий (например, главный герой бегает по зданию), тушка не сможет пройти сквозь потолок. Напомню, что обсчет столкновений и физику ещё никто не отменял, и для трупа все силы продолжают считаться. Тогда можно изменить тип объекта на «декорации». Декорации у нас в физ. расчетах не участвуют и могут располагаться как угодно. Но нужно предварительно удалить объект из мира, т.к. он числится в объектах, которые могут двигаться. А потом добавить его в мир ещё раз, уже как декорацию. Ещё есть вариант с мигающим объектом.
Мигающий вариант
Если кого-то убивают, он останавливается, мигает и исчезает вовсе. Для этого эффекта мы должны взять массив спрайтов и каждый четный элемент заменить прозрачной картинкой. Ну, и конечно же, поверх всего добавляется таймер, который в конце анимации удаляет дохлого персонажа из мира. Старая игра «Червячки» спешит предложить нам третий вариант.
Вариант с надгробиями
Запоминаем координаты трупа, убираем его из мира, а на его месте создаем объект «надгробия».
В общем, выбор за вами. Только если у вас скопытился главный герой, не забудьте отключить тут же джойстик, чтобы игрок не мешал анимации. Если вы привязывали управление к клавиатуре через стандартный модуль контроллера, то отключить клавиатуру можно так:
StalinGrad.module.controller.remove();
В качестве аргумента функция ожидает объект персонажа, на которого создавался контроллер. Например:
StalinGrad.module.controller.remove(man);
Два простых примера анимации смерти можно посмотреть тут и тут
Вопросы и ответы
В данном разделе собраны вопросы, которые мне задавали различные разработчики.
Товарищ guyfawkes спросил:
«— А неминифицированную версию вашего движка где можно увидеть?»
Нигде. Это же не open source стартап про HTML5 с профайлом на github`е. Если я покажу исходники — нормальные пацаны, которые пишут на плюсах и яве, сначала будут очень долго ржать, а потом «забьют меня ссаными тряпками». Реализация коллизий — неэффективна, архитектура свойств объектов — неправильная, а алгоритм строительства — вообще говно и должен быть полностью переписан. Надеется на то, что другие это исправят — это миф.
- С одной стороны, люди в web разработке не парятся по поводу архитектуры, а следовательно могут неверно пофиксить баги.
- С другой стороны, каждый смотрит на проект со своей колокольни. Сторонние разработчики могут начать дорабатывать движок не в ту сторону, в какую мне нужно.
- Ну а с третьей стороны, я просто не люблю чужой код, и в этом «домашнем проекте» он мне и нафиг не сдался. Вы можете скачать разного рода конфиги и изменить их.
Т.к. код модульный, вы можете, в состоянии крайней необходимости, переписать какую-либо функцию ядра или сделать обертку к ней, например так:
StalinGrad.brain = function() {
}
Или же дописать модуль и выложить на своем сайте:
StalinGrad.module.YouModule = function() {
}
Используйте StalinGrad — как есть, из коробки. Если не нравится — выберите что-либо другое на свое усмотрение (тысячи этих движков тут и тут). Что-либо другое ещё и глючит меньше. Ну или запилите свое с canvas`ам и WebGL`ем ;)
Гражданин s1im указал на следующий факт:
«— Намного быстрее будет работать если не менять src у картинки, а показывать один длинный спрайт с чередой кадров, отрисовывая его в background какого-нибудь div-а и меняя background-position»
Я думал над этой схемой, и она показалась мне нерабочий (хотя признаю, что с точки зрения производительности s1im абсолютно прав, схема даст очень хороший выигрыш).
Во первых, посмотрим на объект конфига персонажа:
left: [
"partizan_left_1.png",
"partizan_left_2.png",
"partizan_left_3.png",
"partizan_left_4.png"
],
right: [
"partizan_right_1.png",
"partizan_right_2.png",
"partizan_right_3.png",
"partizan_right_4.png"
]
Любой разработчик сразу поймет, что это массив адресов. Сможет исправить адрес картинки или порядок кадров. При том накосячить и поломать код очень трудно. Если мы опишем это в виде координат на спрайте, то код будет выглядеть примерно так:
image: "partizan_left_1.png"
left: [
[ 0, 40 ],
[ 40, 80 ],
[ 80, 120 ],
[ 120, 160 ]
],
right: [
[ 0, 40 ],
[ 40, 80 ],
[ 80, 120 ],
[ 120, 160 ]
]
- Код стал неочевиден с первого взгляда
- Можно легко ошибиться, и указать координату больше, чем длина спрайта
- Если нам нужно будет переделывать чужую игру (например, замену персонажей) — это будет жестко. Нам постоянно нужно будет смотреть в Paint`е на рисунок, и подставлять координаты. И это ещё удачный вариант (на примере шаг четко 40px).
Два года назад я так переделывал одну игру в другую, и дизайнеры прислали спрайты с разным сдвигом между персонажами (да и размер самих персонажей изменился). Это были долгие упорные часы подсчета координат. Координаты выглядели примерно так:
left: [
[ 0, 34 ],
[ 34, 56 ],
[ 56, 89 ],
[ 89, 112 ]
],
Во вторых, мы теряем резиновый интерфейс.
Можно конечно выкрутиться и задать background-size, но на некоторых девайсах background-size не работает. Не то что бы я хотел поддерживать «зоопарк говнодевайсов», но так получилось, что с имеющейся уже архитектурой и выбранными решениями, мне очень легко его поддерживать. Это требует слишком мало усилий. Чуть поправить CSS и вуаля:
- Пример потерь масштабируемости я описывал выше. Помните про функцию смерти? На таких конфигах можно легко сделать анимацию мигания (заменив каждую вторую картинку — прозрачной пустотой). Какую дополнительную выгоду получить от спрайтов — придумать не смог.
- Мы нарушаем логику разделения CSS и JS. Все div`ы будут выглядеть так
<div class="something" style="background-position: 50px 50px;"></div>
Если нужно будет поправить внешний вид, придется лезть в JS, а проблема внешнего вида — это проблема исключительно CSS. Не каждый джуниор полезет править JS конфиги, т.к. побоится что-нибудь сломать. Опять же, есть вероятность случайных поломок, когда он, сам того не заметив, оставит где-либо лишнюю запятую или символ и поломает JavaScript. А вот подобные косяки в CSS не так страшны.
Но с другой стороны, все мы видим как тормозят демки при нагрузке и сколько 404`х шлет сервер. Поэтому наиболее логичным, будет добавить возможность рендера анимации спрайтами, как и предлогал s1im (при этом оставив существующий рендер). Тогда в зависимости от ситуации разработчики сами смогут выбрать реализацию, которая будет оптимальна для конкретной задачи. А ещё, за эти две недели раздумий над вопросом, я осознал, что не хватает модуля для кеширования ресурсов камеры. Постараюсь его реализовать и объяснить в следующих статьях.
А у вас, дорогие читатели, были случаи, когда нужно было поправить внешний вид какого-либо плагина jQuery, а он задавал свой стиль через JavaScript где-то внутри себя? Насколько вас это выводило из себя в тот момент?
Пользователь VoVanJinn хотел узнать, «как заставить котика какать радугой»
Ответ: «— Никак». Мне кажется это задача для одноразовой игры, котора решается костылями, т.к. какать радугой объектам не естественно. Т.к. я пишу прежде всего сам для себя, я могу не решать задачи, которые мне не нравятся или кажутся неправильными. Понятно, что в реальной жизни есть менеджер или адовый клиент, которые могут захотеть все что угодно. На то они и костыли, что бы решать безумные неадекватные задачи. Правильного универсального решения, по крайней мере я — не вижу. Если вы что придумаете — напишите в коментариях к этой статье (снача ещё подумайте о дополнительных багах, которое решение за собой может принести).
«— Canvas же быстрее верстки работает!?»
Отдельно спасибо товарищам SerafimArts и jetman, за их спор про LG Smart TV 2012 года и тест FPS Canvas`а. Результат: LG Smart TV 2012 (Netcast 3.0, модель LM640T): 0-1 FPS, что дает ещё один голос за извращение с версткой.
Напоследок
Как и обещал, выкладываю пакет разработчика. Там есть графика, не сжатые CSS и пара открытых конфигов. Ссылки дублирую, чтобы избежать хабраэффекта:
Скачать с 1-го сервера
Скачать с 2-го сервера
Скачать с 3-го сервера
Кроме того там же есть куча демок (на тот случай, если демки в этой статье у вас висли и не успевали подгрузиться).
Пакет разработчика будет обновляться. В следующей статье я расскажу о игровых модулях и открою ещё несколько конфигов. Кроме того, когда вы сделаете несколько ботов — они будут неподвижны, и не будут стрелять в игрока. Как их оживлять и разжигать ненависть в игровом мире – будет описано в следующей статье.
Подведем итоги: каждый раз, когда я пишу статью и делаю демки, я нахожу столько новых багов, что просто ужас. А бывает так, что при написании статьи я осознаю, что какой-либо модуль ядра вообще работает нелогично и его приходится переписывать, либо переписывать API на более адекватное. Поэтому к следующей статье некоторые мелочи — могут измениться (например, задавать коррекцию для камеры нужно в процентах, а не в каких-то непонятных долях экрана).
И ещё по поводу багов — очень много людей, сказали мне, что у меня неверно работает прыжок. Открою вам тайну — у меня вообще нет прыжка. Это просто кнопка вверх, для движения по оси Y (на основании этого, я ещё собираюсь сделать вид сверху, как в танчиках — для этого нужно всего лишь на всего отключить гравитацию и увеличить трение). А как сделать адекватно кнопку прыжка — я так и не придумал. Пишите в комментариях свои идеи (а ещё по поводу лестницы, т.к. совершенно не понятна суть этого объекта, и по какой схеме его реализовывать)
Сайт движка тут.
Автор: bakhirev