Примечание. Большая часть текста - необязательный треп на тему "зачем нужен пререндеринг". Его можно пропустить и непосредственно прочитать секретное заклинание рецепт.
Представьте, что вы задумали игровой шедевр. Вы окинули взором список многоразличных изобразительных средств, подивились красотам шейдерной 3D графики, прикинули трудозатраты и остановились на браузерной игре. Ну, и раз уж у нас теперь есть в чем рисовать графику стандартными средствами, то долой Flash, да здравствует HTML5 и его лучший подарок игроделам - Canvas. Вы проштудировали кучу статей по избранной технологии, пару авторитетных мануалов по гейм-крафту для начинающих, написали движок на JavaScript (а то и на Dart). И вот, самое время оптимизировать и улучшать.
Итак, спрайты. Что такое спрайт всем известно - такая картинка, которая отображает некий игровой юнит. Так как юниты у нас бывают анимированные, то и спрайтов на каждый юнит бывает много, иногда очень много. Есть несколько причин, по которым мы можем захотеть не рисовать все-все возможные варианты отрисовки юнита, а только одну его сторону, с мыслью, "развернуть" картинку програмными средствами уже в самом браузере. Начиная экономией трафика (в середине 2010-х? ха-ха!), или вообще, анимация нашего юнита собирается из нескольких частей, и конечный спрайт, при полной предзагрузке будет потенциально составлять тысячи картинок. Например, 8 основных направлений движения, умноженные на два десятка комплектов одежды, умноженное на полтора десятка спецэффектов, и 16 кадров анимации. 8* 20*15*16 = 38400. Рисовать сорок тысяч спрайтов вручную??? Попахивает запущенной формой мазохизма, не так ли? Следующий логичный вывод - отрисовывать все картинки по мере необходимости на невидимом холсте, и использовать их в качестве источника спрайтов для сцены.
А вот другой вариант - оптимизировать отрисовку с помощью буфера, еще одного холста, на котором рисуется некоторый набор спрайтов, составляющий более или менее постоянный задний или передний план сцены.
Итак, мы приходим к вопросу технической реализации.
Существует масса примеров по работе с контекстом холста (ctx = canvasElement.getContext("2d")) и его содержимым (imageData = ctx.getImageData(0,0,w,h)), вплоть до попиксельной обработки. Проблема возникает тогда, когда мы пытаемся с помощью полученного imageData отрисовать спрайты с прозрачностью. Очевидно, что для игры крайне актуальны именно такие спрайты. Ведь редко где игра построена на исключительно прямоугольных элементах графики.
Надо сказать, проблемы, как таковой не существует. Но, по странному стечению обстоятельств, очевидный способ решения задачи лично мне попался спустя пару недель с момента появления этой самой задачи. Нет, я не искал все две недели именно это, но каждый раз, когда отвлекался, за те несколько минут, которые были употреблены на гугление вопроса пререндеринга в canvas, подходящего ответа не находилось.
Пока, посредством курения http://stackoverflow.com не наткнулся на этот пример: http://jsfiddle.net/Urutb/.
Пример решения простой и понятный: используйте в качестве источника сам canvas, а не ImageData. И все. Итого, для пререндеринга "прозрачных" спрайтов используем следующий алгоритм:
1. Загружаем картинки.
2. Создаем два элемента canvas, один - в качестве невидимого буфера предварительной отрисовки, второй - в качестве целевого объекта отрисовки сцены.
3. Отрисовываем нужные нам картинки (PNG) на холсте буфера методом drawImage, используя при необходимости, приемы трансформации, наложение спрайтов один на другой и т.д.
4. Отрисовываем на сцене участки или весь холст буфера тем же методом drawImage.
Ниже упрощенный пример, состоящий только из вызова ключевых методов.
1. Загружаем картинки.
2. Создаем два элемента canvas, один - в качестве невидимого буфера предварительной отрисовки, второй - в качестве целевого объекта отрисовки сцены.
3. Отрисовываем нужные нам картинки (PNG) на холсте буфера методом drawImage, используя при необходимости, приемы трансформации, наложение спрайтов один на другой и т.д.
4. Отрисовываем на сцене участки или весь холст буфера тем же методом drawImage.
Ниже упрощенный пример, состоящий только из вызова ключевых методов.
-----------------------------------------------------------------------------------------------
Рецепт: // первый холст, буфер
var buffer = document.getElementById('buffer-canvas'); // контекст первого холста
var bufferCtx = buffer.getContext('2d'); // PNG картинка с прозрачностью
var img = document.getElementById('sprite-img'); // отрисовка картинки в буфер
bufferCtx.drawImage(img, 0, 0);
// второй холст, это наша сцена
var scene = document.getElementById('scene-canvas'); // контекст второго холста
var sceneCtx = scene.getContext('2d'); // отрисовка первого холста на втором.
sceneCtx.drawImage(buffer, 0, 0);
Приблизительный HTML для примера:
<img id="sprite-img" src="sprite.png" />
<canvas height="600" id="buffer-canvas"
style="display: none;" width="800"></canvas>
<canvas height="600" id="scene-canvas" width="800"></canvas>
Полный код по ссылке http://jsfiddle.net/Urutb/.
Комментариев нет:
Отправить комментарий