Список примеров

Crowd и Delegates (толпа и делегаты). Примеры программирования

Содержание

Введение

Объект Crowd (толпа) управляет поведением объектов делегатов Delegates. По своей сути Crowd является многоагентной системой. Агентами являются делегаты, преследующие одну или несколько целей. Делегаты могут входить в разные команды. Целеполагание достигается за счет назначения делегатам или командам делегатов одного или нескольких видов поведений. Возможные виды поведений перечислены на рис. 1.

Виды поведений делегатов

Рис. 1. Виды поведений делегатов

Русские названия видов поведений следующие: избегать, ориентироваться, придерживаться пути, отталкивать, под управлением программы, искать, использовать пространственные возмущения, изменять скорость, прибытие на объект, следование за объектом, отталкивание от стены, поиск стены и блуждание.
Интерактивное употребление толпы рассмотрим на примере управления двумя делегатами, представляемыми в сцене в виде красных пирамид (рис. 2).

Толпа с делегатами

Рис. 2. Толпа с двумя делегатами

Также в сцену введены конусы и полусфера, покоящиеся на плоскости.
Назначим делегатам 2 следующих вида поведений: Avoid – избегать конусов и Seek – искать полусферу и воспроизведем анимацию. Предварительно, однако, ассоциируем с каждым делегатом по одному объекту, например куб и сферу. Именно они и будут отображаться в процессе анимации. Сами делегаты (пирамиды) могут быть скрыты.
Последовательность действий следующая:

  1. Создать сцену, содержащую представленные на рисунке объекты: плоскость, конусы и полусферу.
  2. Ввести в сцену толпу Crowd. Для этого на вкладке Create командного окна нажать на кнопку Helpers и затем на кнопку Crowd.
  3. Ввести в сцену двух делегатов Delegate, нажав на одноименную кнопку на вкладке Helpers, и приподнять их над плоскостью чуть выше полусферы.
  4. Выбрать значок толпы и перейти на вкладку Modify в режим редактирования объекта.
  5. В списке Setup нажать на кнопку Behavior Assignments (выбор поведений) и в появившемся диалоге создать команду Team0 (кнопка New Team), включив в нее обоих делегатов (рис. 3).

    Команда из двух делегатов

    Рис. 3. Создана команда из двух делегатов

  6. Назначить в том же диалоге, нажав на кнопку New Behavior, для созданной команды поведения Avoid и Seek (избегать и искать).

    Команде назначены поведения Avoid и Seek

    Рис. 4. Команде Team0 назначены поведения Avoid и Seek

  7. Выбрать, находясь в режиме редактирования толпы, в списке Behaviors поведение Avoid и, нажав в списке Avoid Behavior на кнопку Multiple Selection, выбрать в открывшемся диалоге все конусы. Значение параметра Look Ahead (просмотр вперед) установить, например, равным 10.
  8. Выбрать далее в списке Behaviors поведение Seek и, нажав в списке Seek Behavior на кнопку None, указать мышкой на полусферу. Таким образом, члены команды Team0 будут избегать столкновений с конусами, стараясь найти полусферу.
  9. Ввести в сцену куб и сферу, сопоставимые по размеру с образами делегатов.
  10. В режиме редактирования толпы в списке Setup нажать на кнопку Object/Delegate Association и в появившемся диалоге заполнить в соответствии с рисунком оба списка, выделить в них все элементы (посредством клавиши Ctrl и мыши) и нажать последовательно на кнопки Align Objects with Delegates и Link Objects to Delegates (рис. 5).

    Делегаты и объекты

    Рис. 5. Ассоциация делегатов и объектов


    При нажатии на первую кнопку базовые точки соответствующих объектов и делегатов совмещаются. Вторая кнопка предназначена для связывания объектов и делегатов путем создания иерархии "Делегат – Объект". Структура подчиненности отображается, например, в окне выбора объектов по имени. Сцена после выполнения перечисленных выше действий может иметь приведенный на рис. 6 вид.

    Ассоциирование делегатов со сферой и кубом

    Рис. 6. После ассоциирования делегатов со сферой и кубом

  11. Находясь в режиме редактирования толпы, в списке Solve (решить) установить, например, следующие значения параметров (рис. 7):

    Задание параметров решателя Crowd

    Рис. 7. Параметры решателя

  12. Нажать на кнопку решить Solve и воспроизвести анимацию.
  13. Уменьшить, например, в списке Avoid Behavior значение параметра Look Ahead (просмотр вперед) в 2 раза, обновить решение и воспроизвести анимацию.

В рассмотренном примере создание команды Team0 необязательно. Достаточно назначить подходящие виды поведений каждому делегату (рис. 8).

Назначение поведений делегатам

Рис. 8. Поведения назначаются делегатам

Если необходимо, чтобы делегат (связанный с ним объект) не покидал найденную полусферу, то следует добавить поведение Surface Arrive с параметром Distance (расстояние), равным, например, 5 (задается в списке прибытие Arrival).

Программирование поведений Avoid, Seek и Surface Arrive

В примере создаются четыре сферических делегата, которые разбиваются на две команды.
Каждой команде назначается поведение Avoid, в качестве обходимых препятствий назначаются конусы (в сцене пять конусов).
При h = 1 в качестве второго поведения назначается Seek, а при ином значении h – поведение Surface Arrive. Объекты, которые нужно находить или к которым нужно прибыть, – это красный и черный кубы.

h = 1
aRng = 300
delete $*
sliderTime = 0f
viewport.ActiveViewport = 4
max vpt persp user
viewport.SetGridVisibility 4 false
animationRange = interval 0f aRng
timeConfiguration.PlaybackLoop = false
vp0 = 150
vp = 0.75 * vp0 / 2
vp2 = 0.5 * vp0
zd = 30
cn = cone radius1:8 radius2:4 height:30 wireColor: [135, 110, 8]
bx = box length:20 width:20 height:20 wireColor:red pos:[-vp, -vp, 0]
bx2 = box length:20 width:20 height:20 wireColor:black pos:[vp, -vp, 0]
pln = plane length:vp0 width:vp0
w = 10
dlg = delegate width:w depth:w height:w pos:[vp2, w, zd] wireColor:red
dlg2 = delegate width:w depth:w height:w pos:[vp2, -w, zd] wireColor:green
dlg3 = delegate width:w depth:w height:w pos:[-vp2, w, zd] wireColor:blue
dlg4 = delegate width:w depth:w height:w pos:[-vp2, -w, zd] wireColor:black
sp = sphere radius:5 wireColor:dlg.WireColor pos:dlg.pos parent:dlg
sp2 = sphere radius:5 wireColor:dlg2.WireColor pos:dlg2.pos parent:dlg2
sp3 = sphere radius:5 wireColor:dlg3.WireColor pos:dlg3.pos parent:dlg3
sp4 = sphere radius:5 wireColor:dlg4.WireColor pos:dlg4.pos parent:dlg4
arrDlgs = #(dlg, dlg2)
arrDlgs2 = #(dlg3, dlg4)
avd = avoidBehavior()  -- showHardRadius:on
-- Создаем поведения делегатов (Seek или Surface Arrive)
if h == 1 then (
 s = seekBehavior targets:#(bx) name:"sk"
 s2 = seekBehavior targets:#(bx2) name:"sk2"
)
else (
 s = surfaceArriveBehavior surfaces:#(bx) name:"sArv" \
  disableAfterArriving:off facing:on
 s2 = surfaceArriveBehavior surfaces:#(bx2) name:"sArv2" \
  disableAfterArriving:off facing:on
)
arrBh = #(avd, s, s2)
-- Распределяем делегатов по двум командам
tm = crowdTeam members:arrDlgs name:"tm"
tm2 = crowdTeam members:arrDlgs2 name:"tm2"
crd = crowd behaviors:arrBh teams:#(tm, tm2) solveEnd:aRng \
 deleteKeys:on update:off pos:[0, 0, 70] isSelected:off
-- Тиражируем препятствия (конусы), используя объект Scatter,
-- доступ к которому обеспечивает одноименное свойство
crdSct = crd.Scatter
crdSct.CloneObject = cn
crdSct.NumClones = 4
crdSct.CloneType = 0    -- Copy
crdSct.PositionSpace = 3   -- On Surface
crdSct.PositionObject = pln
crowds.GenClones crd
crowds.GenLocations crd
cn.Pos = [0, 0, 0]
arrObstcls = for ob in geometry where substring ob.Name 1 4 == "Cone" collect ob
-- Задаем препятствия поведения Avoid
avd.Obstacles = arrObstcls
-- Назначаем поведения командам
crdSsgn = crowdAssignment team:tm behavior:avd
crdSsgn2 = crowdAssignment team:tm behavior:s
crdSsgn3 = crowdAssignment team:tm2 behavior:avd
crdSsgn4 = crowdAssignment team:tm2 behavior:s2
crd.Assignments = #(crdSsgn, crdSsgn2, crdSsgn3, crdSsgn4)
-- Ассоциируем сферы с делегатами
crd.ObjAssoc.Objects = #(sp, sp2, sp3, sp4)
crd.ObjAssoc.Delegates = #(dlg, dlg2, dlg3, dlg4)
-- Запускаем решатель
crowds.Solve crd
hide #(pln)
max tool zoomExtents
playAnimation()

Один из кадров анимации приведен на рис. 9.

Толпа с четырьмя делегатами

Рис. 9. Толпа из четырех делегатов

Программирование поведений Avoid и Path Follow

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

Толпа с четырьмя делегатами и ограничение пути

Рис. 10. Толпа с четырьмя делегатами, пытающимися двигаться вдоль сплайна

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

aRng = 300
delete $*
sliderTime = 0f
viewport.ActiveViewport = 4
max vpt persp user
viewport.SetGridVisibility 4 false
animationRange = interval 0f aRng
timeConfiguration.PlaybackLoop = false
-- Создаем путь для делегатов
pth = splineShape pos:[0, 0, 0] wireColor:black \
 render_renderable:on render_thickness:2.0
x = 100
dx = 15
y = -10
arrP = #([-x, y, 0], [-80, -50, 0], [-60, -115, 0], [60, -115, 0], [80, -40, 0], [x, y, 0])
addNewSpline pth
for k = 1 to arrP.Count do addKnot pth 1 #smooth #curve arrP[k]
updateShape pth
w = 10
-- Помещаем два делегата в начальной и два делегата конечной точках пути
dlg = delegate width:w depth:w height:w pos:[-x - dx, y, 0] wireColor:red
dlg2 = delegate width:w depth:w height:w pos:[-x + dx, y, 0] wireColor:green
dlg3 = delegate width:w depth:w height:w pos:[x - dx, y, 0] wireColor:blue
dlg4 = delegate width:w depth:w height:w pos:[x + dx, y, 0] wireColor:black
arrDlgs = #(dlg, dlg2, dlg3, dlg4)
rotate arrDlgs 180 [0, 0, 1]
sp = sphere radius:5 wireColor:dlg.WireColor pos:dlg.pos parent:dlg
sp2 = sphere radius:5 wireColor:dlg2.WireColor pos:dlg2.pos parent:dlg2
sp3 = sphere radius:5 wireColor:dlg3.WireColor pos:dlg3.pos parent:dlg3
sp4 = sphere radius:5 wireColor:dlg4.WireColor pos:dlg4.pos parent:dlg4
-- Задаем поведение делегатов
pthF = pathFollowBehavior path:pth start:2 direction:0 name:"pth"
pthF2 = pathFollowBehavior path:pth start:1 direction:1 name:"pth2"
avd = avoidBehavior showHardRadius:off
-- Препятствуем столкновениям между делегатами
avd.Obstacles = arrDlgs
arrBh = #(pthF, pthF2, avd)
-- Создаем команды и назначаем виды поведений
tm = crowdTeam members:#(dlg, dlg2) name:"tm"
tm2 = crowdTeam members:#(dlg3, dlg4) name:"tm2"
tm3 = crowdTeam members:arrDlgs name:"tm3"
crd = crowd behaviors:arrBh teams:#(tm, tm2, tm3) solveEnd:aRng \
 deleteKeys:on update:off pos:[0, 0, 70] \
 isSelected:on
crdSsgn = crowdAssignment team:tm behavior:pthF
crdSsgn2 = crowdAssignment team:tm2 behavior:pthF2
crdSsgn3 = crowdAssignment team:tm3 behavior:avd active:on
crd.Assignments = #(crdSsgn, crdSsgn2, crdSsgn3)
-- Ассоциируем сферы с делегатами
crd.ObjAssoc.Objects = #(sp, sp2, sp3, sp4)
crd.ObjAssoc.Delegates = #(dlg, dlg2, dlg4, dlg4)
-- Запускаем решатель
crowds.Solve crd
playAnimation()

Один кадр анимации приведен на рис. 10.

Толпа двуногих. Crowd and Bipeds

С делегатами можно связать двуногих. Обязательным условием такой связи является общий поток движений (Motion Flow) у всех двуногих, ассоциируемых с делегатами.
В примере делегаты и связанные с ними двуногие обладают поведением Wander (блуждать). Двуногих хранит массив arrBs, а делегатов – массив arrDlgs. Двуногие имеют общий поток движений (Shared Motion Flow).
Массив arrDlgs создается функцией mkBp до запуска кода, отвечающего за создание и расчет толпы. Функция и все предшествующие ее запуску операции рассматриваются ниже.

aRng = 500
-- Удаляем всех помощников
for ob in helpers do
 if classOf ob == Crowd or classOf ob == Delegate do delete ob
sliderTime = 0f
viewport.ActiveViewport = 4
max vpt persp user
viewport.SetGridVisibility 4 false
animationRange = interval 0f aRng
timeConfiguration.PlaybackLoop = false
x = -45
dx = 30
w = 10
-- Создаем 4-х делегатов и определяем значения их свойств
dlg = delegate width:w depth:(2 * w) height:w pos:[x, 0, 0] wireColor:red
dlg2 = delegate width:w depth:(2 * w) height:w pos:[x + dx, 0, 0] wireColor:green
dlg3 = delegate width:w depth:(2 * w) height:w pos:[x + 2 * dx, 0, 0] wireColor:blue
dlg4 = delegate width:w depth:(2 * w) height:w pos:[x + 3 * dx, 0, 0] wireColor:black
arrDlgs = #(dlg, dlg2, dlg3, dlg4)
for dlg in arrDlgs do (
  dlg.StartClip = 1
  dlg.MaxAccel = random 0.1 0.3
  dlg.RandID = random 1 10
  dlg.MaxBank = random 20.0 30.0
  dlg.InclineDecelAngle = random 10.0 20.0
  dlg.DeclineAccelAngle = random 10.0 20.0
  dlg.AverageSpeed = random 1.5 3.5
)
-- Создаем поведение Wander
wndB = wanderBehavior()
arrBh = #(wndB)
-- Создаем команду из делегатов и толпу
tm = crowdTeam members:arrDlgs name:"tm"
crd = crowd behaviors:arrBh teams:#(tm) solveEnd:aRng \
 deleteKeys:on update:off pos:[0, 0, 70]  isSelected:on
-- Назначаем поведение команде
crdSsgn = crowdAssignment team:tm behavior:wndB
crd.Assignments = #(crdSsgn)
max modify mode
msg = "Array of bipeds not ready yet"
if arrBs == undefined then
 messageBox(msg)
else (
 if arrBs.Count == 0 then
  ntBp = true
 else (
  ntBp = false
  for k = 1 to arrBs.Count do (
   b = arrBs[k]
   if classOf b == Biped_Object then (
    dlg = arrDlgs[k]
    dlg.UseBiped = on
    dlg.Biped = b
   )
   else
    ntBp = true
  )
 )
 if ntBp then
  messageBox(msg)
 else
  crd.SolveForBipeds = on
)
max tool zoomExtents
crowds.Solve crd
playAnimation()

Один кадр анимации приведен на рис. 11.

Анимация толпы Biped

Рис. 11. Толпа двуногих

Поток движений создадим на базе трех видов движений: вперед, влево и вправо. Для создания этих видов движений употребим следующий код:

-- Удаляем всех двуногих
delete $bip*
-- Массив углов поворотов в режиме Foot Step
arrBnd = #(0, 20, -20)
-- Число шагов в каждом виде движений
arrNs = #(8, 8, 8)
for k = 1 to arrBnd.Count do (
 b = biped.CreateNew 60 -90 [0, 0, 60]
 select b
 bc = b.Controller
 max motion mode
 walk = biped.GetMultipleFSParams #walk
 walk.NumFootsteps = arrNs[k]
 bc.FootStepMode = true
 biped.AddMultipleFootprints bc walk
 biped.BendFootprints bc arrBnd[k]
 biped.NewFootprintKeys bc
 bc.FootStepMode = false
 -- Путь может быть иным
 f = "G:\\bp" + (k as string) + ".bip"
 biped.SaveBipFile bc f
)

Результат приведен на рис.12; виды движений сохранены в 3-х BIP-файлах.

Создание сценария движения Biped

Рис. 12. Три вида движений

Поток движений на базе созданных выше видов движений создаст следующий код:

delete $bip*
b = biped.CreateNew 60 -90 [0, 0, 60]
select b
bc = b.Controller
max motion mode
bc.MotionMode = true
bmf = bc.MotionFlow
snp = newSnippet bmf "G:\\b1.bip" [100, 30] redraw:true load:true
snp2 = newSnippet bmf "G:\\b2.bip" [50, 120] redraw:true load:true
snp3 = newSnippet bmf "G:\\b3.bip" [150, 120] redraw:true load:true
snp.ClipName = "bp"
snp2.ClipName = "bp2"
snp3.ClipName = "bp3"
addTransition snp snp2 true
addTransition snp2 snp3 true
addTransition snp3 snp true
addTransition snp snp true

Граф, отвечающий созданному потоку движений, приведен на рис. 13 (интерактивно создается для выбранного двуногого в режиме Motion Flow Mode).

Поток движений

Рис. 13. Поток из трех видов движений

Находясь в режиме Motion Flow Mode, откроем форму редактирования графа потока движений (кнопка Show Graph), выделим клип bp и нажмем на кнопку Select Random Start Clips. После этого рис. 13 преобразуется к следующему виду (рис. 14):

Очередность выполнения клипов

Рис. 14. Первым будет исполнен клип bp

Сохраним поток в файле crwd.mfe, нажав в секции Motion Flow на кнопку Save File или выполнив метод

saveMoFlowFile bmf "G:/crwd.mfe"

Создание массива arrBs обеспечит следующий код:

delete $bip*
n = 4
x = -75
dx = 30
arrBs = #()
for k = 1 to n do (
 x += dx
 b = biped.CreateNew 60 -90 [x, 0, 60]
 append arrBs b
)
b = arrBs[1]
select b
bc = b.Controller
max motion mode
bc.MotionMode = true
messageBox("Define Shared Motion Flow now")

Откроем диалог Shared Motion Flow, нажав на одноименную кнопку секции Motion Flow (рис .15).

Поток движений для четырех Biped

Рис. 15. Четыре двуногих обладают одним потоком движений

Нажмем на кнопку New, затем на кнопку Load .mfe и загрузим заготовленный ранее MFE-файл, далее нажмем на кнопку Add и выберем четырех двуногих – OK. Сформируем и рассчитаем толпу двуногих, выберем любого и посмотрим созданный поток скриптов Script Flow (рис. 16).

Скрипт, созданный решателем толпы

Рис. 16. Поток скриптов двуногих, созданный решателем толпы

Источники

  1. Autodesk® 3ds Max® 2009 MAXScript Reference.
  2. Бартеньев О. В. Программирование модификаторов 3ds Max. Учебно-справочное пособие. – М.:Физматкнига, 2009. – 341 с.

Список примеров

Рейтинг@Mail.ru