-
-
Notifications
You must be signed in to change notification settings - Fork 0
Поверхности
В обычных событиях отрисовки GameMaker не отрисовывает напрямую на экран, а вместо этого рисует на поверхности, называемой поверхностью приложения.
Эта поверхность — по сути чистый «холст», который можно изменять перед выводом на экран по мере необходимости, и в большинстве случаев GameMaker делает это автоматически (хотя вы можете управлять ею самостоятельно в коде для шейдеров, масштабирования и других задач — подробности приведены ниже).
Помимо поверхности приложения, вы можете создавать собственные поверхности и использовать их для реализации ярких или тонких специальных эффектов в игре.
Например, с помощью поверхностей можно «захватывать» экземпляры объектов, которые затем уничтожаются, создавая эффект декали: спрайт объекта остаётся на поверхности, как будто он всё ещё существует. Это позволяет добиться графических эффектов (обломки, кровь и т.д.) без значительной нагрузки на обработку.
Также поверхности можно применять в качестве текстур для дальнейшей обработки, создавать спрайты «на лету» или сложные наложения. По сути, возможности их использования безграничны!
Основное применение поверхности выглядит следующим образом:
-
сначала создаём переменную для хранения поверхности, например, в событии создания:
surf = -1;
-
в событии отрисовки (или любом другом событии) до того, как захотите нарисовать что-либо на поверхности, проверьте, существует ли она, и если нет — создайте её:
if (!surface_exists(surf)) { surf = surface_create(960, 540); }
-
если поверхность автоматически удаляется из памяти в нужный момент, этот код возмёт ситуацию в свои руки и воссоздаст её;
-
затем, когда будет необходимо отрисовать что-либо, установите поверхность как цель отрисовки (вместо дисплея), например, в событии отрисовки:
surface_set_target(surf);
-
далее выполните отрисовку нужных объектов и произведите необходимые манипуляции. Рекомендуется очистить поверхность перед началом отрисовки:
draw_clear_alpha(c_black, 0); draw_sprite(spr_icon, 0, 48, 48);
-
после завершения отрисовки на поверхности сбросьте цель отрисовки, чтобы последующая отрисовка происходила на экране:
surface_reset_target();
-
в конце отобразите поверхность (или используйте её в шейдере, или любым иным требуемым способом):
draw_surface(surf, 0, 0);
-
когда поверхность больше не нужна, освободите её из памяти:
surface_free(surf);
Обычные поверхности достаточно просты в использовании, однако есть несколько основных правил, которые необходимо соблюдать:
- во-первых, следует понимать, что поверхности (за исключением поверхности приложения) являются летучими. Это означает, что если устройство или окно теряют фокус или сворачиваются (например, при переключении окон с помощью Alt + Tab в Windows или при потере фокуса приложения на Android из-за входящего звонка), поверхность может быть уничтожена. Это происходит, поскольку она хранится только в памяти текстур (VRAM) и может быть перезаписана, когда системе потребуется эта память для других задач. Поэтому обязательно реализуйте резервный код, обычно с использованием функции
surface_exists()
;
Примечание: это, похоже, не происходит со спрайтами или другими визуальными элементами (хотя, на самом деле, происходит!), так как они тоже хранятся в обычной памяти (ОЗУ), и когда их удаляют из памяти текстур (VRAM), они мгновенно восстанавливаются из обычной памяти, когда игра снова получает фокус.
-
во-вторых, имейте в виду, что поверхности могут занимать значительный объём VRAM, поэтому старайтесь делать их как можно меньше. Старайтесь не превышать размер вида или окна отображения;
-
в-третьих, создавайте поверхности только в событии отрисовки. Если создать поверхность в событии создания экземпляра, она может получить тот же индекс, что и
application_surface
, что приведёт к множеству проблем и путанице: вы можете думать, что используете свою поверхность, а на самом деле используете текущую цель рендеринга. Также старайтесь рисовать на поверхности исключительно в событии отрисовки, поскольку GameMaker оптимизирует этот процесс, и все функции отрисовки (включая очистку поверхности при её создании) должны вызываться именно в этом событии. Рисование на поверхности вне события отрисовки возможно и иногда необходимо для определённых эффектов, но этого стоит избегать; -
в-четвёртых, при ручной отрисовке на поверхности координаты всегда считаются от (0, 0). Это значит, что вам может потребоваться преобразовать абсолютные координаты в относительные для поверхности. Например, если у вас поверхность размером с камеру и вы хотите нарисовать на ней объект, видимый в камере, необходимо вычесть координаты вида камеры из абсолютных координат объекта, чтобы получить позицию относительно (0, 0). Пример кода:
if (view_current == 0) { surface_set_target(surf); with (obj_Effect) { var _vx = camera_get_view_x(view_camera[1]); var _vy = camera_get_view_y(view_camera[1]); draw_sprite(sprite_index, image_index, x - _vx, y - _vy); } surface_reset_target(); } else { draw_surface(surf, 0, 0); }
-
наконец, обратите внимание, что при рисовании на поверхность учитываются как цвет, так и альфа-компонента (прозрачность) каждого пикселя — как самой поверхности, так и наносимых объектов. Это может приводить к неожиданным результатам (например, рисование спрайта с прозрачностью 0,5 на поверхности с прозрачностью 0 даст итоговую прозрачность 0,25). Причины этого описаны в разделе «Руководство по использованию режимов смешивания».
Примечание: это правило не относится к поверхности приложения, только к пользовательским поверхностям.
Ещё один момент: если вам необходимо отрисовать всё содержимое экрана (включая тайлы, фоны и т. д.) на поверхность, можно использовать саму поверхность приложения (подробнее см. ниже) или назначить поверхность определённому порту просмотра, используя переменную view_surface_id[от 0 до 7]
— тогда всё, что отображается в этом порте просмотра, будет нарисовано на соответствующей поверхности.
Самое простое применение поверхностей — отрисовка спрайтов, текста, частиц и других визуальных элементов, у которых для каждого пикселя задаются цвет и, возможно, значение прозрачности (альфа-значение). По умолчанию поверхности также имеют буфер глубины и трафаретный буфер, которые можно использовать для создания самых разнообразных продвинутых графических эффектов.
Подробную информацию смотрите в разделе «Буфер глубины и трафаретный буфер».
Ниже приведён список функций для работы с поверхностями (эти функции предназначены для создания и управления поверхностями; для их отрисовки на экране используйте специальные функции отрисовки, перечислённые далее):
- surface_exists,
- surface_create,
- surface_create_ext,
- surface_resize,
- surface_set_target,
- surface_set_target_ext,
- surface_get_target,
- surface_get_target_depth,
- surface_get_target_ext,
- surface_reset_target,
- surface_copy,
- surface_copy_part,
- surface_depth_disable,
- surface_get_height,
- surface_get_width,
- surface_get_texture,
- surface_get_texture_depth,
- surface_get_depth_disable,
- surface_has_depth,
- surface_getpixel,
- surface_getpixel_ext,
- surface_get_format,
- surface_format_is_supported,
- surface_free,
- surface_save,
- surface_save_part.
Ниже приведены функции для отрисовки поверхностей:
- draw_surface,
- draw_surface_ext,
- draw_surface_part,
- draw_surface_part_ext,
- draw_surface_stretched,
- draw_surface_stretched_ext,
- draw_surface_tiled,
- draw_surface_tiled_ext,
- draw_surface_general.
Наконец, имеются две функции для сохранения и извлечения поверхностей в буферах:
Как уже упоминалось, GameMaker не отрисовывает большинство объектов напрямую на экран, а выводит их на поверхность приложения. Это по сути поверхность, аналогичная тем, которые вы можете создать самостоятельно с помощью функций работы с поверхностями, и соответственно её можно изменять, рисовать на ней, использовать в шейдерах и т. д. Практически всё, что можно сделать с пользовательской поверхностью, применимо и к поверхности приложения.
Примечание: единственное, что нельзя сделать с поверхностью приложения — освободить её из памяти. Она всегда существует, хотя её идентификатор может меняться.
При запуске игры данная поверхность создаётся при первом вызове события отрисовки в каждой новой комнате, то есть до этого момента ничего не отображается. Однако вы можете получить позицию поверхности приложения и изменить её размер в событии создания или любом другом событии, при этом не получая никаких ошибок, и использованные значения будут применяться при создании поверхности. Фактическая последовательность событий для создания и отрисовки поверхности приложения выглядит следующим образом:
- событие перед отрисовкой;
- —> создаётся поверхность приложения (если она ещё не существует) и устанавливается цель отрисовки;
- для каждого видимого порта просмотра (или, если порты просмотра не активны, один раз):
-
- событие начала отрисовки;
-
- событие отрисовки;
-
- событие конца отрисовки;
-
- —> здесь сбрасывается цель отрисовки поверхности приложения;
- событие после отрисовки;
- —> по умолчанию поверхность приложения отрисовывается в буфер экрана (вы можете отключить это с помощью функции
application_surface_draw_enable()
); - событие начала отрисовки слоя интерфейса;
- событие отрисовки слоя интерфейса;
- событие конца отрисовки слоя интерфейса.
Использование этой поверхности позволяет создавать потрясающие переходы с использованием шейдеров, оборачивать экран вокруг 3D-форм или просто масштабировать игру с низким разрешением до любого экрана… Возможности безграничны!
Чтобы получить доступ к этой поверхности, используйте встроенную глобальную переменную application_surface
, о которой поясняется на следующей странице:
Также имеются функции, предназначенные только для работы с поверхностью приложения:
- application_surface_enable,
- application_surface_is_enabled,
- application_get_position,
- application_surface_draw_enable.
- назад: «Отрисовка»;
- далее: «Освещение».