Создаем CSS спрайты. Примеры

05.05.2019

Спрайт (компьютерная графика)

Спрайты в двухмерной графике

Изначально под спрайтами понимали небольшие рисунки, которые выводились на экран с применением аппаратного ускорения . На некоторых машинах (MSX 1, NES) программная прорисовка приводила к определённым ограничениям , а аппаратные спрайты этого ограничения не имели. Впоследствии с увеличением мощности центрального процессора , от аппаратных спрайтов отказались, и понятие «спрайт» распространилось на всех двумерных персонажей. В частности, в видеоиграх Super Mario и Heroes of Might and Magic вся графика спрайтовая.

К аппаратно ускоренным спрайтам вернулись в середине 1990-х годов - когда развитие мультимедиа и взрывное повышение разрешения и глубины цвета потребовало специализированный процессор в видеоплате. Именно тогда, как обёртка над аппаратным 2D-ускорителем, вышел DirectDraw . DirectX 8 ввёл общий API для двух- и трёхмерной графики, и в современных спрайтовых играх двухмерные спрайты выводятся точно так же, как и трёхмерные - как текстурированный прямоугольник.

CSS-спрайты

Применяются в вебе. Несколько изображений располагаются в одном графическом файле. Для того, чтобы вырезать из файла нужный кусок, применяется CSS - отсюда название технологии.

CSS-спрайты экономят трафик и ускоряют загрузку - браузеру потребуется запрашивать меньше файлов.

Спрайты в трёхмерной графике

Движки первого поколения

В первых трёхмерных играх, например, Wolfenstein 3D () и Doom (), персонажи представлялись в виде двумерных рисунков, изменяющих размер в зависимости от расстояния.

На 3D-акселераторах

На 3D-акселераторах применяется такая техника: объект рисуется в виде плоскости с наложенной на неё текстурой с прозрачными областями. Эта технология применяется в трёх случаях.

Оптимизация

Спрайты могут применяться для оптимизации графического ядра, когда отрисовка трёхмерной детализованной модели слишком накладна и может привести к сильному падению скорости рендеринга. Таковыми являются:

  • Объекты, присутствующие в сцене в большом количестве и которые, будучи трёхмерными, имели бы большое количество граней и очень сильно влияли бы на производительность - например, публика (рис. 1), трава и т. д.
  • Удалённые объекты, которые вблизи рисуются полигональными моделями.

Спецэффекты

Для большей правдоподобности изображения используется большое количество сравнительно маленьких спрайтов (так называемая система частиц).

Живая съёмка

Изредка спрайты применяются для добавления в игру живого фото или видео . Например:

  • Живые актёры, например в realMyst (рис. 4).
  • Рычаги управления в симуляторах самолётов, поездов и т. д., сфотографированные в реальной кабине.

Стоит заметить, что в некоторых спрайтовых играх, таких, как Rise of the Triad , спрайты фотографировались с актёров, а не с моделей, игрушек или рисунков.

Появление шейдеров частично снимает потребность в живой съёмке - многие нюансы фотоизображения уже можно передать полигональными моделями в реальном времени.

Разрушение иллюзии

Наблюдение спрайта под несоответствующим углом приводит к разрушению иллюзии. В зависимости от математического аппарата рендеринга («движка»), разрушение может происходить двумя способами.

Терминология

  • Billboard - спрайт, постоянно повёрнутый лицом к камере (по аналогии с рекламными щитами на автодорогах, которые повёрнуты под наиболее выгодным углом).
  • Impostor - спрайт, который заменяет трёхмерную модель на большом удалении.

Для начала краткое вступление. При загрузке странице браузерам допускается только 2 запроса к серверу (у современных браузеров это число увеличилось до 6). Каждый элемент сайта будь то файл с таблицей стилей, файлы javascript, картинки являются независимыми загрузками, которые требуют отдельного запрос на сервер.

На сайте может быть множество миникартинок. Например, смайлики, стрелочки, всевозможные маленькие элементы для дизайна. Каждая такая картинка весит ничтожно мало, но за счет огромного числа запросов создаются проблемы со скоростью загрузки сайта.

Еще давным-давно (2003-2004) было предложено решение этой ситуации с главной целью ускорить загрузку сайта с помощью уменьшения количества запросов к серверу. Это технология получила название "Спрайты".

Что такое спрайты CSS

Спрайт CSS - технология объединения небольших картинок в одну. При помощи сдвигов через CSS достигается вырезание нужной части картинки

Например, вместо 10 картинок грузится одна, но побольше. При грамотном распределение это ускоряет работу сайта. Однако здесь главное не переусердствовать. Не всегда объединение картинок дает преимущество в загрузке сайта.

Пример спрайта CSS

Рассмотрим небольшой пример реализации спрайта CSS. Допустим, мы сделали следующую картинку из трех отдельно взятых стрелочек.

Чтобы обратится к нужной стрелке в CSS надо прописать

Вот как это выглядит на странице:

В рассмотренном примере мы использовали отрицательные отступы. Это делается, для того, чтобы поднять изображение вверх. Например, есть следующий спрайт:


Чтобы вырезать значок RSS мы должны написать следующие отступы

background-position : -90px -40px ;


В примере мы подняли изображение на 40 пикселей вверх и на 90 влево.

Плюсы использования спрайтов

  • Сокращение количества обращений к серверу
  • Если используются динамическое изменение картинки (например, при наведении в элементе меню), то картинка уже будет находится в кэше и поэтому не будет запаздывания на загрузку в нужный момент
  • Если фон объединяемых изображений схож, это еще и уменьшить суммарный "вес" изображений

Из минусов спрайтов: сложность работы в случае ручного труда. Но благо есть бесплатные сервисы, которые готовы упростить работу и сделать все за вас.

Спрайты - это довольно интересная и простая технология. Сейчас я расскажу о ней немного подробнее.

Что такое CSS спрайты?

Если кратко, то CSS спрайты это несколько картинок в одном файле. Файл один, а изображений внутри него несколько. При этом для посетителя это полностью незаметно. Тому кто просматривает сайт кажется, что он видит несколько отдельных картинок.

Зачем это нужно? Спрайты снижают количество запросов к странице от пользователя и уменьшают общий размер изображений. В итоге посетитель быстрее увидит сайт.

Как это делается? Обычная картинка увеличивается в ширину и высоту, то есть просто на обычной картинке размещаются рядом несколько изображений. Потом каждое отдельное изображение из этого набора подставляется в нужный блок с заданной шириной или высотой таким образом, чтобы все остальные изображения не было видно. Весь набор обрезается и остается только одна картинка. Один спрайт. Все остальные картинки остаются за пределами конкретного блока.

Чтобы было понятнее приведу аналогию. Представьте что вы смотрите в замочную скважину. Вы видите только какой то отдельный кусок помещеняи по ту сторону двери. Если вы чуть сдвинетесь в сторону и будете смотреть в скажину под другим углом, то увидите какой то другой кусок того же самого помещения.

Терминология

Чтобы не путаться давайте сразу обозначим термины:
Спрайт - это одна картинка из файла с несколькими изображениями.
Набор спрайтов - это сам файл с несколькими изображениями.

Особенности использования спрайтов

Когда стоит использовать спрайты? Ответ в общем то один - спрайты нужно использовать если у вас на странице много мелких изображений. Неважно какие это изображения. Если у вас много градиентов с одинаковым расположением, много кнопок, много иконок и.д. Если на какой то отдельной странице много мелких изображений, то можно подумать над использованием спрайтов.

На странице как правило бывают три вида картинок - jpg, png и gif. У всех этих форматов есть два режима загрузки - это обычный режим и режим постепенной загрузки.

Формат jpg может быть обычным (базовым) и прогрессивным (progressive). В обычном режиме картинка начинает отображаться по строкам по мере загрузки и сразу в хорошем качестве. В прогрессивном режиме картинка jpg загружается сразу целиком, но в плохом качестве и по мере загрузки качество увеличивается.

Такое же поведение есть у gif и png. GIF может быть обычным и черезстрочным (interlaced). PNG может быть обычным и черезстрочным (interlaced). Поведение черезстрочных gif и png аналогично поведению прогрессивного jpg. Такое поведение немного увеличивает размер файла.

Итого. Изображение может появиться на странице сразу, а может появиться с задержкой. Применительно к спрайтам это важно знать. Спрайты желательно делать черезстрочными или прогрессивными. Пользователь должен как можно быстрее увидеть картинки пусть и в плохом качестве.

Но! Если финальный файл со всеми спрайтами будет слишком большой, то несмотря на всю прогрессивность и черезстрочность посетителю придется ждать даже частичную загрузку файла. Поэтому я не рекомендую использовать большие наборы спрайтов. Если файл большой то полностью теряется весь смысл спрайтов - ускорять работу сайта. С большими наборами спрайтов пользователю придется ждать столько же, если не больше, как и при использовании обычных раздельных изображений.

Большими мне кажутся файлы свыше 30 килобайт. Это субъективно. У вас могут быть какие то свои представления о величине файла. Файл размером 30 килобайт будет загружаться около 7 секунд при скорости интернета 56,6 кбит/с.

Примеры использования спрайтов

Спрайты с иконками

В одном спрайте у меня будут иконки для:

  1. Списка - одна иконка
  2. Ссылок - три иконки
  3. Формы поиска - одна иконка

То есть первый набор спрайтов у меня будет содержать пять картинок. Все картинки у меня будут одинакового размера - 16 на 16 пикселей. Спрайтами можно делать изображения с разными разрешениями, не обязательно чтобы разрешение всех картинок совпдало. При разном разрешении картинок чуть усложняется объединени этих картинок в один файл.

В итоге первый пример будет выглядеть так:

Я нашел пять иконок. После чего просто объединил их все в одном файле. Вот такой вот файл у меня получился в итоге:

Обращаю ваше внимание. В данном случае иконки расположены не вплотную, между ними есть небольшие отступы. Как подобрать эти отступы? Можно конечно рассчитать все по пикселям, но наш случай довольно простой, поэтому здесь лучше всего подобрать эти отступы на картинке экспериментальным путем. Сначала объединяем изображения просто на глаз, потом берем самую верхнюю картинку, ставим ее на нужное место. Если картинка находится на своем месте, но при этом откуда то торчит кусок другого изображения, то значит отступ нужно увеличить.

Еще один момент. Последней в списке идет иконка для списка - зеленая стрелка. Почему она именно последняя? Расположение остальных иконок на картинке нам безразлично, но в списке любой пункт может занимать несколько строк и если зеленая стрелка будет где-то посередине, то на следующих строках будут торчать другие картинки. Посмотрите на картинку списка выше, чтобы понять о чем я говорю.

И так. Я нашел пять иконок, объединил их в один файл. Что делаем дальше? Разумеется пишем код:

  • Пункт списка
  • Еще один пункт списка
  • Пункт списка
  • Еще один пункт списка,
    но в две строки
  • Пункт списка
  • Еще один пункт списка

Это html код списка. Теперь применим к нему наш спрайт:

Ul li{ padding:0 0 0 21px; background:url("sprites.png") 0 -94px no-repeat; }

Что мы тут сделали? Сделали отступ в каждом

  • от левого края в 21 пиксель, чтобы текст не заслонял картинку. Потом в качестве фоновой картинки ставим sprites.png. Высота всей картинки со спрайтами в данном случае 110 пикселей и зеленая стрелка находится в самом конце. Высота зеленой стрелки 16 пикселей, то есть стрелка начинается после 94-го пикселя от верха картинки. Это означает что фон мы должны сдвинуть на 94 пикселя вверх. В css коде это написано так «0 -94px», то есть сдвинули на 0 пикселей вправо и 94px пикселя вверх.

    Закончим со списком. Теперь сделаем примерно так же ссылки:

    A{ padding:0 0 0 20px; background:url("sprites.png") 0 -42px no-repeat; } a{ padding:0 0 0 20px; background:url("sprites..png") 0 -21px no-repeat; }

    Что означают селекторы a? Очевидно данный селектор заставляет браузер применить данный стиль ко всем ссылкам, у которых есть атрибут href, значение которого начинается со строки http://сайт/. Сам спрайт применяется примерно так же, как и в случае со списком. Я рассмотрю только одну ссылку - ссылку на мой блог.

    1. Определяем нужную ссылку по href.. Можно просто присвоить класс нужной ссылке или прописать стили в аттрибут style прямо в html коде. Или идентифицировать нужную ссылку любым другим методом.
    2. Делаем отсуп от левого края у конкретной ссылки в 20 пикселей
    3. Указываем в качестве фонового изображения картинку sprites.png
    4. Картинка которую я подобрал для своего блога находится на расстоянии 21 пиксель от верхнего края, это означает, что фон мы должны сдвинуть на 21 пиксель вниз. В css я это прописал так «0 -21px»

    Домашнее задание

    Спрайты с градиентами

    Теперь посмотрим второй пример.


    На этой картинке изображено окошко. У окна есть заголовок, тело и подвал. У каждого из этих элементов на фоне установлен градиент. Присмотритесь если этого сразу не видно, там есть переход цвета от бледного к насыщенному.

    Я покажу как граденты в этом окошке можно сделать спрайтами. Заголовок и подвал окна будут фиксированной высоты - 30 пикселей. Тело окна будет тянуться в зависимости от длины текста.

    Теперь напишем html код окошка:

    Начинаем применять спрайты. Начнем с заголовка окна:

    #window-header{ height:30px; background:#C0C0FF url("gradients.png") 0 0 repeat-x; }

    В файле gradients.png сначала идет градент для заголовка, потом для тела и потом для нижней строки. То есть градент для заголовка начинается от самого верха. Поэтому мы просто ставим в качестве фона сам файл и позицию указываем как «0 0», то есть никуда не отступать. Чтобы градиент растянулся по горизонтали прописываем «repeat-x».

    Чтобы градент целиком влез в зголовок, указываем высоту в 30 пикселей.

    Точно так же как и заголовок поставим градиент для подвала:

    #window-footer{ height:30px; background: #C0FFC0 url("gradients.png") 0 -60px repeat-x; }

    Только на этот раз мы сдвинем картинку вниз на 60 пикселей.

    С телом окошка ситуация сложнее. Тело у нас будет растягиваться, в отличии от заголовка и подвала. То есть если мы просто сделаем один div для тела окна и поставим туда градент, то в этом диве появятся сразу все градиенты. Как вариант можно поставить градиент для тела последним по вертикали, но что если у нас несколько градиентов для блоков, которые тянутся? Все сразу не сделаешь последними. Мы сделаем чуть хитрее.

    CSS код будет следующий:

    #window-body{ position:relative; } #window-body-gradient{ position:absolute; left:0; top:0; width:100%; height:30px; background:url("gradients.png") 0 -30px repeat-x; } #window-body-text{ position:relative; }

    Теперь расскажу подробнее что мы тут сделали. Вот html код тела окна отдельно:

    Как видите в тело у нас вложено еще два дива. Первый «window-body-gradient» будет отвечать за градиент. Второй «window-body-text» нужен для текста. Кроме того, как это понятно из CSS кода мы применили position:relative; для всего тела окна целиком.

    Для градиентного дива мы указываем position:absolute. Таким образом мы выбили градиентный див из общего потока. Теперь этот див ни на что не влияет. Так как для всего тела целиком у нас указано position:relative, то градиентный див никуда дальше родителя не уплывает. Прикрепляем его к левому и верхнему краю тела окна с помощью «left:0; top:0;». Указываем высоту градиентного дива - 30 пикселей. То есть тут указываем высоту градиента который мы будем прикреплять, если высота дива будет больше высоты градиента, то в диве будут торчать другие спрайты. И наконец прикрепляем к градиентному диву наш файл gradients.png. Как обычно сдвигаем фон вверх на нужное расстояние, в данном случае сдвигаем фон на 30 пикселей вверх.

    Теперь в теле окна у нас есть градиент. Но он заслоняет текст. Чтобы текст не заcлонялся обернем весь текст в див и присвоим ему position:relative. После присвоения текст будет поверх градиента.

    Вот в общем то и все. Теперь мы расставили все градиенты в наше окошко. И в заголовок, и в тело, и в подвал.

    Такие длинные пояснения я делаю, чтобы совсем все было понятно. Но на самом деле если вы немного разбираетесь в верстке, то вам наверное будет достаточно посмотреть сами примеры:

    В очередной раз продублировал ссылку.

    На самом деле можно придумать много примеров по использованию спрайтов. Я показал только два примера, но этих примеров должно хватить для понимания принципов работы спрайтов. Если остались какие то вопросы, то задавайте в комментариях.

    Вы слышали об этом, но...

    Вы действительно понимаете что это такое? Название слегка вводит в заблуждение, потому что спрайты, в данном случае, это не маленькие картинки, а одно большое изображение. Вы, конечно же, встречались с CSS-трюком, в котором для переключения состояния кнопки использовалось одно изображение, которое просто сдвигалось.

    Думаю CSS-спрайты - это расширение этой техники. Разница в том, что вместо двух или трёх изображений, скомбинированных в одно, вы можете использовать сколько угодно картинок в одной. Термин "спрайт" пришёл из компьютерной графики и индустрии видеоигр. Идея была в том, что компьютер может создать изображение в памяти, а затем отображать только его часть, что гораздо быстрее чем создавать каждый раз новое изображение. CSS-спрайты имеют ту же идею: получить картинку один раз, отображать для каждого случая свою часть.

    Зачем комбинировать эти изображения? Разве не быстрее загружать маленькие?

    Нет, это не так. Совсем недавно все разрезали картинки, делали так называемые слайсы, чтобы страница загружалась быстрее. Эта техника создавала видимость того, что страница грузится быстрее, за счёт того, что с сервера подгружалось сразу много кусочков одного изображения, и отображались они почти одновременно. На каждый слайс создавался отдельный http-запрос, и чем больше таких запросов, тем менее эффективна ваша страница.

    Ниже представлена цитата из статьи "Performance Research, Part 1 ":

    В таблице 1 для популярных сайтов показано, что время на загрузку html-разметки варьируется между 5% и 38%. Остальное время (от 62% до 95%) уходит на создание http-запросов для того чтобы подгрузить остальные части документа (например изображения, скрипты и стилевые файлы). Проблема в том, что браузеры могут одновременно качать только в два или четыре потока, в зависимости от версии http и самого браузера. Наш опыт показывает, что сокращение числа http-запросов имеет наибольшее влияние на сокращение времени отклика и зачастую является самым простым способом увеличить производительность.

    Каждое изображение на странице, будь оно в теге img или background-image в вашем CSS-файле - это отдельный http-запрос на сервер.

    Отлично! Итак, как же это сделать?

    Давайте начнём с примера. В представленном CSS для каждого элемента используется своё изображение:

    #nav li a {background:none no-repeat left center} #nav li a.item1 {background-image:url("image1.gif")} #nav li a:hover.item1 {background-image:url("image1_over.gif")} #nav li a.item2 {background-image:url("image2.gif")} #nav li a:hover.item2 {background-image:url("image2_over.gif")} ...

    Используя технику CSS-спрайтов мы реально можем облегчить наш пример. Вместо 10-ти различных картинок, мы можем сделать одну большую, в которой есть все необходимые нам изображения. Теперь наш CSS будет выглядеть так:

    #nav li a {background-image:url("image_nav.gif")} #nav li a.item1 {background-position:0px 0px} #nav li a:hover.item1 {background-position:0px -72px} #nav li a.item2 {background-position:0px -143px;} #nav li a:hover.item2 {background-position:0px -215px;} ...

    Таким образом мы уменьшили количество запросов на сервер на 9, и объём загружаемой информации почти на 8 Кбайт. Но это маленький пример, представьте что будет на реальном сайте.

    Звучит так, что у меня появилось много дополнительной работы.

    Как однажды сказал Чак Норрис: "Все великие вещи требуют большой самоотверженности". Я конечно не уверен ему ли принадлежат эти слова, но это в любом случае хороший совет:) Но к счастью для вас, существует множество веб-сервисов, которые помогают реализовать спрайты без особых усилий. Я остановлюсь на одном - это SpriteMe .

    SpriteMe

    SpriteMe - это закладка. После того как вы разместите её на панели закладок, отправляйтесь на свой сайт, а затем нажимайте на неё. Откроется окошко в правой части вашего сайта.


    Сверху вы увидите список всех фоновых изображений текущей страницы сайта. Ниже список изображений, которые, вероятно, невозможно использовать как спрайты, а также причины. Если вы думаете иначе, вы можете перетащить ссылки из одного списка в другой. После того, как вы собрали необходимые изображения в верхнем списке, вам остаётся нажать на кнопку "Make Sprite" . Всё! Ваши картинки собраны в одну и вы можете её сразу посмотреть


    Для текущей версии сайта это изображение может выглядеть так:

    И наконец, SpriteMe также позволяет экспортировать CSS. Нажмите на соответствующую кнопку (export CSS) и вы увидите необходимый код. Например:

    Link2article { background-image: url(/Content/images/Theme/more.png); background-image: url(http://www.jaredhirsch.com/coolrunnings/public_images/6a02f43daf/spriteme1.png); background-position: -10px -156px; } .articlefooter { background-image: url(/Content/images/Theme/comments_bg.png); background-image: url(http://www.jaredhirsch.com/coolrunnings/public_images/6a02f43daf/spriteme1.png); background-position: 490px -265px; }

    Перечёркнутые строки - это строки из вашего CSS, а ниже предложенные.

    Что не может быть спрайтом?

    В качестве спрайта не может быть повторяющийся фон. Спрайты используются для простых блочных элементов. Например иконки - главные кандидаты на использование их в спрайтах.

    Введение

    Для начала нужно разобраться, что же такое спрайт. Вот такое описание я нашел в книге Андрэ Ла Мота:

    " Знаете, есть такой газированный напиток... Снова шучу. На самом деле спрайты - это маленькие объектики, которые находятся на игровом поле и могут двигаться. Этот термин прижился с легкой руки программистов фирмы Atari и Apple в середине 70-х годов. Спрайты - это персонажи в играх для ПК, которые могут без труда перемещаться по экрану, изменять цвет и размер "

    И так, спрайт это персонаж игры. Не углубляясь в дебри программирования, могу сказать что спрайт это массив из цветов - для простаты представим его как BMP файл или TBitmap, тем более что, этот формат поддерживаемый windows и не содержащий компрессии.

    Что нам нужно от спрайта - заставить его появляться на экране и образовывать анимацию. Анимация это не только смена координаты спрайта, но и изменение самой картинки. Следовательно спрайт может иметь не одно изображение, а несколько. Смена их и приводит к анимации.

    Как я уже говорил спрайт это матрица. При вписывании в кравдрат (прямоугольник) сложного объекта, например волшебника из рисунка ниже, остается свободное пространство. Его заполняют цветом, которого нет в изображении самого объекта. При простом копировании этой матрицы (или для простоты BMP или TBitmap) на экран выводится и волшебник и фон под ним. Но нам это не всегда, подчеркну не всегда, нужно. Если спрайт выводится на фон, то он затирает все квадратную область.

    Не правда ли есть разница, и довольно заметная. При выводе на экран использовался один и тот же рисунок, но все зависит от способа выведения спрайта.

    1-й способ (маг в белом квадрате) основан на простом копировании одной области памяти в другую.

    2-й способ (маг на фоне) то же копирование, но интеллектуальное. Копирование происходит по следующему алгоритму: Если цвет копируемого элементы матрицы (область памяти) соответствует значению цвета Transparent Color, то копирования не происходит, переходим к следующему элементу.

    3-й способ так же основан на копирование области памяти, но с применением логических операций - маски.

    Спрайты c готовой маской

    Способов вывести спрайт на поверхность экрана много. Рассмотрим один из них. Это способ, когда отдельно рисуется спрайт и отдельно маска. Для этого нам понадобится сам спрайт, его маска и буфер.

    Спрайт
    Маска спрайта

    И спрайт и маска должны иметь одинаковый размер, в данном примере 50x50. Для чего нужна маска? Она нужна для того, чтобы при выводе спрайта не затиралось изображение, которое находится под ним. Маску можно заготовить отдельно в BMP файле - более быстрый способ, а можно рассчитать программно.Спрайт и маску помещаем в TBitmap.

    Wizard:=Tbitmap.Create; Wizard.Loadfromfile("spr1.bmp"); // Bitmap для спрайта WizardMask:=Tbitmap.Create; WizardMask.Loadfromfile("spr2.bmp"); // Bitmap для маски

    Ну вот, у нас есть спрайт, маска и нам это вывести его на экран. Для этого существует функция Win32Api:

    BitBlt (param_1,X1,Y1,dX1,dY1,param_2,X2,Y2,param_3);

    • Param_1 - Handle на поверхность куда выводить.
    • X1,Y1 - Смещение от начала координат.
    • dX1,dY1 - Размер выводимого изображения.
    • Param_2 - Handle откуда брать.
    • X2,Y2 - Размер выводимого изображения.
    • Param_3 - Параметры копирования.

    Для нашего случая:

    BitBlt (Buffer.Canvas.Handle,X,Y,50,50, WizardMask.Canvas.Handle,0,0,SrcPaint); BitBlt (Buffer.Canvas.Handle,X,Y,50,50, Wizard.Canvas.Handle,0,0,SrcAnd);

    • SrcPaint - Копировать только белое.
    • SrcAnd - Копировать все кроме белого.

    Сначала выводим маску с параметром SrcPaint, а затем в тоже место (координаты X,Y) сам спрайт с параметром SrcAnd.

    Осталось рассмотреть зачем же нужен буфер. При выводе одного спрайта вы не почувствуете мелькания изображения, но когда их будет 100-200 это будет заметно. По этому все спрайты копируются в буфер - это Tbitmap размером с экран или окно, короче изменяемой области. Вот как окончательно будет выглядеть фрагмент программы:

    Var Wizard, WizardMask,Buffer:Tbitmap; X,Y:integer; ... Wizard:=Tbitmap.Create; Wizard.Loadfromfile("spr1.bmp"); WizardMask:=Tbitmap.Create; WizardMask.Loadfromfile("spr2.bmp"); // Копируем маску в буфер BitBlt(Buffer.Canvas.Handle,X,Y,50,50, Buffer:=Tbitmap.Create; : WizardMask.Canvas.Handle,0,0,SrcPaint); // Копируем спрайт в буфер BitBlt(Buffer.Canvas.Handle,X,Y,50,50, Wizard.Canvas.Handle,0,0,SrcAnd); ... // Перемещаем буфер на форму BitBlt(Form1.Canvas.Handle,0,0,320,240, // Buffer.Canvas.Handle,0,0,SrcCopy);

    Флаг SrcCopy означает копирование без изменения, аналогичен простому перемещению одного участка памяти в другой.

    Не нужно думать, что готовая маска это прошлое компьютерных игр. В любом случае, маска создается, только иногда это делается программно, а иногда заготавливается в виде отдельного файла. Какой вариант лучше, нужно смотреть по конкретному примеру.

    Я не буду расписывать все параметры BitBlt, если интересно смотрите сами в Delphi Help. Ну вот и все. Напоследок исходники и картина творчества.

    Cпрайты c программной маской - Transparent

    Другой метод вывода спрайтов - методом программной маски. Этот способ, немного медленнее, но не требует возни с изготовлением масок. Это не значит, что маски вообще нет. Маска присутствует и создается в памяти.

    Для счастливых обладателей Windows NT подойдет способ, который используется в самой ОС. Это функция MaskBlt. Судя по ее названию, она позволяет выводить растры используя битовые маски.

    Привиду пример на спрайтах из игры Эпоха Империй I. Наша задача, как и во всех предыдущих примерах, вывести спрайт с Transparent Color (по русски плохо звучит). В игре он черный.


    var Sprite,Mask:TBitmap; begin Sprite:=TBitmap.Create; Sprite.LoadFromFile("G0100219.bmp"); Mask:=TBitmap.Create; Mask.LoadFromFile("G0100219.bmp"); Mask.Mask(clBlack); // Создание маски // Преобразование в маску, после этого получится Bitmap, представленный // на Рис 2 MaskBlt(Form1.Canvas.Handle, 10,10, Sprite.Width, Sprite.Height, Sprite.Canvas.Handle,0,0,Mask.MaskHandle,0,0,SRCPAINT); // После вызова этой функции, экран выглядит как на рисунке 3. BitBlt(Form1.Canvas.Handle, 10, 10, Sprite.Width, Sprite.Height, Sprite.Canvas.Handle,0,0, SRCPAINT); end;

    С Windows NT все понятно, но как быть в других ОС? (Хотя возможно, эта функция появится(-лась) в Windows 2000 и Windows Me). Использовать библиотеки сторонних разработчиков. Если они поставляются с исходным кодом, то вы можете перенести необходимые вам процедуры в собственный модуль.

    Я нашел самую быструю библиотеку для работы с графикой - Media Library Component Version 1.93 . В примере используется только часть ее. Нам понадобится только одна процедура:

    DrawBitmapTransparent (param_1,X,Y,param_2,param_3);

    • param_1 - Canvas, куда копировать
    • X,Y - Смещение
    • param_2 - TBitmap, что копировать.
    • param_3 - TColor, цвет Transparent - этот цвет не будет копироваться

    Применение только данной библиотеки не принципиально. Практически в любом наборе VCL компонентов от сторониих производителей есть процедуры или функции для вывода Bitmap с использованием цвета прозрачности. Такие процедуры есть в библиотеке RXLib, LMD Tools, Cool Control и многих других.

    Для нашего примера: DrawBitmapTransparent(Buffer.Canvas,WizardX,WizardY,Wizard,clRed); Спрайт должен выглядеть так:

    Небольшое замечание по поводу Transparent. Цвет надо выбирать такой, которого нет на самом спрайте, иначе неизбежны "дырки" в изображении. Лучше всего такой: #00FF00 - ярко зеленый, но можно использовать черный или белый.

    В предыдущей главе "Работа спрайта c готовой маской" я подвесил передвижение спрайта на таймер:

    Procedure TForm1.Timer1Timer(Sender: TObject); begin ... // тело цикла end.

    Да cпособ хорош, но не так быстродейственен. Есть еще пара вариантов:

    1. Создать поток TThread - в примере разобран именно он.
    2. "Подвесить" на IDL

    Рассмотрим сначала второй способ т.к. он наименее прогрессивен:) Пишем такую процедуру:

    Procedure TForm1.Tic(Sender: TObject; var Done: Boolean); begin ... // Сюда заносим, что надо исполнять. ... Done:= false; end;

    И еще немного:

    Procedure TForm1.FormCreate(Sender: TObject); begin ... Application.OnIdle:= Tic; end;

    Способ быстрее в 1-2 раз чем таймер, но не лишен недостатков. Не буду объяснять почему. Первый способ самый оптимальный для игры, как самой сложной так и простой. Реализуется он с помощью потоков. В игре их можно создать несколько - один для обработки графики, другой для AI, третий для музыки и т.д. У каждого потока свой приоритет, но высший только у одного. При работе с несколькими потоками не забывайте их "прибивать" при выходе из программы.

    Сначала заводим новый класс:

    TGameRead=class(TThread) // класс для таймера игры protected procedure Execute;override; // Запуск procedure Tic; // Один тик программы end;

    Потом переменную:

    Var ... T1:TGameRead; ...

    Описываем процедуры класса:

    Procedure TGameRead.execute; begin repeat synchronize(Tic); until Terminated end; procedure TGameRead.Tic; begin ... // Тут пишем все как в TTimer - OnTime ... end;

    В событии Form1.Create инициализируем поток, и задаем приоритет. Расписывать все приоритеты не буду, читайте Delphi Help ...и не забываем убрать за собой:

    T1:=TGameRead.Create(false); // Создаем поток T1.Priority:=TpHighest; // Ставим приоритет... procedure TForm1.FormDestroy(Sender: TObject); begin T1.Suspend;// Приостановим T1.Free; // и прибьем end;

    Ну вот и все. Ах да, вас наверное заинтересовала строчка FPS. Так это тоже самое, что выдает Quake на запрос "showframerate" или что-то такого плана - количество кадров в секунду. Делается это так: заводится переменная:

    Var G:integer; ...

    При каждом вызове потока Tic, она увеличивается на единицу:

    Procedure TGameRead.Tic; begin ... Inc(G); // Увеличиваем значение G end;

    Создаем таймер с интервалом 1000 - это 1 секунда, и в событии OnTime выводим значение G в метку. В значении G будет количество вызовов процедуры DoSome за 1 секунду:

    Procedure TForm1.Timer1Timer(Sender: TObject); begin label1.caption:="FPS:"+IntToStr(G); G:=0; // Обнуляем G end;

    На моем средненьком Pentium AMD 233 c Intel 740 8M - выдает 90-100 кадров в секунду, при окне 360X360. Для начала неплохо! Исходники , картинка перед вами.

    P.S. У вас может возникнуть вопрос - почему передвижение спрайта за мышкой. Ответ: наименьшие затраты на писанину тест программы, при неплохом разнообразии движения.

    Использование внешних процедур для Transparent вывода спрайтов, хорошо но есть несколько минусов данного способа:

    во первых эти процедуры не слишком оптимизированы - их основное предназначение вывод простеньких элементов приложения, таких как иконок, картинок кнопок и т.д. Хотя это не относится к некоторым библиотекам, код которых на 90% состоит из ассемблера.

    во вторых хранить выводимое изображение нужно в bmp файле, хотя подойдет и любой другой, не применяющий компрессию с потерей (Jpeg) . Если картинок более 1-й, а при нормальной анимации их набирается порядка 150-200 на один юнит, то сложно получать именно нужный участок файла.

    Приведу пример.

    В bmp файле содержатся 8 картинок - 64x64 пикселя. Нужно получить доступ к 6-й картинке (на рисунке помечена розовым квадратом)- ее координаты будут 128,64

    Чтобы получить следующий кадр анимации, нужно снова ко номеру кадра считать координаты: Не совсем удобно. Все эти проблемы можно решить используя TImageList.

    Использование TImageList

    Используя этот компонент можно не думать о координатах картинки, цвете прозрачности - он решает сразу две проблемы.

    Разберем что нужно сделать, для вывода спрайта с использованием TImageList. Во первых нужно загрузить набор спрайтов TImageList, для этого лучше всего использовать команду:

    TImageList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;

    Первый параметр - это Bitmap, второй Transparent Color - цвет прозрачности. Если Вам не нужно использовать цвет прозрачности, то нужно использовать процедуру Add. После загрузки всех картинок, можно приступать к их выводу на экран. Для этого существует процедура:

    Procedure TImageList.Draw(Canvas: TCanvas; X, Y, Index: Integer);

    Первый параметр Canvas на который будет произведена отрисовка, второй и третий координаты для вывода X и Y а четвертый индекс или порядковый номер выводимого изображения.

    Для примера:

    ImageList1.Draw(Canvas,0,0,6);

    BitBlt(Canvas.Handle,0,0,64,64,Bitmap_Mask.Canvas.Handle,128,64,SrcPaint); - маска BitBlt(Canvas.Handle,0,0,64,64,Bitmap.Canvas.Handle,128,64,SrcAnd; - спрайт

    ImageList1.Draw(Canvas,0,0,6);

    Тот же самое, но с использованием BitBlt:

    BitBlt(Canvas.Handle,0,0,64,64,Bitmap_Mask.Canvas.Handle,128,64,SrcPaint); - маска BitBlt(Canvas.Handle,0,0,64,64,Bitmap.Canvas.Handle,128,64,SrcAnd; - спрайт

    Думаю пояснять нет нужды, что использовать TImageList лучше, и проще. Пример работы с TImageList описан в файле. Там показана анимация персонажа из игры WarCraft и Warlord III. Я так и не разобрался как работает механизм отрисовки в TImageList. Мои раскопки привели к такой функции:

    Function ImageList_Draw(ImageList: HImageList; Index: Integer; Dest: HDC; X, Y: Integer; Style: UINT): Bool; stdcall;

    Function ImageList_DrawEx(ImageList: HImageList; Index: Integer; Dest: HDC; X, Y, DX, DY: Integer; Bk, Fg: TColorRef; Style: Cardinal): Bool; stdcall;

    HImageList - Handle на TImageList.

    Так как вызывается экспортируемая процедура, находящаяся в библиотеке Comctl32.dll то остается не понятным, какие алгоритмы используются при выводе изображения. Могу только сказать, что при добавлении нового изображения, добавляется как изображение так и маска.

    Я заинтересовался данным вопросом и продолжал копать стандартные библиотеки Windows и компоненты. Возможно информация по данным вопросам содержится во многочисленных SDK, выпускаемых Microsoft. В компоненте TFastDIB я наткнулся на процедуру Draw:

    Procedure TFastDIB.MaskDraw(fDC,x,y:Integer;c:TFColor); begin TransBlt(fDC,x,y,Width,Height,hDC,0,0,Width,Height,PDWord(@c)^); end;

    Естественно меня заинтересовала процедура TransBlt и вот что я нашел:

    Function TransBlt(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11:DWord):BOOL; stdcall; ... function CreateDIB; external "gdi32.dll" name "CreateDIBSection"; function TransBlt; external "msimg32.dll" name "TransparentBlt"; function AlphaBlt; external "msimg32.dll" name "AlphaBlend";

    Мне захотелось посмотреть, а что еще может библиотека "msimg32.dll" и вот полный список:

    • AlphaBlend
    • GradientFill
    • TransparentBlt
    • DllInitialize
    • vSetDdrawflag

    Все, хватит, а то некоторые читатели и так ничего не поняли. Но для интересующихся скажу - не все процедуры и функции описаны в Delphi, многое не документировано.

    Использование Direct X

    Чем плохи рассмотренные выше методы вывода спрайтов - они медленные. Хочу подчеркнуть, что для каждой программы нужно выбирать свои методы написания. Конкретное задание требует своих средств исполнения. То что Microsoft написал библиотеку Direct X не значит что тут же нужно писать всю графику используя ее.

    Приведу пример. Самая популярная игра для Windows - Quake II, Warcraft, Diablo - нет САПЕР и ПАСЬЯНС. Можете не верить, но это факт. В первую категорию играют ограниченный контингент людей в последнюю играли ВСЕ. Я это говорю к тому, что если вы пишите графическое приложение, то нужно ориентироваться на его потребности и выбирать соответствующие технологию зависимости от них. Какие это потребности:

    необходимость вывода большого количества часто сменяющихся изображений
    большой объем графической информации
    аппаратная поддержка
    максимальное быстродействие

    Используя Direct X можно получит все вышеперечисленное. Набор этих библиотек, изначально разрабатывался как средство для работы с графикой. Что было, когда писали под DOS: строили в участке памяти (back buffer) какое то изображение или копировали туда спрайты, а потом перемещали этот back buffer в область "экранной" памяти. Сразу отрисовывался весь экран. С приходом Windows, переместить участок памяти в экранную область не возможно. Приходится использовать Canvas, Handle.

    DirectX позволяет решить все эти проблемы. Вы можете подготавливать изображение на так называемой поверхности, и потом FLIP и вся поверхность становится видимой - копируется в экранную область видеопамяти. Должен заметить, что алгоритм работы ничуть не меняется.

    С появлением DirectX появились и аппаратные поддержки таких необходимых вещей как: Trancparent Color и Bit blitting.

    Термин бит-блиттинг означает процесс перемещения группы битов (образа) из одного места экрана в другое или памяти. В играх на ПК нас интересует перемещение образа из области хранения вне экрана в область видеобуфера. Кто интересуется аппаратными возможностями своей видео карты, то их можно узнать достав Microsoft DirectX CPL. В ней можно просмотреть, какие функции в видео карте реализуются аппаратно, а какие програмно.

    Итак процесс работы таков, загружаете спрайты на поверхность (ISurface) затем нужно вызвать процедуру BLT или BLTFAST, потом поменять буферную и видимую поверхность командой FLIP и все.

    В начале раздела я написал Direct X, но я несколько обманул Вас. Я расскажу как выводить спрайты с помощью Direct X, но с использованием набора VCL компонентов DelphiX . Я это делаю по той простой причине, что если я напишу пример используя стандартные модули DirectX то их некоторые не поймут, отчаяться и бросят программировать вообще:) Согласитесь не все сразу поймут, что делает данная процедура, хотя она всего лишь меняет поверхности.

    Var hRet: HRESULT; begin Result:= False; while True do begin hRet:= FDDSPrimary.Flip(nil, 0); if hRet = DD_OK then Break else if hRet = DDERR_SURFACELOST then begin hRet:= RestoreAll; if hRet <> DD_OK then Exit; end else if hRet <> DDERR_WASSTILLDRAWING then Exit; end; Result:= True; end;

    По этому я и решил использовать . Писать с помощью него очень просто. Нам потребуется всего два компонента. Первый TDXDraw - если объяснить коротко, то это аналог TCanvas. Еще один компонент это TDXImageList - прямой аналог TImageList, единственно все элементы являются TDIB и не содержат ни каких масок.