Немного о настройке персонажей для анимации, введение в MEL

Немного о настройке персонажей для анимации, введение в MEL и практическое применение скриптов в «сетапе». (Maya 7.0)

Предисловие.

Сейчас появилось много различных скриптов, помогающих настроить персонажа для анимации практически с нуля. Как правило, все происходит просто и наглядно. Буквально несколькими кликами мыши. Но при создании сетапа с помощью таких скриптов возникает ряд проблем. Как правило, изменить такой сетап бывает проблематично. Что-либо добавить тоже. Так же не всегда устраивает внешний вид контрольных объектов (далее контроллер). Поэтому завсегда полезно уметь настраивать персонажа своими собственными руками.
Впихнуть полный сетап персонажа в один урок практически нереально. Тем более, что под каждого конкретного персонажа делается свой сетап. В данном уроке мы затронем лишь часть сетапа персонажа, посвященную иерархии контрольных объектов и рассмотрим, как с помощью супермена мира Maya по имени MEL, автоматизировать выполнение некоторых повторяющихся процессов. Некоторых 3Дешников начавших осваивать Майю по началу пугает даже само название MEL. И я не был исключением. :о) Хочу сказать, что не стоит бояться скриптов. Не так страшен черт, как его малютка… ;о) Более того, иногда он затягивает человека как наркотик. И подсев на него человек начинает писать скрипты буквально для каждого своего действия. :о) Однако, не стоит увлекаться.
Урок рассчитан на людей знающих инструменты Maya и пробовавших сетапить персонажей ранее. Поэтому разжевывать «где какую пипку ткнуть, чтобы стало красиво» не буду. Объем урока и так получается довольно большим.

Немного о том, что собсна мы будем делать.

Исходим из того, что аниматор не должен видеть кости вообще никогда. Они ему просто не нужны. Поэтому кроме скелета создается еще одна иерархия, иерархия контрольных объектов, которые собсна и управляют костями. Вращения, перемещения и прочие скейлы передаются с контрольных объектов на кости с помощью “Constraints” , “Utility Nodes”, “Expressions” или их комбинаций. Иногда делается и простой “Parent”.
Предположим, вы заранее изготовили себе набор контрольных объектов из NURBS кривых или скачали с инета. Лучше чтоб контрольные объекты были удобной формы, размера и были расположены так, чтобы аниматору не пришлось переключаться из вида в вид, лишь для того, чтоб схватить нужный контрольный объект. Канешна можно написать или взять готовый скрипт, где в отдельном окне на миниатюре персонажа можно выделить любой контролер. Но это тема для отдельного урока и здесь мы рассматривать ее не будем.
При создании скелета для персонажа с симметричным строением тела в 90% случаев используют “Mirror Joint”. Значения “Rotate” на костях отзеркаленной половины скелета равны нулю, но при этом мы имеем возможность симметрично вращать кости. Т.е. выделив плечевые кости обеих рук и увеличив значение “Rotate” по оси Y, мы увидим, что обе руки повернуться зеркально, навстречу друг другу.

Это возможно благодаря “Joint Orient”. Однако если мы попробуем ту же процедуру проделать с обычными отзеркаленными объектами, например, контрольными объектами рук, то объекты повернуться в одном направлении.

Вот именно эту проблему мы и попробуем решить.

Создание иерархии контрольных объектов:

Object Orient”.

На самом деле никакого “Object Orient” в настройках объекта вы, конечно же, не найдете. Это понятие я ввел по аналогии с “Joint Orient”, но применительно к объекту. Роль, так называемого, “Object Orient”а, может взять на себя группа. Для этого создаем, пустую группу. Делаем ей “Parent Constraint” от кости, которой должен управлять контрольный объект (офсеты должны быть отключены). И убиваем констрейн с группы. Т.е. теперь группа расположена там же где и кость и ориентирована точно так же. И пусть вас не пугают дикие значения в “Translate” и “Rotate” группы, кроме вас их никто не увидит. ;о) Теперь делаем “Parent” контрольного объекта к группе. Располагаем его так, как нужно. “Pivot Point” у него должен быть совмещен с пивотом кости. Вращаем и маштабируем до кондиции. И делаем ему “Freeze Transformations”. Вуаля. Контрольный объект имеет такую же ориентацию что и кость, внешний вид и размер тот самый, который вы заказали и при этом нулевые значения на “Rotate”.

Добавим “Orient Constraint” с контрольного объекта на кость. Кость вращается вслед за контрольным объектом.

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

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

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

Введение в MEL. (теория)

Задача перед нами поставлена, теперь надо вооружиться инструментами для ее решения. В этой части урока мы рассмотрим общие понятия MEL и те команды, которые нам понадобятся для написания скрипта. Зачем они нам и вкратце как они работают. За более подробным описанием обращаемся в хелп. Не забываем, что он наш друК, товарищьЧ и брат… ;о) В хелпе есть примеры использования команд. Поэтому можно не стесняясь выдирать их оттуда и модифицировать под свои нужды.

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

select

выделяет объект

rename

переименовывает объект

delete

удаляет объект

duplicate

делает копию объекта

xform

задает и возвращает значения на translate, rotate и scale объекта

makeIdentity

делает “Freeze Transformations” объекту

getAttr

возвращает значение заданного атрибута

setAttr

изменяет значение заданного атрибута

connectAttr

делает connect одного атрибута к другому

createNode

создает заданный нод в сцене

group

создает группу

parent

делает парент одного объекта к другому

pointConstraint, orientConstraint, parentConstraint

создают констрейны от одного объекта к другому

ls

возвращает список объектов (например, выделенных в данный момент)

listRelatives

возвращает список «родителей» и «детей» заданного объекта

listConnections

возвращает список входящих и выходящих связей объекта

size

возвращает размер массива

objExist

проверяет, есть ли заданный объект в сцене

nodeType

возвращает тип нода или объекта

Структура команд и синтаксис:

Для начала о том, как вообще выглядят команды MEL.

команда –флаги объект;

«команда» - это то, что вы собственно хотите сделать.

«флаги» - это настройки, с которыми вы хотите запустить данную команду.

«объект» - это имя объекта, над которым вы хотите провести данную операцию.

«Точка с запятой» указывает, где кончается одна команда и начинается другая.
Пример:

select –replace “object1”;

Эта команда сделает выделенным объект “object1”, а флаг –replace указывает на то, что предыдущее выделение необходимо снять. Флагов у команды может быть несколько. Поэтому чтобы не загромождать скрипт, для флагов введены сокращения. Например, команда:

group -empty -name “group1”;

Равносильна команде:

group -em -n “group1”;

И создаст, пустую группу с именем “group1”. Подробнее о сокращении для флагов каждой команды смотрите в хелпе.
Некоторые команды выполняются не применительно, к какому то объекту, а просто с набором флагов:

grid –spacing 1 –size 10;

Команда задаст конфигурацию для сетки в сцене.

Строка, начинающаяся с двух обратных слэшей //, считается невыполняемой. Чтобы добавить комментарий, описывающий то или иное место скрипта, просто поставьте двойной обратный слэш и пишите то, что считаете нужным. Если комментарий большой и одной строкой не ограничивается, то блок текста с описанием можно заключить в такую комбинацию /* в начале и*/ в конце.

Переменные:

Переменная – это выделенный, именованный участок памяти в котором вы можете хранить какуюнить информацию. В MEL переменные обозначаются знаком доллара в начале имени. Чтобы использовать переменную, ее необходимо сначала «описать». В зависимости от типа переменной, компилятор выделит под нее участок памяти нужного размера и присвоит ему метку.
Переменные одного типа не могут принимать значения переменных другого типа. Т.е. переменная, описанная как string (строковая) не может принимать числовые значения. А переменной типа integer (целочисленное), нельзя присвоить числовой значение с плавающей запятой. Еще одной причиной разделения переменных на типы, является например то, что операции с целочисленными переменными компьютер выполняет в разы быстрее, чем операции с переменными с плавающей запятой.
Описание переменной выглядит так:

типПеременной $имяПеременной;

int $numCount;

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

float $randNumber = 10,32;
string $simpText = ”Maya must live.”;

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

Если переменной не присвоено никакого значения, то по умолчанию, ей присваивается ноль или пустая строка.

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

string $simpText = ”Maya must live”;
string $simpTextAdd = ”forever.”;
print ($simpText + “ “ + $simpTextAdd);

Результатом будет надпись

Maya must live forever.

Для перевода строки используют \n внутри текста.

print “Maya must live \n forever.”;

Результатом будут две строки на экране.

Maya must live
forever.

Массивы.

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

string $wordsSet [] = { “take” , “it” , “easy” };
float $numsSet [] = { 1.2 , 3.546 , 127.1 , 12 , 0.023 };

// Вызываются переменные из массива по их порядковому номеру. (отсчет начинается с нуля).
string $wordSum = ($wordsSet [0] + “ “ + $wordsSet [2] + “ “);
print $wordSum;
print $numsSet [4];

Результатом будут надписи take easy и 0.023 на экране.

«Обратные апострофы»:

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

string $pCons[] = `pointConstraint object1 object 2`;
delete $pCons;

Такой конструкцией как:

delete `pointConstraint object1 object2`;

Этой строкой мы создаем “Point Constraint” объекта 1 на объект 2, который тут же удаляется. Во первых мы обошлись без переменной, а во вторых визуально объем текста сократился и стал более читаем.
А строка:

string $selArray[] = `ls -selection`;

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

Циклы и ветвления.

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

Цикл с выходом по условию организуется командой while.

string selected[] = `ls -selection`;
int $somSel = `size($selected)`;
while ($somSel >= 1)
{
print “Selection is not empty.”;
}

Переменной $somSel присваивается количество выбранных на текущий момент объектов. И пока значение переменной будет больше либо равно единице, т.е. будет выделен хоть один объект, то на экран будет выведена строка “ Selection is not empty.”.

Для создания цикла с заданным количеством повторов используется команда for.

<font color="#ff00ff">int</font> $i;
<font color="#515151">for </font>($i = 1; $i <= 10; $i++)
{
<font color="#515151">print</font> ($i + " <font color="#3366ff">cycle \n</font>");
}
<font color="#515151">print</font> ("<font color="#3366ff">End…</font>");

Заводим переменную $i. После команды for в круглых скобках последовательно идут, начальное значение переменной, условие выхода из цикла (цикл будет выполняться, пока $i будет меньше либо равна 10) и выражение $i++ значит, что переменная $i с каждым циклом будет увеличиваться на единицу.

Для пошагового перемещения по массивам используется немного видоизмененная команда for in. Связано это с тем, что не всегда размер массива заранее известен и поэтому мы не можем заранее указать точное количество циклов.

string $selArray[] = `ls -selection`;
string $sel;
for ($sel in $selArray)
{
print ($sel + “\n”);
}

Создаем массив $selArray[] в который сохраняются имена выделенных объектов. Заводим переменную $sel , которой при помощи команды for in последовательно присваиваем значения каждой переменной из массива и выводим значение $sel на экран. Получаем список всех выделенных объектов. С помощью команды for in легко произвести необходимые действия со всеми объектами, которые были выделены пользователем.

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

int $objEx = `objExists object1`;
if ($objEx == 1)
{
select object1;
}
else
{
print “object1 does not exist”;
}

Создаем целочисленную переменную $objEx и с помощью команды objExists проверяем существует ли в сцене объект с именем object1. Результат сохранятся в $objEx. Далее командой if проверяем условие и если $objEx равна единице (т.е. объект с заданным именем в сцене есть), то выделяем object1, иначе выводим текст «object1 не существует».

Процедуры.

Процедуры - это несколько команд объединенных в одну группу. Если часть команд выполняется за программу несколько раз. То есть смысл объединить их в процедуру. Процедуре присваивается имя, по которому она вызывается.
Пример:

proc simpleProcedure ()
{
print “My first procedure very nice! \n”;
print “But very small… :o)”;
};

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

// описываем процедуру
proc simpleProcedure ()
{
print “My first procedure very small… \n”;
print “And do nothing! :o)”;
};

// выполняем какие либо действия если необходимо
string $pCons[] = `pointConstraint object1 object 2`;
delete $pCons;

// вызываем процедуру
simpleProcedure;

Процедуры могут быть с входящими параметрами и возвращать какое-либо значение. Процедура возвращающая значение называется «Функцией». Например, функция:

<font color="#515151">proc</font> <font color="#ff00ff">int</font> sum (<font color="#ff00ff">int</font> $a, <font color="#ff00ff">int</font> $b)
{

return $a + $b;
};

Вернет сумму двух чисел.
В результате вызова функции с параметрами 2 и 3:

int $simpSum = sum (2, 3);

Переменной $simpSum будет присвоено значение 5.

«Глобальные» и «локальные».

Переменные как процедуры могут быть «локальными» и «глобальными». «Локальная» переменная, описанная в теле процедуры, будет видна только в пределах этой процедуры. Т.е. использовать ее можно будет только внутри процедуры. Вызов ее из другого места программы приведет к ошибке. «Глобальная» же переменная будет видна из любого места программы, в том числе из любой другой процедуры.
Разделение на «глобальные» и «локальные» связано с тем, что «глобальные» переменные висят в памяти постоянно, а «локальные» только тогда когда нужно. Во времена когда деревья были большими, а компьютеры еще больше, и размер их памяти шел на килобайты, это было донельзя актуально. ;о) Сейчас же нужно сильно потрудиться, вручную создавая столько переменных, чтобы забить оперативку даже на плохеньком компе.. :о)
Еще одна причина разделения это наименование переменных. В теле основной программы и в какойнить процедуре могут находиться «локальные» переменные с одинаковыми именами и иметь при этом разные значения. Однако этот вопрос можно разрешить по-другому. Просто обзывая каждую переменную своим уникальным именем. Например, ставя в начале названия переменной сокращение на основе названия процедуры, в которой она используется, состоящее из нескольких букв.
Посему можно не боясь делать все переменные глобальными, главное не забывать давать им уникальные имена. И тогда не придется думать о том где данная переменная будет видна, а где нет. При отладке это сильно облегчит вам жизнь.. ;о)

Например есть процедура simpleProcedure и в ней нам надо создать переменную numSel.

float $spNumSel = 10;

Увидев буквы sp в начале названия, мы можем понять, что эта переменная относится к процедуре simpleProcedure.
Описание «глобальной» переменной отличается от описания «локальной» добавлением global в начале.

float $lcNumSel;
global float $glNumSel;

Переменная $lcNumSel будет локальной, а $glNumSel глобальной.

Пишем скрипт.

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

//--------------------------CUT--------------------------//

// создадим процедуру, которая построит интерфейс для более удобной работы со скриптом
global proc tiCopyObjectXform_gui ()

{
// проверяем, не открыто ли уже окно, которое мы хотим создать, и если да, то убиваем его
if (`window -exists CopyObjectXform_ui`)
deleteUI CopyObjectXform_ui;

// создаем окно CopyObjectXform_ui с заданными параметрами
// окно должно автоматически растягиваться до размеров элементов
// расположенных в нем и в заголовке окна будет текст "Copy Object Xform"
window -rtf on -t "Copy Object Xform" CopyObjectXform_ui;

// указываем, что элементы должны располагаться в один столбец
columnLayout -adj 1;

// в окне делаем текстовую подсказку о том, что сначала надо выделить кость,
// а потом контрольный объект (текст подсказки выровнен по центру)
text -align "center" "<< Select Joint then Ctrl >>";

// вставляем разделитель для более эстетичного вида окна
// (параметры подбираем на свой вкус)
separator -height 5 -style "out";

// вставляем «чек бокс» выравненный по левому краю
// и имеющий название "Orient Ctrl by Join"
// с помощью него мы сможем указать, надо ли ориентировать объект по кости
checkBox -v 1 -l "Orient Ctrl by Join" -align "left" checkOCtr;

// снова разделитель
separator -height 5 -style "in";

// следующий «чек бокс» указывает надо ли повернуть контроллер по кости
checkBox -l "Rotate Ctrl by Joint" -align "left" checkRCtr;

// разделитель
separator -height 5 -style "in";

// еще один «чек бокс» определяет надо ли кидать “Orient Constraint"
// с контрольного объекта на кость
checkBox -v 1 -l "Create Orient Constraint" -align "left" checkOCns;

// разделитель
separator -height 5 -style "in";

// последний «чек бокс» отвечает за то надо ли делать “Point Constraint"
// с контрольного объекта на кость
checkBox -l "Create Point Constraint" -align "left" checkPCns;

// внизу окна втыкаем кнопку с надписью "Run"
// которая собсна и будет запускать основную процедуру
button -label "Run" -c ("tiCopyObjectXform");

// эта команда отображает, ранее созданное окно, со всеми его параметрами и объектами
showWindow CopyObjectXform_ui;
};

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

// описываем основную процедуру
global proc tiCopyObjectXform ()

{

// сохраняем текущие выбранные объекты в массив
global string $select[] = `ls -selection`;

// «защита от дураков» проверяем если количество выделенных объектов не равно двум,
// то аварийный выход из процедуры и надпись «выделите два объекта и запустите».
if (`size ($select)` != 2)
error ("tiCopyObjectXform :: Select two objects and run");

// переменной $parGrp присваиваем имя выделенной кости
// и добавляем к нему null_ в начале и _Ctrl в конце
// это будет имя родительской группы для контрольного объекта
global string $parGrp = "null_" + $select[0] + "_Ctrl";

// в массив $parCtrl[] сохраняем список родителей контрольного объекта
global string $parCtrl[] = `listRelatives -p $select[1]`;

// проверяем, если контрольный объект к чему то припаренчен
if (`size ($parCtrl)` > 0)

// то создаем группу с именем $parGrp для контрольного объекта
// которая в свою очередь припаренчена к родителю контроллера
$crrGr = `group -p $parCtrl -n $parGrp $select[1]`;

// иначе создаем такую же группу, но в мировых координатах
else
$crrGr = `group -n $parGrp $select[1]`;

// снапим группу к кости делая ей “Point Constraint” без офестов
// и убиваем констрейн
// “Point Constraint” делаем в любом случае потому что,
// “Pivot Point”-ы кости, группы и контрольного объекта должны совпадать
delete `pointConstraint $select[0] $parGrp`;

// если включен чек бокс checkOCtr то ориентируем группу по кости
// создав и убив orientConstraint без офсетов
if (`checkBox -q -v checkOCtr`)
delete `orientConstraint $select[0] $parGrp`;

// если включен чекбокс checkRCtr то создаем и убиваем orientConstraint
// с группы на контрольный объект
// тем самым мы поворачиваем контрольный объект так же как и кость
if (`checkBox -q -v checkRCtr`)
delete `orientConstraint $select[0] $select[1]`;

// снапим контрольный объект к группе создав и удалив pointConstraint без офсетов
delete `pointConstraint $select[0] $select[1]`;

// выделяем только контрольный объект
select -r $select[1];

// обнуляем значения Translate Rotate и Scale на контрольном объекте
// флаг -apply on указывает, что необходимо сохранить текущее
// местоположение и размер объекта
// т.е. тем самым мы сделали Freeze Transofrmations контрольному объекту
makeIdentity -apply on;

// очищаем выделение
select -cl;

// если включен чек бокс checkPCns то создаем pointConstraint с офсетами
// с контрольного объекта на кость т.е. контроллер будет управлять местоположением кости
if (`checkBox -q -v checkPCns`)
pointConstraint -mo $select[1] $select[0];

// если включен чек бокс checkOCns то создаем orientConstraint с офсетами
// с контрольного объекта на кость т.е. контроллер будет управлять поворотом кости
if (`checkBox -q -v checkOCns`)
orientConstraint -mo $select[1] $select[0];

// называем контрольный объект так же как и кость, но с добавлением _Ctrl
rename $select[1] ($select[0] + "_Ctrl");
};

// вызываем процедуру построения интерфейса
tiCopyObjectXform_gui;

//--------------------------CUT--------------------------//

Вот как будет выглядеть окно интерфейса.

Запускаем скрипт, появляется окно. Выделяем кость и контрольный объект, ставим, нужные галочки в чекбоксах и жмем “Run”. Процесс создания и удаления констрейнов, занимающий раньше несколько минут, теперь занимает около десяти секунд. А если создаются подряд связи для контроллеров с одинаковыми свойствами, то еще меньше. Оптимизация на лицо. Особенно учитывая, что контрольных объектов на персонаже редко бывает меньше пятнадцати.
По умолчанию стоят галочки на чекбоксах «Orient Ctrl by Joint» и «Create Orient Constraint» на кость. Это дефолтовые параметры для создания обычной FK связи. Подразумевается, что контрольные объекты уже имеют нужный вид. Как правило, кто часто делает сетап персонажа, тот имеет под рукой набор готовых контроллеров.
При таком раскладе, контрольный объект приснапится к кости, будет ориентирован по ней же посредством группы и с контроллера на кость будет создан “Orient Constraint”. При этом правильно названа должна быть только кость. Группа и контрольный объект обзываются скриптом автоматически, исходя из названия кости.
Для контроллера, который будет управлять тазовой костью, необходимо будет включить галочку «Create Point Constraint», для управления Translate-ами кости.
Если кисть персонажа расположена не параллельно земле, а под уголом, то для ее контрольного объекта можно включить галочку «Rotate Ctrl by Joint». Контроллер будет не только ориентирован так же как кость, но и визуально будет повернут так же.

Для контроллеров глаз наоборот надо снять все галочки кроме «Create Orient Constraint». Ориентировать контроллеры глаз по костям не надо, потому как глаза должны поворачиваться синхронно в одну сторону, а не зеркально как кости.

Комментарии и оптимизация.

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

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

Старайтесь делать структуру программы максимально простой. Избегайте многократно вложенных циклов и ветвлений. А так же многократных вызовов процедур из других процедур. Это тоже поможет в последующей отладке и редактировании скрипта.

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

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

Да прибудет с вами MEL. ;о)

Успехов.

Файл урока: tiCopyObjectXform_gui.mel

645 0 850 37
17
2006-08-02
Загнул так загнул. Ничего себе.
2006-08-02
ниасилил, многа букав Автор, а вы этот урок читали?: http://render.ru/books/show_book.php?book_id=4
2006-08-02
Спасибо! Уважаю. Именно таких уроков не хватает. Балл по максимуму.
2006-08-02
2 Михаил Ершов: Читал. P.S. может вы ошиблись адресом? меньше букав на удаве... сходите... там осилите... ;о)
2006-08-02
Alien, а приставка :[ncux] никакого отношения не имеет к клантэгу цс команды? :) уж больно много совпадений в написании :))
2006-08-02
2 Dmd: имеет, имеет... ;о) я капитан клана...
2006-08-02
ой ну понятно :) а я вот SNT | GO1em вдруг тебе это о чем-то говорит :) ..... пришел, понимаешь, наофтопил :)) извиняюсь :)))
2006-08-02
Очень хороший урок. Спасибо большое!
2006-08-03
питёрки поставил
2006-08-07
Немного сумбурно, но эту кашу есть можно.
2006-08-07
Все хорошо, только зеленые буквы на белом, глазам пипец!
2006-08-24
Спасибо автору, полезный урок. 5+5
2006-08-27
Чур меня... Какой урок по Maya не откроешь, всюду скрипты... :) Такое впечатление, что там бокс нельзя без скриптов сделать. :)
2006-08-29
Хороший урок для тех, кто начинает писать сои скрипты. Сам скачал, поднастрою и в работу! :) Жаль только иконку на шелф не сделал :) Из полезностей - чтобы имя контроллера бралось с кости + префикс, но это уже так... по вкусу.
2006-09-08
Сразу видно человек работал. Серьёзно и солидно!
2006-09-11
Скрипты это страшная сила!
2006-09-18
Атличный урог! и снт помню.. я там раньше [PTB]Baralgin'ом звался
RENDER.RU