Интроспекция в MaxScript
Оглавление
Функции для исследования классов и объектов
getPropertyController() и setPropertyController()
showInterfaces() и showInterface()
getInterfaces() и getInterface()
Интроспекционные методы класса Value
Интроспекционные свойства и методы класса MAXWrapper
Интроспекция функций, определённых пользователем
Введение
MaxScript имеет развитые средства интроспекции, т.е. возможность определить тип и структуру объекта во время выполнения программы или при работе в консоли (Listener).
В справке по MaxScript функции и метды, ответственные за интроспекцию, разбросаны по разным разделам, что не очень удобно.
В дальнейшем, при перечислении методов будут упоминаться только методы, имеющие отношение к интроспекции. Это не значит, что у класса нет других методов.
При написании данной статьи не ставилась цель дать полное описание каждой упоминаемой функции. Полную и подробную информацию можно найти в системе помощи по MaxScript.
Некоторые фрагменты статьи являются переводами на русский язык материалов из системы помощи по MaxScript.
Функции для исследования классов и объектов
apropos()
apropos <pattern_string> [ to:<stream> ]
Функция осуществляет поиск и вывод на консоль информации о глобальных переменных на основании указанного шаблона имени переменных и класса их значений. Синонимом для apropos() является help().
Следует отметить, что в MaxScript все имена встроенных функций, классов и других постоянных сущностей являются глобальными переменными.
Надо отметить, что хотя у этой функции имеется многообещающий псевдоним «help», какой-то развёрнутой информации по интересующему объекту (т.е. глобальной переменной), функция не выдаёт. Однако с её помощью можно обнаружить объекты, о существовании которых вы даже не подозревали.
showClass()
showClass <pattern_string> [ to:<stream> ]
Функция выводит в окно консоли информацию о классе (или классах) MaxScript. В качестве первого аргумента она принимает шаблон для поиска (строку с символами подстановки) имён классов, имён суперклассов и/или имён свойств. Шаблон имеет вид:
"<class_name>[ :<superclass_name> ][ .<property_name> ]"
где <class_name> – имя класса, <superclass_name> – необязательное имя суперкласса для указанного класса и <property_name> – необязательное имя свойства. Все три части шаблона допускают символы подстановки.
Одним из наиболее полезных применений функции является получение списка свойств для интересующего класса. Т.е. когда имеется объект известного класса (получить класс объекта всегда можно с помощью функции classOf()) и требуется изменить одно из свойств, но имя свойства не известно, то с помощью функции showClass() можно узнать имя свойства и его тип.
Например, можно посмотреть все свойства класса Box:
showClass "box.*"
Box : GeometryClass {10,0}
.height : float
.length : float
.lengthsegs : integer
.width : float
.widthsegs : integer
.mapcoords : boolean
.heightsegs : integer
OK
Обычно имена свойств класса имеют достаточно прозрачное соответствие с названиями элементов управления свойствами объекта в пользовательском интерфейсе 3ds Max.
showProperties()
showProperties <maxwrapper_object> [ <property_pattern> ] [ to :<stream> ]
Функция немного похожа на showClass() в режиме просмотра свойств класса, но работает на уровне объектов, т.е. на уровне экземпляров класса. Объект должен быть наследником класса MAXWrapper. В отличие от showClass(), функция showProperties() также показывает динамические свойства, которые индивидуальны для объекта, и свойства, добавляемые посредством механихма интерфейсов.
Если объект имеет модификаторы, то функция показывает свойства базового объекта. Если нужно посмотреть свойства модификатора, то при вызове функции это надо указать явно:
-- смотрим свойства класса Box
showClass "box.*"
Box : GeometryClass {10,0}
.height : float
.length : float
.lengthsegs : integer
.width : float
.widthsegs : integer
.mapcoords : boolean
.heightsegs : integer
OK
-- создаём объект Box с количеством сегментов по высоте 20
b = box heightsegs:20
$Box:Box01 @ [0.000000,0.000000,0.000000]
-- добавляем модификатор Twist со скручиванием на 90 градусов
addModifier b (twist angle:90)
OK
-- смотрим свойства базового объекта
showProperties b
.height : float
.length : float
.lengthsegs : integer
.width : float
.widthsegs : integer
.mapcoords : boolean
.heightsegs : integer
.realWorldMapSize : boolean
False
-- смотрим свойства модификатора Twist
showProperties b.twist
.axis : integer
.bias : float
.angle : float
.limit : boolean
.upperlimit : float
.lowerlimit : float
.Center : point3
.Gizmo : transform
false
Для функции showProperties() имеется псевдоним show().
getPropNames()
getPropNames <maxwrapper_obj> [ #dynamicOnly]
Получить список свойств объекта в виде массива можно с помощью функции getPropNames(). По логике работы эта функция аналогична функции showProperties().
getPropNames b
#(#height, #length, #lengthsegs, #width, #widthsegs, #mapcoords, #heightsegs, #realWorldMapSize)
getPropNames b.twist
#(#axis, #bias, #angle, #limit, #upperlimit, #lowerlimit, #center, #gizmo)
Функция также работает и со свойствами интерфейсов:
showInterfaces b
Interface: realWorldMapSizeInterface
Properties:
.realWorldMapSize : boolean : Read|Write
Methods:
Actions:
OK
getPropNames b.realWorldMapSizeInterface
#(#realWorldMapSize)
Подробнее интерфейсы рассматриваются ниже.
Особым случаем является вызов этой функции с суперклассом node в качестве аргумента:
getPropNames node
В качестве результата функция вернёт массив свойств класса node (.position, .name, .wireColor и т.д.).
getProperty () и setProperty()
getProperty <obj> <property_name>
setProperty <obj> <property_name> <value>
Функции предназначены для доступа к свойствам объекта через их имена. Обе функции могут принимать в качестве аргумента как имена (#heightsegs), так и строки ("heightsegs").
Выражение $Box01.height аналогично выражению getProperty $Box01 #height, а выражение $Box01.height = 50 – аналогично setProperty $Box01 #height 50. Когда класс объекта известен на этапе написания программы, то конечно предпочтительна первая форма. Когда класс объекта заранее неизвестен, список свойств объекта можно получить на этапе выполнения программы с помощью функции getPropNames(), а доступ к этим свойствам – с помощью функций getProperty() и setProperty().
Следующий пример выводит на консоль свойства и их значения для объекта типа Box:
for p in getPropNames b do format "% = %\n" p (getProperty b p)
#height = 25.0
#length = 25.0
#lengthsegs = 1
#width = 25.0
#widthsegs = 1
#mapcoords = false
#heightsegs = 20
#realWorldMapSize = true
OK
getPropertyController() и setPropertyController()
getPropertyController <value> <string_or_name>
setPropertyController <value> <string_or_name> <controller>
Получить доступ к контроллеру, который назначен свойству объекта, можно с помощью функций getPropertyController() и setPropertyController(). Если свойству не назначен контроллер, функция getPropertyController() вернёт значение undefined.
hasProperty() и isProperty()
hasProperty <obj> <prop_name_or_pattern_string>
Функция hasProperty() позволяет проверить имеется ли указанное свойство у объекта. В качестве аргумента функция принимает строку-шаблон с символами подстановки.
Функция внутри себя использует вызов функции showProperties() и, следовательно, имеет те же особенности работы.
isProperty <MAXWrapper_object> <property_name_or_string>
Функция isProperty() позволяет проверить имеет ли объект указанное свойство. В качестве аргумента функция принимает имя или строку без подстановочных знаков.
hasProperty() и isProperty() похожи, но в некоторых случаях дают разные результаты:
b = Box()
$Box:Box01 @ [0.000000,0.000000,0.000000]
hasProperty b "name"
false
isProperty b "name"
true
Дело в том, что hasProperty() использует результат вызова функции showProperties() в котором ищет соответствие шаблону имени свойства. Функция isProperty() пытается обратиться к указанному свойству объекта. Для объекта класса Box функция showProperties() возвращает только список свойств класса Box, но не родительских классов. Свойство name принадлежит классу Node, поэтому функция hasProperty() его не видит, а isProperty() – видит.
isPropertyAnimatable()
isPropertyAnimatable <obj> <string_or_name>
Функция возвращает true, если указанное свойство существует и оно может быть анимировано.
-- создаём сферу
s = Sphere()
-- распечатываем список всех свойств сферы
-- с указанием возможности анимирования
for i in getPropNames s do format "% Property: % - Animatable: %\n"s.name i (isPropertyAnimatable s i)
Sphere01 Property: #smooth - Animatable: true
Sphere01 Property: #radius - Animatable: true
Sphere01 Property: #mapCoords - Animatable: false
Sphere01 Property: #segs - Animatable: true
Sphere01 Property: #slice - Animatable: false
Sphere01 Property: #hemisphere - Animatable: true
Sphere01 Property: #sliceFrom - Animatable: true
Sphere01 Property: #sliceTo - Animatable: true
Sphere01 Property: #chop - Animatable: false
Sphere01 Property: #recenter - Animatable: false
OK
Интроспекция интерфейсов
Если мы создадим объект класса Box и посмотрим список свойств через showClass() и showProperties(), то обнаружим, что набор свойств отличается:
b = box()
$Box:Box01 @ [0.000000,0.000000,0.000000]
showClass "Box.*"
Box : GeometryClass {10,0}
.height : float
.length : float
.lengthsegs : integer
.width : float
.widthsegs : integer
.mapcoords : boolean
.heightsegs : integer
OK
showProperties b
.height : float
.length : float
.lengthsegs : integer
.width : float
.widthsegs : integer
.mapcoords : boolean
.heightsegs : integer
.realWorldMapSize : boolean
false
В свойствах объекта (экземпляра класса) появилось свойство realWorldMapSize. Оно было добавлено интерфейсом realWorldMapSizeInterface. Механизм интерфейсов в MaxScript, по сути, реализует идею множественного наследования. Например, упомянутый интерфейс имеют все стандартные геометрические примитивы и нектороые модификаторы.
Эта область очень плохо документирована в системе помощи по MaxScript. Если не вдаваться в подробности архитектуры 3ds Max, то интерфейсы, с точки зрения MaxScript, являются классами, в которые выделена некая функциональность. Таким образом, каждый класс может иметь не только свои свойства и методы, но и интерфейсы с их свойствами и методами, а также унаследованные от родительских классов свойства, методы и интерфейсы.
Класс Box имеет интерфейс realWorldMapSizeInterface. Класс Box является потомком классов (по иерархии наследования) GeometryClass, node, MAXWrapper, Value. Из этих родительских классов только у класса node имеется нескольких интерфейсов. Таким образом, при создании экземпляра класса Box, объект наследует как свойства, так и интерфейсы класса Box, и всех его родительских классов.
Надо отметить, что функция showProperties() работает только с последним уровнем наследования. Например, экземпляр класса Box имеет интерфейс INodeLayerProperties, унаследованный от класса node. Этот интерфейс, среди прочих, имеет свойство displayByLayer. Следующий код срабатывает без ошибок:
b.INodeLayerProperties.displayByLayer
false
Этот пример также показывает, что можно обратиться к любому свойству любого интерфейса (который имеет объект), используя в иерархической dot-нотации имя интерфейса. То же действие в большинстве случаев может быть выполнено проще:
b.displayByLayer
false
Такой метод сработает, если имя свойства интерфейса не повторяется среди имён свойств объекта и/или среди имён свойств других интерфейсов.
showInterfaces() и showInterface()
Перечень интерфейсов объекта можно получить с помощью функции showInterfaces():
showInterfaces b
Interface: realWorldMapSizeInterface
Properties:
.realWorldMapSize : boolean : Read|Write
Methods:
Actions:
OK
showInterfaces() {<maxwrapper> | <maxclass> | node} [ to:<stream> ]
В качестве параметра функция принимает класс или объект, унаследованный от класса MAXWrapper. Как и showProperties(), функция showInterfaces() работает только с последним уровнем наследования.
showInterface <interface> [ to:<stream> ]
Функция showInterface() в качестве аргумента принимает интерфейс. Выдача аналогична функции showInterfaces() для одного интерфейса:
showInterface b.realWorldMapSizeInterface
Interface: realWorldMapSizeInterface
Properties:
.realWorldMapSize : boolean : Read|Write
Methods:
Actions:
OK
getInterfaces() и getInterface()
getInterfaces {<MAXWrapper> | <MAXClass>}
Для того, что бы получить все интерфейсы класса или экземрляра класса (объекта), унаследованного от класса MAXWrapper, надо использовать функцию getInterfaces(), которая возвращает массив со ссылками на все интерфейсы для аргумента.
Например, посмотреть полный перечень интерфейсов можно с помощью следующей функции:
fn shInterf obj = (
obj_interfaces = getInterfaces obj
for i in obj_interfaces do (
showInterface i
format "\n"
)
)
Надо отметить, что эта простая функция также не обходит всё дерево наследования: она выдаёт информацию только по основной иерархии классов. Если в составе интерфейса имеется другой интерфейс, то будет просто отображена ссылка на этот интерфейс без раскрытия его деталей. Можно написать функцию, которая бы рекурсивно обходила всё дерево наследования, но выдача слишком большого объёма информации – тоже не всегда хорошо. Поэтому лучше исследовать заинтересовавший интерфейс отдельно, не забывая о том, что к его свойствам и методам также можно обратиться напрямую через объект, используя dot-нотацию (т.е. через точку).
getInterface {<MAXWrapper> | <MAXClass>} <InterfaceNameString>
С помощью функции getInterface() можно получить ссылку на конкретный интерфейс класса или экземпляра класса, унаследованного от класса MAXWrapper.
Когда мы выполняем выражение b = Box(), на первый взгляд кажется, что мы просто создаём бокс с семью параметрами (см. выдачу функции showClass()), которые доступны для изменения через пользовательский интерфейс 3ds Max. На самом деле создаётся достаточно сложный объект с десятками, если не с сотнями свойств и методов.
Интроспекционные методы класса Value
В MaxScript класс Value является базовым для всех остальных классов. Даже такие типы данных как числа и строки являются классами и наследуются от класса Value. Поэтому не удивительно, что именно он имеет интроспективные методы, которые наследуются всеми остальными классами, а, следовательно, и объектами.
classOf()
Функция возвращает класс переданного ей аргумента. Результатом является именно класс, а не строка с названием класса. Поэтому вполне допустим следующий код:
b1 = Box pos:[50, 50, 0]
$Box:Box01 @[50.000000,50.000000,0.000000]
b2 = (classOf b1)()
$Box:Box02 @ [0.000000,0.000000,0.000000]
Во втором выражении используется конструктор класса Box по умолчанию.
Для объектов, унаследованных от класса node, функция возвращает тот класс, который имеет объект после применения всех модификаторов из своего стека модификаторов:
b = Box()
$Box:Box01 @ [0.000000,0.000000,0.000000]
classOf b
Box
addModifier b (Twist())
OK
classOf b
Editable_mesh
Можно написать функцию, которая выводит на консоль иерархию классов для своего аргумента:
fn hierarchy arg = (
local cls = arg
local tabspace = " "
local tab = ""
do (
cls = classOf cls
format "%%" tab cls
tab += tabspace
) while cls != Value
Ok
)
Ниже приведены два вызова функции hierarchy() для класса Box и для экземпляра объекта класса Box.
hierarchy Box
GeometryClass
node
MAXWrapper
Value
OK
hierarchy $Box01
Box
GeometryClass
node
MAXWrapper
Value
OK
superClassOf()
Возвращает суперкласс для аргумента. Т.е. класс, от которого унаследован класс аргумента. Выражение superClassOf arg фактически эквивалентно выражению classOf (classOf arg).
isKindOf()
isKindOf <value> <class>
Функция возвращает true, если проверяемый объект является экземпляром указанного класса или экземпляром класса, унаследованного от указанного.
С помощью этой функции удобно проверять принадлежность объекта к определённому классу. Например, необходимо сделать некую операцию со всеми выделенными камерами в сцене. В простейшем случае задача решается тривиально. Но если допустить, что могут быть выделены не только камеры, и в сцене имеются камеры разных типов, то решение становится более сложным. Эту задачу можно решать и с помощью функции classOf(), но в этом случае будет проверяться соответствие только конкретному классу и надо делать несколько проверок. Проще использовать функцию isKindOf():
for obj in selection do (
if isKindOf obj camera do (
-- делаем операцию с объектом камера
-- например распечатываем имя объекта
print obj.name
)
)
Здесь в условии проверяется принадлежность объекта к классу camera, или к классу, унаследованному от класса camera. Все типы камер в 3ds Max должны быть унаследованы от класса camera. Это касается как встроенных типов (Free camera и Target camera), так и типов камер, добавляемых плагинами третьих производителей (например VRayPhysicalCamera). Таким образом, операция будет производиться с камерами и только с камерами.
Функции проверки типов
isStructDef <value>
Возвращает true, если аргумент – определение структуры.
isStruct <value>
Возвращает true, если аргумент – экземпляр структуры.
isController <value>
Возвращает true, если аргумент – контроллер.
isMSPlugin <value>
Возвращает true, если аргумент – плагин, написанный на MAXScript.
isMSPluginClass <value>
Возвращает true, если аргумент – класс плагина, написанного на MAXScript.
isMSCustAttrib <value>
Возвращает true, если аргумент – пользовательский атрибут MaxScript (MAXScript Custom Attribute).
Замечание. Поскольку пользовательские атрибуты являются специальным случаем плагина, написанного на MaxScript, функция isMSPlugin() также будет возвращать true.
isMSCustAttribClass <value>
Возвращает true, если аргумент – класс пользовательского атрибута MaxScript.
Интроспекционные свойства и методы класса MAXWrapper
Класс MAXWrapper является базовым классом для всех классов, которые представляют объекты 3ds Max, такие как геометрические объекты, модификаторы, материалы и т.п. Экземпляры классов, унаследованных от MAXWrapper, содержат ссылки на связанные объекты 3ds Max, что позволяет им следить за объектом. Это позволяет MaxScript знать, когда объект подвергся трансформации, удалён пользователем или были изменены его свойства.
Как можно было заметить, многие интроспекционные функции принимают в качестве аргумента объекты типа MAXWrapper.
Класс MAXWrapper имеет несколько свойств, относящихся к интроспекции, но они достаточно специфичны и редко используются при практической работе с MaxScript.
Свойства
Следующие свойства класса MAXWrapper или экземпляров класса дают доступ к категории плагина (Standard Primitives, Extended Primitives или Particle Systems). Эти свойства доступны только для чтения.
<MAXWrapperobject>.category
<maxclass>.category
<maxsuperclass>.categories
Свойство category возвращают значение типа Name. Оно доступно как для классов объектов (например Box, Line или Fog), так и для любых объектов (экземпляров классов, унаследованных от MAXWrapper). Для узлов сцены категория обычно соответствует одному из названий в выпадающем списке панели Create. Это такие категории как #Standard_Primitives, #Compound_Objects или #Particles_Only.
Для модификаторов категория определяется тем, какой группе модификаторов он принадлежит в списках Configure Button Sets / Modifiers list. Это такие категории как #MAX_STANDARD, #MAX_EDIT или #MAX_SURFACE.
Для текстур категория определяется группой в окне Material/Map Browser. Это такие категории как #2D (2D maps), #3D (3D maps) или #COMP (Compositors).
Свойство categories доступно для любых объектов суперклассов 3ds Max, таких как Shape, GeometryClass, Helper, SpacewarpObject, TextureMap или Modifier. Оно содержит список доступных категорий для этого суперкласса. Возвращаемое значение - массив значений типа Name.
<maxsuperclass>.classes
Возвращает массив значений MAXClass (т.е. классов), которые принадлежат данному суперклассу.
<MAXWrapperobject>.classID
<maxclass>.classID
Свойство содержит внутренний идентификатор (ID) класса 3ds Max для классов и объектов, унаследованных от класса MAXWrapper. Представляет собой массив из двух целых чисел, однозначно идентифицирующих класс.
<MAXWrapperObject>.superClassID
<maxclass>.superClassID
Свойство содержит внутренний идентификатор (ID) суперкласса 3ds Max для классов и объектов.
Полный перечень свойств класса MAXWrapper - см. систему помощи по MaxScript.
Методы
getClassName <MAXWrapper>
Возвращает имя класса аргумента в виде строки.
Зависимости
В 3ds Max многие объекты зависят от других объектов. Материал зависит от своих карт, геометрический объект зависит от базового объекта и т.п. Внутренний механизм 3ds Max определяет отношения зависимостей между объектами.
В MaxScript имеется структура refs, которая определяет четыре функции для работы с зависимостями:
refs
#Struct:refs(
DependencyLoopTest:<fn>,
dependents:<fn>,
dependsOn:<fn>,
dependentNodes:<fn>)
refs.dependents()
refs.dependents <MAXWrapper_object>[ immediateOnly:<boolean>]
Возвращает массив других объектов (точнее, ссылок на эти объекты) 3ds Max, которые зависят от указанного объекта (все объекты являются потомками класса MAXWrapper). Если необязательный аргумент immediateOnly равен true, то будут возвращены только объекты, зависящие непосредственно.
Например, следующее выражение показывает, что диффузная карта материала, назначенного объекту foo, используется в материалах Material_#1, Material_#2, Material_#3 и в текстурной карте Map_#2:Noise.
refs.dependents $foo.material.diffuseMap
#(Material_#3, Material_#2, Map_#2:Noise, Material_#1)
Другой пример:
-- создать сферу
theSphere=sphere ()
$Sphere:Sphere01 @ [0.000000,0.000000,0.000000]
-- создать конус
theCone=cone radius1:0 radius2:20
$Cone:Cone01 @ [0.000000,0.000000,0.000000]
-- создать спираль
theHelix=helix height:100 pos:[100,100,0]
$Helix:Helix01 @ [100.000000,100.000000,0.000000]
-- назначить theSphere целью для theCone, которому
-- автоматически назначается контроллер lookat
theCone.target=theSphere
$Sphere:Sphere01 @ [0.000000,0.000000,0.000000]
-- назначить theSphere контроллер пути по сплайну theHelix
theSphere.position.controller=path path:theHelix
Controller:Path_Constraint
-- объекты, зависящие от сферы
refs.dependents theSphere
#(Controller:Look_At, $Cone:Cone01 @ [0.000000,0.000000,0.000000])
-- объекты, непосредственно зависящие от сферы
refs.dependents theSphere immediateOnly:true
#(Controller:Look_At)
-- объекты, зависящие от конуса
refs.dependents theCone
#()
-- объекты, зависящие от спирали
refs.dependents theHelix
#(ReferenceTarget:ParamBlock2, Controller:Path_Constraint, Controller:Position_Rotation_Scale, $Sphere:Sphere01 @ [135.000000,100.000000,0.000000], Controller:Look_At, $Cone:Cone01 @ [0.000000,0.000000,0.000000])
refs.dependsOn()
refs.dependsOn <MAXWrapper_object>
Возвращает массив объектов, от которых аргумент зависит непосредственно.
b = Box()
$Box:Box01 @ [0.000000,0.000000,0.000000]
refs.dependsOn b
#(Controller:Position_Rotation_Scale, Box)
Пример показывает, что узел сцены (в данном случае бокс) непосредственно зависит от контроллера трансформаций и от своего базового объекта. Также узел косвенно зависит от объектов, от которых зависят эти объекты (контроллеры положения, поворота и масштаба для контроллера трансформаций).
refs.dependencyLoopTest()
refs.dependencyLoopTest <MAXWrapper_object> <MAXWrapper_object>
Возвращает true, если два аргумента функции будут создавать циклическую зависимость.
a = box pos:[-100,0,0]
$Box:Box01 @ [-100.000000,0.000000,0.000000]
b = box pos:[100,0,0]
$Box:Box02 @ [100.000000,0.000000,0.000000]
a.material = standardMaterial()
Standardmaterial:Standard
refs.dependencyLoopTest a b
false
refs.dependencyLoopTest a a.material
true
refs.dependentNodes()
refs.dependentNodes
Возвращает массив узлов сцены, которые зависят от аргумента.
Продолжая пример из refs.dependents(), получим
refs.dependentNodes theCone
#()
refs.dependentNodes theSphere
#($Cone:Cone01 @ [0.000000,0.000000,0.000000])
refs.dependentNodes theHelix
#($Sphere:Sphere01 @ [135.000000,100.000000,0.000000])
Интроспекция функций, определённых пользователем
Функции в MaxScriptтакже являются объектами. Определение функции является выражением, результатом выполнения которого будет объект типа MAXScriptFunction. По умолчанию именем этого объекта будет имя определяемой функции. Однако выражение определения функции возвращает значение, которое может быть присвоено другой переменной:
fp1 = fn func1 a b = ( a - b )
func1()
func1 5 2
3
fp1 5 2
3
В этом случае переменные func1 и fp1 ссылаются на один объект, но сами переменные являются разными и независимыми друг от друга.
Если рассматривать классическую схему в стиле C, то в вышеприведённом примере func1 является именем функции, а fp1 – указателем на функцию. И, подразумевается, что func1 – это константа, а fp1 – переменная. Однако в MaxScript эти оба идентификатора именуют ссылки на объект и являются идентификаторами переменных. Таким образом, переменную func1 можно в любой момент переопределить, назначив ей любой другой объект (функцию, число, etc.), а к функции обращаться через переменную fp1. Если переменная fp1 будет также переопределена, т.е. на объект-функцию не будет ссылаться ни одна переменная, то объект-функция будет утилизирован сборщиком мусора.
showSource()
Функция showSource() предназначена для поиска определения функции, ссылка на которую передана ей в качестве аргумента. Она открывает файл с определением функции в окне редактора скриптов и позиционирует курсор на начало определения функции. Это полезно, когда используется много функций и/или много исходных файлов, загружаемых через fileIn() или команду Run Script.
Заключение
В этой статье я постарался собрать вместе сведения о наиболее важных и употребимых (с моей точки зрения) функциях, свойствах и методах, имеющих отношение к интроспекции в MaxScript. Возможно, что за рамками этой статьи остались какие-то важные и интересные моменты. Возможно, что каким-то вещам я не уделил достаточно внимания. MaxScript Reference вам в помощь – там есть почти всё.
© Black Sphinx, 2013. All rights reserved.