КсиТехнология-01 — система анимаций «движение-атака»

GameMaker

Слишком претенциозное название для способа анимировать объекты в Гейм Мейкере, я знаю. Однако, это первое серьёзное усовершенствование, которое я придумал в ГМ, и использование которого ни разу не видел в исходниках (и уверен, что мало кто использует этот способ в принципе).

Когда я в каком-то бородатом 2008 году программировал платформер (это был пресловутый Эсдировский Red Zone, который, конечно, так и не вышел), я встретился со следующей проблемой, или, точнее говоря, неудобством — у персонажа много спрайтов, и их нужно не просто переключать в зависимости от того, в воздухе он или нет, а — как в Мегаменах — в зависимости и от того, стреляет от в данный момент или нет, находится ли в рывке, прыжке, на лестнице, и прочее. Об этом я и поведаю под катом.

Ставьте лайк, если всегда гадали, какого же цвета становится Марио/Луиджи, когда берёт звёздочку.

У него таких проблем как раз нет. Он парень простой, 8-битный.

(у него проблемы с обилием перекрасок, что было решено КсиТехнологией-02)

А это Мегамен Икс 4, по клику — всё даже ещё не всё разнообразие спрайтов, переключение которых мне надо было запрограммировать. Там используются отдельные перемежающие кадры с подсветкой. Смысл их в том, что только первый кадр после выстрела спрайт сменяется на подсвеченный, чтобы сочно показать выстрел как произошедший, а сама пушка убирается только через кадров 5-10.

В Гейм Мейкере есть встроенные средства для анимирования спрайтов, которые работают через следующие переменные:

  • sprite_index — индекс спрайта, который отображается в координатах объекта (в случае если у него нет своего собственного события Draw, а при создании его и нету).
  • image_index — индекс кадра из текущего спрайта. Так как один спрайт состоит из нескольких кадров, из них каждый раз выбирается кадр для отображения в текущий момент (шаг) времени.
  • image_speed — регулирует скорость анимации, то есть банально на каждом шаге игры происходит image_index+=image_speed. Учитывая, что количество кадров в секунду у меня обычно 50, то приемлемый image_speed будет примерно 0.2, что значит 10 смен кадров анимации в секунду.
  • image_xscale — масштабирование спрайта по горизонтали. Это важно, потому что если не знать об этой переменной, то можно заморочиться с загрузкой абсолютно идентичных спрайтов, только отражённых по горизонтали. А потом же их ещё надо подцепить к программе, и есть важная деталь — они должны быть отцентрированы по горизонтали. Ещё есть image_yscale, но обычно персонажи по потолку не ходят, а для поворота всей сцены сразу есть view_angle, который я уместно использовал ровно один раз за почти 10 лет — в переходе между фазами скролл-шутера и платформера в EX-802.

Первое время я вообще программировал какую-то хрень вроде

if walking && shooting==0 || !jump
    if dashing
        if face==left
            sprite_index=s_walk_left

Реконструкция вольная, на историчность не претендую. Но — определённо похожая.

А потом я придумал — я могу переключать спрайты по массиву, в который предварительно запишу все используемые индексы спрайтов. И так возникла система, которую я простецки назвал «движение-атака», потому что именно эти два «измерения» были моей целью.

Составляется эта штука в два этапа. Первый — это перечисление самих состояний по двум категориям:

НомерДвижениеАтака
0СтояниеВыстрел
1ПрыжокМощный выстрел
2Дэш (рывок)Удар лучевой саблей
3Скольжение по стенеГига-Атака
4СидениеНова Страйк
5Лазание по лестницеНет атаки
6Повреждение 
7Телепортация 
8Переход в дэш 
9Переход в лазание 
10Переход в скольжение 

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

Второй этап — собственно составление индексов в 2-мерный массив. Берём всё из первой таблицы и расставляем по осям.

 Нет атакиВыстрелМощный выстрелУдар лучевой саблейГига-АтакаНова Страйк
Стояние      
Прыжок      
Дэш      
Слайд      
Сидение      
Лазание      
Повреждение      
Телепорт      
...      

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

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

Ну а теперь - при чём тут вообще Гейм Мейкер.

for (i=0 i<всего_состояний_движения i+=1)
for (j=0 j<всего_состояний_атаки j+=1)
    массив_спрайтов[i, j] = индекс_спрайта

Это просто инициализация «никакими спрайтами», чтобы не возникало ошибки при обращении в какое-то место массива спрайтов, которое мы не инициализировали. Потом следует задать конкретно всю таблицу, составленную по методу, описанному выше. Просто прописать кучу строк типа массив_спрайтов[с_лазание, с_выстрел] = %тут внутрисистемное название соответствующего спрайта%.

И, наконец, самое главное.

Переключение происходит не через if walking && shooting==0 || !jump для кучи состояний, а просто...

sprite_index=массив_спрайтов[текущий_статус_движения, текущий_статус_атаки]

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

Model-View-Controller на коленке, в каком-то смысле.

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

Похожие статьи

38 комментариев
buntarsky
Не понял причем тут MVC, и чем описанный вариант отличается от конечного автомата. Есть состояние, есть переходы, а как оно реализовано — дело десятое.
Xitilon

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

Во-вторых я имел в виду использовать отдельный конечный автомат для игровой логики, а не для прорисовки. «Автомат» в посте, если и есть, хранит переменные, которые зависят от переменных внутри игровой логики.

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

Raseri

о_0

Ещё одна особенность языка Lua в том, что такие вещи для него совершенно естественны и приходят в голову в первую очередь :D
Потому что там ВСЁ — массивы, для удобной работы с ними язык и был создан :D
Ты писал это как Луашник, Ксит :D

Xitilon

Щта? Так ведь конкретный спрайт в конкретный момент всё равно один. А для остальных спрайтов - тут на любом языке нужны массивы, не?

Массивы в ГМ, конечно, оставляют желать лучшего даже в Студии. Но они вполне рабочие.

DarkDes
Т.е. каждое действие — отдельный спрайт? А как быть с «один спрайт на весь объект»? Ну т.е. вот как у меня в Земе было (где-то это подсмотрел) — один спрайт, а я храню начало, конец, шаг и ещё какую-то штуку для анимации. По идее твоя таблица останется, но элемент уже будет сложной структурой. Метод с «начало-конец» также требует конкретного назначения image_index, что обычно можно делать только при смене состояния (значит нужно мониторить).
Xitilon

Каждое пересечение двух состояний — отдельный спрайт (анимированный). Так тут и используется один спрайт на весь объект! В один момент времени — один спрайт, именно он и меняется тут:

sprite_index=массив_спрайтов[текущий_статус_движения, текущий_статус_атаки]

храню начало, конец, шаг и ещё какую-то штуку для анимации. По идее твоя таблица останется, но элемент уже будет сложной структурой.

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

DarkDes

Вот именно! В смысле нет.

В текущий момент времени — да, всего один спрайт, но я имею ввиду именно один спрайт вообще. Т.к 1 спрайт == 1 перс. Все анимации в одном спрайте. Это кстати намного проще будет для скинов. Так в Земе и сделал — просто меняешь одну переменную (спрайта) и всё. Короче у меня проще и руче — не спорь!!11

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

Xitilon
Это проще, если ты однозначно определяешь, сколько кадров в какой анимации. Как только тебе нужно добавить кадр в анимацию бега — всё, скины пропали. А у тебя разве двухмерный массив? Ты ж сказал просто одна переменная.
DarkDes

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

Может и полетят, а может и нет. Ведь Begin\End задаются где-то (в базе данных анимаций) к каждому спрайту-скину. Например, у одного перса бег на 10 кадров, а у дрогого всего на 5.

Да, у меня сейчас переменные только. Но по идее их можно завернуть в массив. Собственно сейчас в ГМ всё через массив делаю (не который ds_*, а который через []).

Xitilon

Так зачем твой метод, если он сложнее? Тут всё и так хорошо составлено.

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

ds_* это списки, сетки, стэки, очереди и прочее, массивов там нет.

DarkDes

Нет. В твоём методе надо для каждого «стейта» отдельный спрайт создавать — вот это много возни. По крайней мере мне такое не нравится, когда в проекте 100500 спрайтов для одного перса, например.

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

Считай это твоя же матрица + ещё массив для (begin, end, loop, etc) + аналогичные переменные, вместо одного sprite_index. Минусы есть, но это плата за скины и более красивой структуре контента — когда нет тысячи спрайтов для одного перса.

В 3Д вообще проще с этим (отчасти) — просто блендишь анимацию стрельбы с анимацией действия (бега, например) и всё. Вообще в «скелетном 2Д» аналогично  можно.

 

Xitilon

Знаешь, если бы игры работали на том, нравится разработчику или нет, как они сделаны изнутри… У тысяч людей не было бы ни одной игры. Так я делаю их деревом. Стоящие спрайты — одна папка, сидящие — другая. Или например стреляющие — одна, нестреляющие — другая.

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

Нет, тут кто-то кого-то не понял. Я говорю о ситуации если нужно добавить кадры в исходный «скин», раздвинув границы. Тогда нужно переделывать все спрайты. А у меня эти границы автоматически соблюдаются со стороны ГМа, и всё.

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

Да, про векторные анимации верно. Но тут — только пиксели, только хардкор!

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

Т.е. ты имеешь ввиду изменение данных границ спрайта? Похоже действительно кто-то кого-то не понял. Но похожу да — такая штука плохая идея.

Короче ты победил

Всё равно ГМ (не знаю как 8 и младше) пихает спрайты (каждый кадр) в атласы текстурные.

И как это там матрицы нет? Просто она 3х мерная же! Считай твоя система + массив для значений границ и всё. Чуть сложнее, но всё же.


ПыСы: Кто-нибудь реально использовал столько спрайтов\анимаций в своей игре? Ну т.е. собственно-нарисованных, а не рипнутых из другой игры?
Xitilon
«невидимыми» функциями

Вся суть, ага. Я написал их уже слишком много за свой геймдев, надо, что называется, get real.

Всё равно ГМ (не знаю как 8 и младше) пихает спрайты (каждый кадр) в атласы текстурные.

Не, ну это уже оптимизация для машины, а я говорю про для человека. Ранее Студии, то есть абсолютно все версии оригинального ГМ с первой по 8.1 ни о каких атласах ничего не знают. Последние версии разве что пакуют отрендеренные каждым используемым в проекте шрифтом символы (конкретный интервал причём, чтоб лишнего не хватать) в атлас, но и это происходит вдали от разработчика и не нуждается в его вмешательстве, как ты наверняка заметил.

Трёхмерная матрица называется тензор. Либо вектор матриц. Либо вектор векторов векторов.

Да. Не поверишь, но наш с Эсдиром Ред Зоун использовал (не знаю, сколько тут «столько», но порядка 12 спрайтов там есть). Я ж для чего систему-то делал?! Хочешь поглядеть?

DarkDes

Да, про атласы это так — «вне зрения человекоФ». Вообще штука с управлением \ видом контента очень важна для движков. Мне вот даже в Юнити оно не особо нравится, хотя соглашусь, что в некотором смысле удобно там, но ещё нет

Тензор… что-то знакомое… физика… тогда ясно

РедЗоун? Ну давай глянем. Прямо таки очень много анимаций? Зачем столько? Вообще можно делать анимацию как в Орках Поземелья — по одному кадру (а на самом деле нет)

Xitilon

Потому что разнообразие — красиво же.

Ну давай считать:

  • Стояние на месте (да, это тоже анимация)
  • Бег
  • Остановка (переход из бега в стояние)
  • Прыжок/падение
  • Повреждение/отбрасывание
  • Удар мечом
  • Второй удар мечом
  • Удар мечом вокруг себя
  • Выстрел стоя вперёд
  • Выстрел в прыжке вперёд
  • Выстрел в прыжке назад
  • Выстрел в прыжке по диагонали
  • Выстрел в прыжке по другой диагонали

Вообще, можно подумать, что это игра о стрелянии в воздухе, но это не так… это вообще не игра, да и прототип находится в архиве, до которого я всё ещё не дошёл — дело было где-то в 2010 году.

DarkDes

Стояние на месте (да, это тоже анимация)

Спору нет, но обычно кадра два у меня Хотя думаю, что сюда можно записать «долгое бездействие» — очень нравилась мне такая штука в играх, когда стоишь, чего-то ждёшь, а персонаж начал что-то своё делать, например, Соник такой стоит ждёт игрока с выражением лица типа «Ну чЁ? Где ты там?».

  • Выстрел стоя вперёд
  • Выстрел в прыжке вперёд
  • Выстрел в прыжке назад

Ээээй, а где выстрел назад?

Окей, много анимаций. А вообще нужны просто цветные кубики, говорят прокатывает нормально

Xitilon
очень нравилась мне такая штука в играх, когда стоишь, чего-то ждёшь, а персонаж начал что-то своё делать, например, Соник такой стоит ждёт игрока с выражением лица типа «Ну чЁ? Где ты там?».

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

А выстрела назад с места нету — ведь можно просто обернуться.

Какие кубики?

DarkDes

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

А выстрела назад с места нету — ведь можно просто обернуться.

А если бежишь вправо и нужно резко влево пострелять (убегать от босса, например)? Такого не было? Это на будущее, когда будешь релизить РедЗоне

Кубики… ну типа минимализм — один кубик — это персонаж, очень красивый такой, просто Щедевр графики, а другой кубик — это супер-враг… ну ты понял короче, типа прототип, типа графика на высоте

Xitilon

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

Не, не было. На самом деле это всё Эсдир придумал, а выстрела назад не придумал. Кстати, стрелять в беге вообще нельзя, даже вперёд. Типа отдача очень сильная — за счёт этого можно подлетать в воздухе, поэтому там можно стрелять во столько сторон.

когда будешь релизить РедЗоне

Ай, размечтался. Этот проект мутировал потом в другой проект — Let's Destroy The World, спин-оффом которого является True Heart, сделанный на Гаминатор 9 (в 2012 году). А до того этот проект вообще назывался Rage, начатый в 2007 (6? 5?!). Конца-края этой разработке не видно. Да увидишь потом, я пост про это всё напишу. Он давно в черновиках уже висит, и соответствующие материалы готовятся.

А, ты про ПИКСЕЛИ наверное. Тогда это понятно!

Eldar

Действительно очень полезная идея. Я раньше задумывался только об одномерной такой штуке. Вроде, в коде события атаки дописывать

player.sprite = player.sprites.punch (это на LOVE, конечно)

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

Xitilon
Тут оно конкретно двухмерное, потому что этого достаточно для двух часто используемых функций. Если персонаж и не атакует, то он может совершать какое-то действие помимо перемещения, но в том же время перемещаться он может несколькими способами. В этом суть.
Eldar

Я теперь задумался о необходимости двумерности. Получается, что происходит что-то вроде такого.

При изменении атаки:

sprite_index=массив_спрайтов[текущий_статус_движения, новый_статус_атаки]

А при изменении статуса движения

sprite_index=массив_спрайтов[новый_статус_движения, текущий_статус_атаки]

Если так, то да. Это удобнее одномерной штуки, в которой получается только

текущая_анимация = новая_анимация

По сути, если в игре есть много разных видов состояний, то эту штуку можно же расширить ещё дальше… Что-то вроде анимации атаки в Транзисторе — «активная_способность + улучшение_1 + улучшение_2 + пассивная_способность_1 + ...» Но тогда либо в дверь стучит комбинаторика и говорит охладить пыл, либо надо только учесть теоретически возможные комбинации состояний. Либо заполнить некоторые ячейки одинаковыми анимациями.

В любом случае чем больше я думаю об этой идее многомерной организации анимации, тем больше она мне нравится

DarkDes
Мне больше всего жаль аниматора, который всё это рисовать будет
Xitilon
Ну «активная_способность + улучшение_1 + улучшение_2 + пассивная_способность_1 + ...» это реально слишком много. Я думаю, имеет смысл рисовать всякие спецэффекты уже поверх спрайта игрока, дополнительными функциями, если само движение персонажа при этом не отличается. Не всё же одним куском рисовать. Но если есть человек, который столько согласен рисовать вручную, то хозяин — барин.
DarkDes
Немного не в тему, но… была однажды идея для игры, дак там всё надо было руками рисовать (не пиксель арт и даже не вектор) — просто прикинул сколько это всего (анимаций всего лишь 5 где-то) — это жуть. Поэтому да, спецэффекты — простой способ, но иногда прямо хочется именно «шоб красиво было» т.е. плавные переходы или вроде того. Можно на это потратиться, если речь о файтингах каких-нибудь, но вот в платформмере с N персонажами
Xitilon
Комбинаторный взрыв получается. Шоб красиво — это нужен отдельный выделенный аниматор со стажем. Или хотя бы с пониманием объёма и качества работ. Ну, в файтинге это ведь самая главная графика. И трёхмерные модели вряд ли делать сильно проще. Хотя анимировать переходы циклических анимаций проще, как мы уже выше упомянули.
DarkDes
Не, я просто про анимацию, а не набор всяких спецэффектов.

Хотел я как-то стать аниматором… в смысле попробовать «что это есть», но только так сказать «труЪ» — мульт-анимацию запилить думал, а не просто пиксель-арт. В итоге ничего конечно. Но есть персонажи, небольшая идея для игры, комикса — короче как всегда

У моделей однотипных есть огромный плюс — анимацию можно использовать и к другим моделям. Но если речь о уникальных персах и анимациях, то тогда да — разницы мало.
Xitilon

При изменении атаки:
sprite_index=массив_спрайтов[текущий_статус_движения, новый_статус_атаки]
А при изменении статуса движения
sprite_index=массив_спрайтов[новый_статус_движения, текущий_статус_атаки]

Дело в том, что эти статусы могут измениться и одновременно тоже. Моя система это полностью поддерживает.

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

Именно об этом я писал вот в этом месте:

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

Если говорить прям в общем и целом, глобально и абстрактно, то можно свести все эти индексы в один. Рассмотрим, скажем, 8-битное число в таком виде по битам:

R J Dmg Sh Sl GA NS T

Каждый бит будет соответствовать состоянию (да/нет) для: бега, прыжка, повреждения, выстрела, взмаха лучевой саблей, Гига-Атакой, Нова Страйка и телепортации. Осталось только пронумеровать все спрайты уже в одномерном массиве. И написать функцию/скрипт, которая принимает на входе эти восемь бит, и возвращает на выходе индекс для спрайта.

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

Мурка
Проблематика тут не столько в том, чтобы отрисовать спрайты, а в том, чтобы увидеть многомерную систему там, где она есть.
Xitilon
Она тут всего-то двухмерная, и я вроде всё пояснил о том, как это складывается. Ну ты-то понял?
Hrenzerg

А что делать если у меня помимо этого имеются варианты поворота вправо и влево? Можно сказать «А image_xscale тебе на что?», на что я аргументированно возражу «Он ломает освещение спрайта, если на нём есть тень».

Я использую похожую штуку, только у меня не атака, а поворот влево-вправо.

Xitilon

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

for (i=0 i<всего_состояний_движения i+=1)
for (j=0 j<всего_состояний_атаки j+=1)
    массив_спрайтов[i, j] = индекс_спрайта
    массив_спрайтов_теней[i, j] = индекс_спрайта_теней

Хотя стой. Тень в смысле кастомное освещение? Проклятье, я не так понял! Ну и естественно в отдельный спрайт оно не выделяется (хотя можно, но это трудоёмко).

Это аналогично проблеме лево-праворукости. Типа, Мегамен стреляет левой рукой когда идёт вправо, но правой когда влево, а ты хочешь чтобы всегда одной, и делаешь два набора спрайтов соответственно.

У меня есть только одно решение — сплющить состояние движения и состояние атаки из двух чисел в одно число.

Если состояний у тебя (например) не более чем 16, то можно выкинуть всё что дальше первых 4 бит, следовательно:

if статус_движения<=16
    код_статуса=статус_движения mod 16
else
    какой-то неправильный статус, надо решить что делать

код_статуса=код_статуса + статус_атаки * 16

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

спрайт[код_статуса, сторона]

pevzi
Вспомнил вдруг Neverhood, в котором у Клеймена пальцы чёрные то на правой руке, то на левой
Xitilon

Блин, и там тоже схалтурили?!

Хотя о чём я. В Armikrog у Томминавта две полоски то на одной руке, то на другой. Индустрия в плане таких деталей очень уж инертна.

 

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

Xitilon

А, ну и как же я забыл про это!

Hrenzerg
Именно потому что я забиваю свою голову такими мелочами как «лампочка на колене правой ноги ДОЛЖНА БЫТЬ ВСЕГДА НА МЕСТЕ» — у меня получаются игры на определённо новом уровне, с подробнейшей детализацией.
Xitilon
Ну, не знаю, как новом. Но отчётливо высоком.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.