Способы анимации замка "молнии"

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

Введение

Суть способа заключается в основной системе хелперов, которая задает нам последовательную зависимость элементов обеспечивающих сближение точек шва «молнии» от замка, который перемещается вперед и назад по этой «молнии». Проще говоря, при приближении замка, элементы должны последовательно закрываться. Именно эта система будет управлять всей механикой застежки молнии, и уже к этой системе мы будем привязывать работу нашей молнии, причем делать это можно настолько разными способами на сколько хватит фантазии. То есть в этой системе можно привязать и тканную динамику, и костную и морфинговую, и так далее. В данном случае, разберем два варианта – тканный и костный.

Основа механики «молнии»

Итак, приступим к построению управляющей системы. Для начала постройте линию, я, для ровного счета, сделаю ее высотой 10 клеток. Далее создайте Point, назначьте ему Path Controller и «посадите» на эту линию. Назовите его c_obj (названия имеют значение, потому что мы будем заводить их в Script Controller). Настройте параметры поинта, чтобы его лучше было видно в сцене.  Это будет замок молнии.

И далее, с шифтом тянем наш поинт за стрелочку вверх, копируем его. Пишем название zip_obj01. Это будет закрывающийся элемент.

Еще, нам понадобится слайдер, расскажу о нем позже, пока просто создайте его и разместите в удобном месте экрана. Еще один момент. Выделите слайдер, зайдите в Curve Editor, найдите там параметр Value и назначьте на него контроллер Bezier Float.

 

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

Выделяем поинт zip_obj01, идем в Curve Editor и на Scale поинта назначаем скрипт-контроллер. Это и есть тот самый алгоритм закрывания элемента при приближении замка.

В скрипт-контроллере первым делом заведем переменную this и привяжем ее к текущему поинту, эта переменная нам нужна для того, чтобы когда мы будем копировать этот поинт сколько угодно раз, нам не придется лезть в скрипт каждого поинта и править там имена поинтов. Для этого сделаем несколько действий: первое – пишем имя переменой, второе – создаем ее нажатием кнопки Create, третье – нажиаем Assign to Node и указываем наш zip_obj01.

А в окне скрипт-контроллера пишем следующее:

dependsOn $c_obj.pos.controller  --define reatime reaction denending on control object position
dependsOn $Slider01.value.controller --define reatime reaction denending on slider object value
pos=this.pos.controller.percent -- position of dummy on a path (0-100%)
range=$Slider01.value  -- limit of scale range (0-100%)
cobj=$c_obj -- control object
mult=(100/range) -- multiplier to correct output scale value
pr= cobj.pos.controller.percent
c=pos-range            
sc=100
x=100-((100-pr)+c) -- counter of percent inside scale range
if pr <=c then sc=100 else (
   if x >=range then sc=0 else sc= 100-(x*mult)
)
sc=sc/100 -- it's because "percent" value  range (0-100) and "scale" value range (0-1) are not proportional
[sc,sc,sc]

Нажимаем кнопочку Evalute и закрываем окно скрипт-контроллера.

Как работает этот скрипт

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

dependsOn $c_obj.pos.controller  
dependsOn $Slider01.value.controller

задают переменные, основанные на управляющих объектах, это значит, что при изменениях в контроллере объекта c_obj или ползунка Slider01 то изменения мы сможем видеть во вьюпорте в реальном времени.  Если эти переменные не задать, то все, все равно будет работать, но изменения будут видны только при создании ключей анимации.

Далее задаем три переменные:

pos=this.pos.controller.percent
range=$Slider01.value
cobj=$c_obj

Переменной pos присваиваем текущее значение Path контрллера текущего масштабируемого поинта. Это нужно для того, чтобы показать скрипту в каком месте пути находится поинт, чтобы он срабатывал при приближении управляющего объекта-замка. В качестве имени текущего объекта мы использовали переменную this, которую создали в начале. Переменная range будет использоваться для задания пределов срабатывания поинта, то есть на каком расстоянии в процентах, при приближении замка начнет закрываться поинт. Переменную cobj заводим чисто для удобства чтобы не писать все время $c_obj01.

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

mult=(100/range)
pr= cobj.pos.controller.percent
c=pos-range            
sc=100
x=100-((100-pr)+c)

Переменная mult - это множитель который будет доводить, полученное в результате работы скрипта, значение масштаба поинта, до нужного размера. Переменная pr это положение замка на пути в процентах. Переменная c – это точка в которой начинается масштабирование поинта, то есть начало области масштабирования. В переменной sc будет храниться значение которое мы потом отдадим масштабу поинта. Переменная x это самое интересное и самое сложное в этом скрипте. Переменная x, это счетчик, который выдает нам значение пройденного пути в процентах, НО внутри области масштабирования поинта:

 

На рисунке я вывел значение x, чтобы показать как оно работает. Вы можете видеть, что при вхождении в область масштабирования счетчик выдает нам значение от 0 до 20, то есть он пролистывает всю область масштабирования и выдает нам текущее значение внутри нее.(Область масштабирования в данном случае установлена как раз на 20). Именно это значение с помощью множителя mult мы будем превращать в масштаб поинта.

Ну, а теперь само выражение:

if pr <=c then sc=100 else (
   if x >=range then sc=0 else sc= 100-(x*mult)
)

Его можно толковать так: если положение управляющего объекта (pr) меньше начальной точки масштабирования (c), то масштаб равен 100, иначе, если управляющий объект зашел в зону масштабирования, то вступает в силу следующая проверка: если счетчик пройденного пути внутри области масштабирования (x) превышает значение области масштабирования, то масштаб поинта равен 0, а если он соответствует значениям предела, то мы берем это значение (x) и через нехитрое выражение с множителем (mult) превращаем его в значение масштаба и отдаем переменной (sc).

Теперь полученное значение переменной sc надо отдать собственно масштабу поинта, для этого сделаем еще пару штрихов:

sc=sc/100
[sc,sc,sc]

Итак, sc=sc/100, тут все просто, чтобы нам было удобнее работать, в скрипте мы делали все вычисления в пределах 100%. Однако масштабирование работает в пределах не от 0 до 100, а в пределах от 0 до 1, поэтому полученное значение мы просто делим на 100, после чего  позволяем вывести его скрипт-контроллеру в качестве значения масштаба нашего текущего поинта, в виде XYZ матрицы [sc,sc,sc]

Если вы ничего не поняли, это не страшно, я сам часа два потратил на то чтобы вспомнить как вообще работает мой же собственный скрипт )))

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

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

Для этого выделяем замок c_obj, идем в Wire Parameters, Transform->Position->Percent и привязываем его к слайдеру Object (Slider)->Value.

Ставим стрелку, чтобы c_obj зависел от слайдера и жмем Connect. Созданные, в процессе анимации, ключевые кадры, будем искать, выделив объект Slider02.

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

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

Практическое применение. Вариант первый (тканный).

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

Итак, давайте создадим объект, который у нас будет непосредственно тканью, которую вы будем соединять нашей молиней.  Чтобы не впадать в долгие размышления, создайте обычный цилиндр, преобразуйте его в Editable Poly и удалите ненужные части:

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

Затем, в подобъекте Vertex, выделяем все точки каждого сплайна и и тип точек устанавливаем на Smooth. Дело в том, что сплайны будут привязаны к поверхности ткани, и чтобы они плавно гнулись при анимации, меняем тип точек. После этого привязываем точки к поверхности цилиндра. Для этого каждому сплайну назначаем модификатор SkinWrap и в качестве деформера указываем наш цилиндр. В общем-то все это делается для достижения следующего эффекта:

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

Ну, а теперь, на среднем сплайне строим ту самую систему, которую мы строили в начале. Масштабируемые поинты необходимо разместить в точках меша ткани, именно эти точки мы будем «сшивать» тканью.

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

Построил я его из плоскости, преобразовав ее в Editable Poly и склеив несколько точек. Такие лоскутки надо пробросить между всеми точками, которые будем склеивать. Поэтому мы копируем этот лоскуток между всеми точками шва ткани.

Теперь выделяем цилиндр с лоскутками и применяем модификатор Cloth. Заходим в Object Properties и указываем что все объекты – ткань. Чтобы все заработало, нужно создать множества различных групп и привязать точки лоскутков к точкам шва. Тут придется потрудиться. Выделяем цилиндр и начинаем «колдовать» с группами.  В модификаторе Cloth идем в Group, выделяем пол цилиндра точек, как бы сказать… со стороны «спины» что ли, а можно и больше.

Эти точки мы зафиксируем, чтобы ткань у нас была закреплена в пространстве. Создаем из этих точек группу, нажав точку Group. После того как группа создана, нажимаем кнопку Preserve. Это зафиксирует точки ткани в пространстве. Вообще группа Preserve, на самом деле, обычно используется для того чтобы данная группа управлялась предшествующими модификаторами, это, как правило, модификатор Skin. Но, в данном случае, эта группа точек просто застынет на месте. 

Теперь самое «муторное». Нам нужно создать группы для всех точек взаимодействия шва и лоскутков, чтобы потом соединить их на уровне групп. Для этого, оставаясь в подобъекте Group,  выделяем по очереди каждую точку шва и назначаем ей группу. Тут очень важно правильно дать название группам, чтобы потом не сбиться при создании связей.  Я называю их 1,2,3,5,6,7… и т.д.:

Теперь проделываем то же самое с точками лоскутков. Создаем группы в точно таком же порядке, только имена я даю такие же, но с префиксом 0, чтобы легче было назначать связи, то есть 01, 02, 03… и т.д, и тут же привязываем каждую группу лоскутка к одноименной группе цилиндра, без префикса 0 в названии. Делается это нажатием на кнопку Group, после чего выбираем из списка группу, к которой будем привязываться. Вот что должно получиться:

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

 

Но и это еще не все. Теперь нам нужно заставить сжиматься лоскутки чтобы они притягивали ткань. Для этого нам нужно создавать, опять таки, группы и поместить каждый лоскуток в отдельную группу. Для этого, выделяем лоскуток, заходим в подобъект Group, выделяем все точки лоскутка, создаем группу, называем ее, например как я – 0001, выходим из подобъекта, выделяем следующий лоскуток, создаем группу, называем 0002, и так далее, для всех лоскутков. Вот что получается:

Так для чего же нам эти группы? Все просто, мы можем назначить этим группам, собственные настройки ткани! А зачем нам назначать собственные настройки ткани? Да затем, что это позволит нам назначать контроллеры на отдельные параметры этой ткани. Чтобы стало понятнее - продолжим.

Итак, выделяем по очереди каждую из последних групп, в которые мы заключали полностью лоскутки, и для всех этих групп ставим галку «Use This Properties»:

Мы здесь собрались, чтобы таки сделать застежку «молнию», так давайте уже, наконец, сделаем то, зачем мы здесь собрались!

Итак, выходим из групп и выделяем наш тканный цилиндр. Идем в Curve Editor, находим там модификатор Cloth, этого цилиндра, открываем и видим там все наши группы! Но нас-то интересуют только группы с лоскутками:

Теперь, нужно проделать одну и ту же операцию для всех указанных групп. А именно, открыть группу и назначить скрипт-контроллеры на 4 отдельных параметра. Итак, открываем группу и находим там параметры U Stretch, V Stretch, U Scale и V Scale. Сейчас будем назначать им скрипт контроллеры:

Итак,  на U Scale назначаем скрипт-контроллер и пишем в нем:

$zip_obj01.scale.Z

Перед закрытием окна скрипт-контроллера не забываем нажимать кнопочку Evalute.

Потом то же самое пишем в скрипт-контроллере V Scale. А лучше просто скопировать контроллер с U Scale и вставить как Instance на параметр V Scale. Instance – это значит что контроллер используется тот же самый, и если что-то поменять в одном контроллере то изменения коснутся всех копий.

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

Следующие два параметра нам нужны опционально, в принципе их можно вообще не делать, но мы сделаем, и я объясню для чего они нужны. В момент, когда молния расстегивается, и лоскутки резко увеличиваются, мы, с помощью этого параметра делаем их мягче и они при расстегивании замка не так «глючат» (все таки, данный прием анимации вряд ли был предусмотрен разработчиками), и работают вполне прилично.  Вот и делаем. Назначаем на U Stretch скрипт-контроллер и пишем в нем:

5+((1-$zip_obj01.scale.Z)*500)

Потом копируем этот контроллер и вставляем как Instance на параметр V Stretch.

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

U Scale и V Scale - $zip_obj02.scale.Z
U Stretch, V Stretch -  5+((1-$zip_obj02.scale.Z)*500)

Для третьего:

U Scale и V Scale - $zip_obj03.scale.Z
U Stretch, V Stretch - 5+((1-$zip_obj03.scale.Z)*500)

И так далее, для всех лоскутков.

840 0 850 97
33
2010-03-17
Внушает :)
2010-03-17
Неплохая тренировочка по скрипт-контроллерам )))
2010-03-17
шикарный урок. действительно УРОК. спасибо.
2010-03-17
Круто! На шарик с молнией можно смотреть часами. Завораживает [smile=04] 5/5
2010-03-17
Отличный урок ! как и все предыдущие !
2010-03-17
Отличный урок! Поставил 5/5. Но ощущение, что можно сделать проще не покидает. Пошел экспериментировать :)
2010-03-17
Спасибо! 5/5
2010-03-17
Меня, кстати, тоже не покидает это ощущение. Долго пытался найти способ склеить ткань без лоскутков, стандартными средствами или макссктиптом, пока ничего не вышло, так что если кто обнаружит более простой способ, мы легко можем развить эту тему в отдельном топике. Так что, если есть идеи, милости просим )))
2010-03-17
Класс 5\5 )
2010-03-18
Жаль сейчас нет времени копаться, но по-моему достойный урок. Автору, как всегда, есть чем поделиться. Спасибо. Пятёрки с меня.
2010-03-18
Здравствуйте! Открыла почту и вижу, что от Максима Поклонова пришло письмо. Так обрадовалась. Новый урок об анимации. Просто супер. Спасибо за урок!
2010-03-18
Даа..ужж, вотето класс!!! 5/5!
2010-03-18
[b]Cucu[/b], да уж, как обещал...
2010-03-18
Максим,очередной шедевр,ну собственно как и предыдущие уроки.Ниже уже всё написали,добавить нечего.Остаётся только поставить 5/5
2010-03-18
Спасибо, Виктор )
2010-03-18
хардкорно)
2010-03-19
позновательно :)
2010-03-19
супер 5\5 [smile=04]
2010-03-19
Awesome.
2010-03-20
давненько небыло УРОКОВ!!! Максим как всегда,радует 5+
2010-03-21
А не пора бы автору браться за книгу...
2010-03-21
книгу, книгу... Пора, конечно. Только на написание книги уйдет полгода-год, если не больше, а все это время еще как-то работать надо. Еще и не известно принесет ли это доход (пока не занимался данной темой). Поэтому пока до книги не добрался, нет времени... А мысль такая бродит уже давно, потому что там можно выдать полностью всю необходимую информацию о всех возможных приемах и способах анимации, по персонажке по ткани, по волосам, динамике, по частицам, по постобработке, да там опупеть просто сколько писанины.... Поле непаханное!!! А непаханное, потому что все книги по максу обычно толком не меняются, просто заголовок меняют на последнюю версию а оглавление остается то же... А спец лит-ры я и в глаза толком не видел, где бы все это можно было найти, то что реально надо для нормальной работы. Бродит еще мысль диск записать, это проще, но опять же - время...
2010-03-22
классно!!!
2010-03-22
Однозначно 5\5 у Вас всегда очень оригинальные и полезные уроки, спасибо!)
2010-03-25
пять и пять )
2010-03-27
урок просто супер, есле вы Максим когда то напишете книгу, или выпустите диск я его (её) обязательно куплю, так мало полезного материала по Максу в наших краях.
2010-03-27
Ну что ж, придется тогда книгу писать, один покупатель уже есть ))))
2010-03-27
небольшое замечание к уроку с моей стороны. Когда насаживаете c_obj на путь НЕ назначайте ему Path Constraint через Animation->Constraints->Path Constraint, есле вы это сделаете, Path Constraint назначится через Position List и скрипт в его исходном варианте не будет работать. Назначайте Path Constraint через вкладку Motion, Assing controller на Position. Еще раз спасибо за урок :)
2010-03-29
Всем привет! Хочу сказать, что Максим Поклонов мой учитель по 3д максу. Всегда помогает в трудные моменты. Я пишу ему свои вопросы и на ответе получаю полезные советы. Эти советы помогает мне развиваться. Слижу за его уроками. Очень часто задаю вопросы. А он радует нас своими уроками. Учитель мой, обязательно задам вопросы на эту тему урока. Спасибо что вы есть!
2010-04-11
ммм...к сожалению нет макса в данный момент, не могу потестить...Но, судя по видео, только я заметил что "молния" постепенно открывается, а по ней едет собачка т.е. движение плохо синхронизировано? Вообще оба способа какие-то мудренные, кажутся не пригодными для продакшена. Совет автору: сделать урок, который расскажет больше о работе МаксСкрипт и контроллеров в максе(не перечислить, а иммено рассказать базу-архитектуру) , и примеры. Я бы за такой урок(если он получится действительно хорошим и правдивым) похвалил баллами. А данный урок...особенно во второй части, сильно уходит в разряд "нажимай кнопочки, как я" имхо
2010-04-17
Profi, super!
2010-04-29
максим по больше уроков бы [smile=06]
2011-08-28
великолепно, невероятно актуальный урок, больше уроков! ^^
RENDER.RU