Перейти к содержимому

Как сделать анимацию спрайта

  • автор:

Модульные спрайтовые персонажи и их анимация

Эта запись девлога целиком посвящена моей системе анимации персонажей, она наполнена полезными советами и фрагментами кода.

За последние два месяца я создал целых 9 новых действий игрока (такие забавные вещи как блокировка щитом, уворачивание в прыжке и оружие), 17 новых носимых предметов, 3 набора брони (пластинчатый, шёлковый и кожаный) и 6 видов причёсок. Также я завершил создавать всю автоматизацию и инструменты, поэтому всё уже используется в игре. В статье я расскажу, как этого добился!

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

Краткое описание

Изначально я хотел проверить, можно ли совместить наложенные друг на друга спрайты с синхронизованными аниматорами для создания модульного персонажа с заменяемыми причёсками, снаряжением и носимыми предметами. Можно ли объединить нарисованную вручную пиксельную анимацию с по-настоящему настраиваемым персонажем.

Разумеется, такие функции активно используются в 3D- и 2D-играх с пререндеренными спрайтами или в 2D-играх со скелетной анимацией, но насколько я знаю, существует не так много игр, совмещающих созданную вручную анимацию и модульных персонажей (обычно потому, что процесс оказывается слишком монотонным).

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

Я создал прототип при помощи системы анимаций Unity, а затем для проверки концепции добавил одну рубашку, одну пару штанов, одну причёску и три предмета. Для этого потребовалось 26 отдельных анимаций.

В то время я создавал всю свою анимацию в Photoshop и не заморачивался автоматизацией процесса, поэтому он был очень скучным. Потом я подумал: «Так, основная идея сработала, позже я добавлю новые анимации и снаряжение». Оказалось, что «потом» — это несколько лет спустя.

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

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

Спойлер: всё получилось замечательно. Ниже я раскрою свои ***секреты***

Модульная система спрайтов

I. Познай свои границы

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

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

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

Аспект отслеживания времени оказался очень важным, и я крайне рекомендую использовать его, чтобы отвечать на вопросы типа: «Сколько врагов могу я позволить себе создать в игре?». Всего после нескольких тестов мне удалось экстраполировать достаточно точную оценку. При дальнейшей работе над анимациями я продолжил следить за временем и пересматривать мои ожидания.

Поделюсь копией моего журнала работы за последние два месяца. Учтите, что это время идёт в добавок к моей обычной работе, где я провожу по 30 часов в неделю:

II. Смена палитры ради светлого будущего

С умом используя цвета в дизайне спрайтов, можно отрисовать один спрайт и создать множество различных вариаций при помощи смены палитры. Можно менять не только цвета, но и создавать различные включаемые и отключаемые элементы (например, заменой цветов на прозрачность).

Каждый набор брони имеет 3 вариации, а смешивая верхние и нижние части, можно получить множество комбинаций. Я планирую реализовать систему, в которой можно собрать один набор брони для внешнего вида персонажа, а другой — для его характеристик (как в Terraria).

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

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

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

1. Текстура поиска для каждой палитры

Это наилучшая стратегия для создания вариаций врагов, фонов и всего того, где множество спрайтов иеет одинаковую палитру/материал. Различные материалы нельзя сгруппировать в батчи, даже если они используют одинаковый спрайт/атлас. Работа с текстурами довольно мучительна, но палитры можно изменять в реальном времени, заменяя материалы, с помощью SpriteRenderer.sharedMaterial.SetTexture или MaterialPropertyBlock, если вам нужны разные палитры для каждого экземпляра материала. Вот пример фрагментной функции шейдера:

sampler2D _MainTex; sampler2D _PaletteTex; float4 _PaletteTex_TexelSize; half4 frag(v2f input) : SV_TARGET < half4 lookup = tex2D(_MainTex, input.uv); half4 color = tex2D(_PaletteTex, half2(lookup.r * (_PaletteTex_TexelSize.x / 0.00390625f), 0.5)); color.a *= lookup.a; return color * input.color; >
2. Массив цветов

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

sampler2D _MainTex; half4 _Colors[32]; half4 frag(v2f input) : SV_TARGET

Я представил свои палитры как тип ScriptableObject и использовал для их редактирования инструмент MonoBehaviour. Проработав долгое время над редактированием палитр в процессе создания анимаций в Aseprite, я понял, какие инструменты мне требуются и писал эти скрипты соответствующим образом. Если вы хотите написать собственный инструмент для редактирования палитр, то вот какие функции я обязательно рекомендую реализовать:

— Обновление палитр на различных материалах при редактировании цветов для отображения изменений в реальном времени.

— Присваивание названий и изменение порядка цветов в палитре (используйте поле для хранения индекса цвета, а не его порядка в массиве).

— Выбор и редактирование нескольких цветов за раз. (Совет: поля Color в Unity можно копипастить: просто нажмите на один цвет, скопируйте, нажмите на другой цвет, вставьте — теперь они одинаковы!)

— Применение цвета оверлея ко всей палитре

— Запись палитры в текстуру

3. Единая текстура поиска для всех палитр

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

Во-первых, нужно будет упаковать все палитры в одну большую текстуру. Затем вы используете цвет, заданный в компоненте SpriteRenderer (AKA цвет вершины) для определения строки, которую надо считать из текстуры палитры в шейдер. То есть палитра этого спрайта управляется через SpriteRenderer.color. Цвет вершины — это единственное свойство SpriteRenderer, которое можно менять без нарушения батчинга (при условии, что все материалы одинаковы).

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

sampler2D _MainTex; sampler2D _PaletteTex; float4 _PaletteTex_TexelSize; half4 frag(v2f input) : SV_TARGET < half4 lookup = tex2D(_MainTex, input.uv); half2 paletteUV = half2( lookup.r * _(PaletteTex_TexelSize.x / 0.00390625f), input.color.a * _(PaletteTex_TexelSize.y / 0.00390625f) ) half4 color = tex2D(_PaletteTex, paletteUV); color.a *= lookup.a; color.rgb *= input.color.rgb; return color; >

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

III. Автоматизируйте всё и применяйте подходящие инструменты

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

Первым моим шагом стало создание экспортёра для Aseprite, чтобы управлять моей безумной схемой слоёв спрайтов при помощи удобного интерфейса командной строки. Это просто скрипт на perl, который обходит все слои и метки в моём файле Aseprite и эспортирует изображения в определённой структуре каталогов и имён, чтобы я смог в дальнейшем их считывать.

Затем я написал импортёр для Unity. Aseprite выводит удобный файл JSON с данными кадров, поэтому можно создавать ассеты анимаций программно. Обработка Aseprite JSON и запись этого типа данных оказались довольно нудными, поэтому я привожу их здесь. Вы можете с лёгкостью загрузить их в Unity с помощью JsonUtility.FromJson, только не забудьте запустить Aseprite с опцией —format ‘json-array’.

[System.Serializable] public struct AespriteData < [System.Serializable] public struct Size < public int w; public int h; >[System.Serializable] public struct Position < public int x; public int y; public int w; public int h; >[System.Serializable] public struct Frame < public string filename; public Position frame; public bool rotated; public bool trimmed; public Position spriteSourceSize; public Size sourceSize; public int duration; >[System.Serializable] public struct Metadata < public string app; public string version; public string format; public Size size; public string scale; >public Frame[] frames; public Metadata meta; >

На стороне Unity серьёзные проблемы у меня возникли в двух местах: в загрузке/нарезке спрайтшита и в построении клипа анимации. Мне бы очень помог понятный пример, поэтому вот фрагмент кода из моего импортёра, чтобы вы не так мучились:

TextureImporter textureImporter = AssetImporter.GetAtPath(spritePath) as TextureImporter; textureImporter.spriteImportMode = SpriteImportMode.Multiple; SpriteMetaData[] spriteMetaData = new SpriteMetaData[aespriteData.frames.Length]; // Slice the spritesheet according to the aesprite data. for (int i = 0; i < aespriteData.frames.Length; i++) < AespriteData.Position spritePosition = aespriteData.frames[i].frame; spriteMetaData[i].name = aespriteData.frames[i].filename; spriteMetaData[i].rect = new Rect(spritePosition.x, spritePosition.y, spritePosition.w, spritePosition.h); spriteMetaData[i].alignment = (int)SpriteAlignment.Custom; // Same as "Pivot" in Sprite Editor. spriteMetaData[i].pivot = new Vector2(0.5f, 0f); // Same as "Custom Pivot" in Sprite Editor. Ignored if alignment isn't "Custom". >textureImporter.spritesheet = spriteMetaData; AssetDatabase.ImportAsset(spritePath, ImportAssetOptions.ForceUpdate); Object[] assets = AssetDatabase.LoadAllAssetsAtPath(spritePath); // The first element in this array is actually a Texture2D (i.e. the sheet itself). for (int i = 1; i < assets.Length; i++) < sprites[i - 1] = assets[i] as Sprite; >// Create the animation. AnimationClip clip = new AnimationClip(); clip.frameRate = 40f; float frameLength = 1f / clip.frameRate; ObjectReferenceKeyframe[] keyframes = new ObjectReferenceKeyframe[aespriteData.frames.Length + 1]; // One extra keyframe is required at the end to express the last frame's duration. float time = 0f; for (int i = 0; i < keyframes.Length; i++) < bool lastFrame = i == keyframes.Length - 1; ObjectReferenceKeyframe keyframe = new ObjectReferenceKeyframe(); keyframe.value = sprites[lastFrame ? i - 1 : i]; keyframe.time = time - (lastFrame ? frameLength : 0f); keyframes[i] = keyframe; time += lastFrame ? 0f : aespriteData.frames[i].duration / 1000f; >EditorCurveBinding binding = new EditorCurveBinding(); binding.type = typeof(SpriteRenderer); binding.path = ""; binding.propertyName = "m_Sprite"; AnimationUtility.SetObjectReferenceCurve(clip, binding, keyframes); AssetDatabase.CreateAsset(clip, "Assets/Animation/" + name + ".anim"); AssetDatabase.SaveAssets();

Если вы этого ещё не делали, то поверьте — начать создавать собственные инструменты очень легко. Самый простой трюк заключается в размещении в сцене GameObject с привязанным к нему MonoBehaviour, которое имеет атрибут [ExecuteInEditMode]. Добавьте кнопку, и вы готовы к бою! Помните, что ваши личные инструменты не обязаны выглядеть хорошо, они могут быть чисто утилитарными.

[ExecuteInEditMode] public class MyCoolTool : MonoBehaviour < public bool button; void Update() < if (button) < button = false; DoThing(); >> >

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

string path = "Assets/Whatever/Sprite.png"; Texture2D texture = AssetDatabase.LoadAssetAtPath(path); TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; if (!textureImporter.isReadable) < textureImporter.isReadable = true; AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); >Color[] pixels = texture.GetPixels(0, 0, texture.width, texture.height); for (int i = 0; i < pixels.Length; i++) < // Do something with the pixels, e.g. replace one color with another. >texture.SetPixels(pixels); texture.Apply(); textureImporter.isReadable = false; // Make sure textures are marked as un-readable when you're done. There's a performance cost to using readable textures in your project that you should avoid unless you plan to change a sprite at runtime. byte[] bytes = ImageConversion.EncodeToPNG(texture); File.WriteAllBytes(Application.dataPath + path.Substring(6), bytes); AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
Как я перерос возможности Mecanim: жалоба

Со временем прототип системы модульных спрайтов, который я создал с помощью Mecanim, стал самой большой проблемой при апгрейде Unity, потому что API постоянно сильно менялся и был плохо задокументирован. В случае простого конечного автомата было бы разумно иметь возможность запрашивать состояния каждого клипа или менять клипы во время выполнения. Но нет! Из соображений производительности Unity запекает клипы в их состояния и заставляет нас использовать для их смены неуклюжую систему переопределений.

Сам по себе Mecanim не такая уж плохая система, но мне кажется, что ему не удаётся реализовать свою основную заявленную особенность — простоту. Идея разработчиков заключалась в том, чтобы заменить то, что казалось сложными и мучительным (скриптинг) чем-то простым (визуальным конечным автоматом). Однако:

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

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

— Забавно, что в результате вам всё равно приходится писать код, ведь чтобы конечный автомат делал что-то интересное, нужен скрипт, вызывающий Animator.SetBool и подобные ему методы.

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

— Если вы хотите изменять то, что находится в состоянии во время выполнения, то у вас проблемы. Решением будет или плохой API, или безумный граф с одним узлом для каждой возможной анимации.

Рассказ о том, как разработчики Firewatch попали в ад визуального скриптинга. Самое забавное в том, что когда докладчик показывает наиболее простые примеры, они всё равно выглядят безумно. Зрители в буквальном смысле стонут на 12:41. Добавьте огромные затраты на обслуживание, и вы поймёте, почему я сильно не люблю эту систему.

Многие из этих проблем даже не вина разработчиков Mecanim, а просто естественный результат несовместимых идей: нельзя создать общую и в то же время простую систему, а описывать логику при помощи изображений сложнее, чем просто словами/символами (кто-нибудь помнит флоучарты UML?). Я вспомнил фрагмент из доклада Зака Макклендона на Practice NYC 2018, и, если найдётся время, рекомендую вам посмотреть видео целиком!

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

Если вы уже немного программист и делаете игры со спрайтами, то вам возможно стоит подумать дважды. Когда я начинал, то меня был уверен, что никогда не смогу написать что-то связанное с движком лучше, чем разработчики Unity.

И знаете что? Оказалось, что аниматор спрайтов — это просто скрипт, меняющий спрайт через заданное количество секунд. Как бы то ни было, мне всё-таки пришлось написать собственный. С тех пор я добавил ещё события анимации и другие функции под мой конкретный проект, но базовая версия, которую я написал за полдня, покрывает 90% моих потребностей. Она состоит всего из 120 строк и её можно бесплатно скачать отсюда: https://pastebin.com/m9Lfmd94. Благодарю за то, что прочитали мою статью. До встречи!

  • процедурная генерация
  • спрайты
  • анимация
  • mecanim
  • процедурная генерация персонажей

Спрайтовая Анимация

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

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

Когда компьютеры были большими, спрайты были совсем крошечными, размером 8 на 8 пикселей и могли быть раскрашены в 4-8 цветов. Самым известным героем первых спрайтовых игр был всеми любимый Марио. Из-за крохотных размеров спрайта нарисовать этому персонажу рот было невозможно: так появились характерные усы Марио, герой стал узнаваемым.

С увеличением мощности компьютеров спрайты росли, становились полноцветными, всё более похожими на настоящих персонажей из мультфильмов. Пик распространения спрайтовой анимации пришёлся на девяностые годы: как на компьютерах, так и на игровых приставках тогда расцвёл жанр «файтингов», где каждый мог почувствовать себя Брюсом Ли. Способствовал этому популярный тогда жанр «боевиков» в кинематографе. Кто из нас не играл в Mortal Kombat, по мотивам которого был даже снят фильм! Такие «файтинги» были полностью основаны на спрайтах.

На рубеже тысячелетий традиционная анимация начала уступать место 3D-анимации. То же самое происходило и в мире компьютерных игр — спрайтовые персонажи стали вытесняться полигональными. Однако спрайтовая анимация ещё не собирается уходить со сцены. Она продолжает широко использоваться в оформлении (вспомните «помощников» из более ранних версий Office), в обучающих программах, а также в играх — главным образом, для мобильных устройств. Кроме того, в наши дни спрайты используются во Flash и gif-анимации, только хранятся не в виде битмэпа, а в формате многослойного графического файла, где каждый кадр анимации расположен на отдельном слое. В Adobe After Effects можно поместить в сцену анимацию, созданную из последовательности графических файлов. Строго говоря, это тоже спрайтовая анимация, только источником спрайтов является не многослойная картинка или битмэп, а отдельная папка на компьютере. Так что спрайты, как волшебные феи, всегда готовы прийти на выручку аниматору, особенно при создании игр!

Спрайты из игры Angry Birds(2009 г.)

Несколько способов создания красивой анимации спрайта в Scratch

Творческое программирование для всех

Александр Леонидович Зубрицкий, учитель информатики и музыки из агрогородка Ломаши делится своим опытом создания красивой анимации спрайтов на основе файла формата .gif или даже из видео-записей мультфильмов.

1. Создаём анимацию спрайта из гифки

Многие школьники знают и любят так называемые «гифки» (.gif-файлы, кликните по картинке слева и посмотрите). Особенно полезны гифки для тех, кто не очень умеет красиво рисовать. Важно только не забывать, что нельзя нарушать авторские права. Поэтому при использовании любых изображений, взятых из сети Интернет, надо убедиться, что их можно использовать, и обязательно сослаться на источник. Например, эту танцовщицу мы нашли с помощью поиска Google на сайте fi.pinterest.com.

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

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

2. Создаём анимацию на основе видео

В следующем уроке Александр Леонидович делает ещё более интересную анимацию.

В следующем уроке берём аудио-трек из любимого мультфильма и добавляем в свою анимацию Scratch.

Уроки созданы в рамках конкурса для учителей «Моя Scratch-история-2020» в номинации «Опыт мастера». Александр Леонидович Зубрицкий — финалист конкурса и докладчик конференции «Scratch conference Belarus 2020».

Информатика. 11 класс (Повышенный уровень)

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

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

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

Спрайты использовались при создании анимации в примерах 17.13 (луна) и 17.14 (бабочка). В этих примерах спрайт содержал фиксированное изображение. Современным подходом к применению спрайтов в анимации является использование раскадровок (spritesheet — атлас спрайтов) — графических файлов, содержащих несколько фаз движения какого-либо объекта (пример 17.15). Быстрая смена отдельных кадров позволяет создать иллюзию движения. Такую анимацию называют спрайтовой .

Удобно использовать раскадровки, в которых все кадры имеют одинаковую ширину (высоту).

Алгоритм создания анимации с использованием атласа спрайтов будет аналогичен алгоритму, рассмотренному в примере 17.5:

  1. Сохранить часть фона.
  2. Нарисовать первый кадр спрайта.
  3. Запустить таймер.
  4. Пока таймер работает:
    4.1. восстановить фон;
    4.2. рассчитать новое положение объекта;
    4.3. сохранить часть фона;
    4.4. перейти к следующему кадру;
    4.5. нарисовать кадр.

Пример 17.16. Создать проект, в котором бабочка будет летать вокруг цветка. Для полета бабочки использовать спрайт с раскадровкой.

Этапы выполнения задания

  1. Анимация запускается сразу при загрузке формы (без кнопки запуск).
  2. Если не учитывать необходимость смены кадров из атласа спрайтов, то задание аналогично примеру 17.13. Рассмотрим смену кадров.
  3. При каждом срабатывании таймера будем выделять новый кадр из раскадровки. Если горизонтальная координата текущего кадра стала больше, чем ширина атласа спрайтов, то выделение начинается сначала.
  4. Атлас спрайтов для бабочки состоит из 18 кадров, поэтому при каждом срабатывании таймера значение переменной t будем изменять на .

Слово «спрайт» было придумано в 1970-е годы сотрудником компании Texas Instruments: их новая микросхема TMS9918 могла аппаратно отображать небольшие движущиеся картинки поверх неподвижного фона.

Пример 17.15. Примеры раскадровок[1]:

[1] Изображения бесплатных раскадовок взяты с сайта https://ru.freepik.com/

Пример 17.16. Раскадровка бабочки (первые четыре кадра):

* kadr = new Graphics :: TBitmap (),

* spritesheet = new Graphics :: TBitmap (),

* fon = new Graphics :: TBitmap (),

* fon_kadr = new Graphics :: TBitmap ();

int w , h , x_fly , y_fly , x_k , y_k ;

int x_c = 150 , y_c = 120 , R = 120 ;

//количество кадров

int k = 18 ;

bool f = false ;

TRect pr_kadr , pr_fon , pr_fly ;

double t = 0 , pi ;

Обработчик события FormCreate для формы.

void __fastcall TForm1 ::

FormCreate ( TObject * Sender )

// загрузка изображений

fon -> LoadFromFile (» podsolnuh.bmp «);

Image1 -> Picture -> Assign ( fon );

spritesheet -> LoadFromFile (» fly.bmp «);

w = spritesheet -> Width / 18 ;

h = spritesheet -> Height ;

//определение битового образа

//для хранения кадра бабочки

kadr -> Height = h ;

kadr -> Transparent = true ;

pr_kadr = Bounds ( 0 , 0 , w , h );

//начальный кадр

kadr -> Canvas -> CopyRect ( pr_kadr ,

spritesheet -> Canvas , pr_fly );

//определение битового образа

//для хранения части фона

fon_kadr -> Width = w ;

fon_kadr -> Height = h ;

//начальное положение бабочки

//сохранение части фона под бабочкой

pr_fon = Bounds ( x_fly , y_fly , w , h );

fon_kadr -> Canvas -> CopyRect

( pr_kadr , fon -> Canvas , pr_fon );

// рисование бабочки

Image1 -> Canvas -> Draw

( x_fly , y_fly , kadr );

Обработчик события OnTimer для таймера:

void __fastcall TForm1 ::
Timer1Timer ( TObject * Sender )

// восстановление фона

Image1 -> Canvas -> Draw

( x_fly , y_fly , fon_kadr );

//новый кадр бабочки

if ( x_k > w * k )

pr_fly = Bounds ( x_k , y_k , w , h );

kadr -> Canvas -> CopyRect ( pr_kadr ,

spritesheet -> Canvas , pr _ fly );

//новое положение бабочки

if ( t > 2 * pi )

x_fly = x_c + ceil ( R * sin ( t ));

y_fly = y_c — ceil ( R * cos ( t ));

// сохранение части фона

pr_fon = Bounds ( x_fly , y_fly , w , h );

fon_kadr -> Canvas -> CopyRect

( pr_kadr , fon -> Canvas , pr_fon );

// рисование бабочки

Image1 -> Canvas -> Draw

( x_fly , y_fly , kadr );

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Luxury Car Rental in Miami | Reserve a Premium Vehicle with RealCar