Учебник по PhysX Wrapper для Blitz3D
Итак, по просьбам трудящихся я таки решился написать малюсенький туторчик по врапперу товарища Рендера
( www.xors3d.com - офф. сайт, враппер можно скачать оттуда http://forum.boolean.name/showthread.php?t=2734 - тема на Булке (похоже, сдохла, но ссылочка пускай будет) http://blitz.pp.ru/forum/showthread.php?s=&threadid=10 - тема на Блицпп ) Убедительная просьба! Если у вас есть вопросы или пожелания, то задавайте их, пожалуйста, в соседней теме, чтобы не прерывать здесь ход повествования, а я постараюсь по мере возможностей дополнять и изменять некоторые участки этого тутора дабы сделать его более качественным. Лучше указывайте ссылку на номер поста, по которому есть вопрос или который надо дополнить чтобы я смог оперативней среагировать Буду всегда в аттачи класть архивы с полным исходным кодом примеров, также нахожу для себя возможным публиковать картинки небольшого размера и веса чтобы наглядно показать суть происходящего. Для тех, кто в танке. Что такое реал-таймовая физика в играх? Что такое PhysX Wrapper? Думаю, вам уже известно, что такое реал-таймовая физика. Помните, как реалистично в Half-Life 2 отскакивали ящики от метких выстрелов, или как здорово труп валился вниз по лестнице, подчиняясь всем законам Ньютона? Помните, как в Crysis'е можно было схватить ящик и засадить им в супостатов, при этом сам ящик разбивался, а враги без сознания отлетали к дальней стенке? Всё это стало возможным относительно недавно, когда компьютеры стали достаточно мощными, чтобы обрабатывать подобные вычисления без огромных тормозов. Сначала такие игры можно было пересчитать по пальцам и они достаточно ограниченно использовали физику. Можно было потолкать ящики при помощи выстрелов, скинуть их в пропасть, но не более того. Позже трупы врагов стали тоже физическими и теперь перестали проваливаться сквозь стены (помните в Counter-Strike 1.6 если чел сидел в углу и его пристрелить, то он откинется на спину и только ножки торчат из стены. Поначалу забавно, но ни о какой реалистичности и речи быть не могло), а стали падать в соответствии с тем окружением, где они находятся. Благодаря этому смерти каждого персонажа перестали быть похожими друг на друга. Кстати, эта технология называется "Ragdoll", как такое сделать самому я тоже обязательно раскажу. Конечно, такие штуковины сделали игры намного более реалистичными, но не повлияли на геймплей. Игрой, по-настоящему завязанной на физике, стала Half-Life 2. Всякие приколы типа "Положи 5 кирпичей на один конец качели чтобы другой конец поднялся вверх и ты смог бы по нему забраться на уступ", "Поставь ящики горкой чтобы по ним забраться выше" или "Подсунь бочки с воздухом под решётку чтобы она вспыла из-под воды" просто немыслимы без физики. А одна гравипушка чего стоит! До этого ничего подобного нигде не встречалось и она была бы невозможно без рассчёта игровой физики. С тех пор прошло досточное количество времени и теперь игровая физика стала стандартом де-факто в играх ААА-класса. Нынче игры без физики вообще - редкость, разве что-то мелкое типа головоломок, или стратегии, где игрок не принимает непосредственного участия в передвижении по уровню, так что физика там не так важна, хотя она, безусловно, встречается в играх и этого жанра. Короче, физика стала такой же неотъемлемой частью игр, как в конце бородатых девяностых стала трёхмерность, начало которой положил ещё старичок Quake. Но для чтобы написать алгоритм обработки физики, необходимо блестяще знать физику, высшую математику и т.п., что проходят далеко не в каждом ВУЗе, не говоря уже о школах, учениками которых является немалая часть нашей инди-гейдвевелоперской братии. Да и одному человеку не по силам создать почти идеальную физику с кучей возможностей, или у него уйдёт на это туча времени и сил. Поначалу так и было. В играх не было очень качественной физики. В основном это были ящики, имеющие форму параллелепипеда, да какие-нибудь доски. В принципе, это вполне удовлетворяло потребности игродела того времени. Но требования неуклонно росли и программистам приходилось всё сложнее и сложнее. А ведь им кроме физики надо ещё писать движок, логику, графику... Но зачем 10 людям делать одно и то же, 10 раз, когда можно собраться вместе и сделать это один раз, но в 10 раз лучше, да ещё и дать пользоваться другим, чтобы те не изобретали велосипед? Так уже давно произошло с трёхмерными движками. Сначала геймдевелоперы сами пытались написать себе 3Д-графику, но, когда вышел Quake, стало ясно, что гораздо проще взять уже готовый движок и делать на нём игру, чем только год потратить на создание такого движка. (Справедливости ради стоит отметить, что и до этого делались попытки использовать движок другой игры, например, Doom. Например, на его движке написана культовая в своё время игра Blood. Но Quake Engine благодаря своей универсальности пользовался бешеным успехом и произвёл настоящий бум. Игры на нём и на его модифицированных версиях писались вплоть до середины 2000-х) Вот и мы используем Blitz3D и не задумываемся о всяких матрицах трансформации, конвеере рендера и т.п. - всё это уже сделано до нас. Точно так же произошло и с физическими движками. Стоит только оформить все необходимые функции и структуры в удобный интерфейс - и ими сможет пользоваться всякий желающий. Одним из самых известных, мощных и функциональных движков стал Havoc. Именно благодаря ему мы кидались ящиками из гравипушки в Half-Life 2, благодаря ему так смачно разлетались тушки скелетов в Painkiller'е. Одно время с ним конкрурировал физический движок Karma (известен нам по играм Unreal 2, Unreal Tournament 2003-2004, Postal 2, Ведьмак и многие другие игры на движке Unreal Engibe 2.0 и не только) И вот, совсем недавно на сцену вышел физический движок нового поколения - PhysX. Он примечателен тем, что стал первым (и пока единственным) физическим движком, который ускоряется аппаратно. Сначала эта возможность была доступна тольтко владельцам специальных устройств, именуемых "Ageia PhysX Chip", который стоил как хорошая видяха и пихался в PCI-разъём. Были выпущены специальные демки, в которых демонстрировался огромный прирост произовдительности по сравнению с рассчётами на ЦП. Но, к сожалению, игр, которые поддерживали этот физ. ускоритель, было катастрофически мало и желающих его купить нашлось немного. Позже компанию Ageia купила nVIDIA и внедрила аппаратную поддержку физики в свои видеокарты начиная с серии GeForce 8xxxx. Таким образом отпала необходимость покупать отдельный чип и любой владелец современной видеокарты GeForce мог кусить все прелести аппаратного ускорения физики. Вследствие этого популярность движка возросла, теперь список игр, его поддерживающих, стал достаточно внушительным. И, наконец, nVIDIA сделала всем нам огромный подарок, разрешив юзать PhysX всем желающим бесплатно (Вроде как есть какие-то ограничения, вроде того что надо указывать, что твоя игра использует PhysX, но это фигня, главное, что не надо платить баснословные бабки за лицензию). Это послужило причиной роста популярности движка и позволило ему на равных конкурировать с монстрилой Havoc'ом, который в последнее время теряет свои позиции. Нынче он используется в нескольких десятках самых известных игр, среди них: Unreal Tournament 3, Sacred 2, CellFactor и многие другие (думаю, все тайтлы перечислять не стоит, если вам это интересно, то на Википедии есть полный список, кроме того можете почитать там и о самом движке) Набор функций и библиотек для использования PhysX'а в своей игре называется PhysX SDK. Однако, он очень сложен в освоении да и не существует его версии для Blitz3D (если монстрилы индустрии вообще знают, что вообще есть на свете такая штука) Как же быть? На наше счастье, существует враппер этого самого SDK для Блитза, именуемый PhysX Wrapper за авторством Андрея Гутыри, более известного, как Render. Увы, он не бесплатен, но прогерам кушать тоже хочется. Корче того, то, что просит за него афтор - воистину смехотворная сумма по сравнению с тысячами долларов, которые просят за лицензию движки ААА-класса. Итак, что же представляет из себя этот враппер? А представляет он из себя набор dll-ок и деклз-файл, которые позволяют использовать движок PhysX на нашем старичке Блитзе. Да-да, вы не ослышались, это действитиельно возможно! Всё вышеописанное можно сделать и на Блитзе (с прямыми руками и головой на плечах, разумеется) Конечно, для Блитза и ранее существовали врапперы физических движков (Newton, ODE). Я не стану перечислять их достоинства и недостатки, и так я уже расписал своё лирическое отступление на полчаса чтения)) Коль речь идёт о PhysX'е, я постараюсь объяснить новичкам как им пользоваться, а заодно, возможно, и сам приобрету что-то новое. Итак, вперёд! |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 2
Начало работы. Что понадобится? Первое простое приложение на ФизикСе.
Вам понадобятся:
Итак, качаем установщик враппера. Запускаем его, указываем путь к Блитзу. Там несложно, разберётесь. Собсно это всё. Теперь перезапускаем Блитз и можно уже писать программу. Вот простой пример кода на Блитз3Д, который я взял за основу: Код:
Graphics3D 800,600,32,2 Цитата:
Эту команду надо вставить после инициализации графического 3Д-режима aka Graphics3D (как видите, я создал физ. плоскость и не стал передавать ключ): Код:
pxCreateWorld(1,"") Цитата:
Код:
pxRenderPhysic(60,0) Итак, давайте создадим кубик. Обычный, блитзевский, при помощи всем нам хорошо известной команды CreateCube(): Код:
Cube = CreateCube() Цитата:
На заметку Если выставить массу 0, то объект уже не будет динамическим, а будет статическим. Об этом я расскажу позже, но пока просто имейте ввиду. Для динамических тел нам понадобится масса, отличная от нуля. Итак, пишем код создания тела прямо после кода создания модели куба: Код:
Body = pxBodyCreateCube(1,1,1,1) Цитата:
Код:
pxBodySetPosition Body,0,10,0 Цитата:
Код:
pxBodySetEntity(Cube,Body) Да, чего-то здесь не хватает. Похоже, неплохо бы добавить освещение: Код:
light = CreateLight() Полный код примера вы найдёте в аттаче "PhysXExample1.zip" Мда, с одним кубиком не очень-то впечатляет. Давайте насоздаём их много. Используем для этого типы. Буду краток, есчли вы не очень хорошо разбираетесь в типах, то советую почитать вот этот перевод от товарища Импера: http://forum.boolean.name/showthread.php?t=10 (я сам по нему когда-то учился :super: ) Так, значится. Создадим тип для кубика: Код:
Type pxCube Теперь напишем функцию обновления каждого кубика: Код:
Function UpdatepxCubes() Код:
UpdatepxCubes() Код:
Function CreatepxCube(x#,y#,z#) Ну, раз мы упорядочили всё в типы, то можно удалить наш старый кубик. Стираем этот код: Код:
Cube = CreateCube() Код:
pxBodySetEntity(Cube,Body) Код:
If KeyHit(57) Then CreatepxCube(0,10,0) Да, этот результат намного интереснее! Полный код примера вы найдёте в аттаче "PhysXExample2.zip" Ну что ж, я описал базовые знания об этом враппере. Думаю, они окажутся вам полезными и вы прочитаете продолжение, которое я вскоре напишу. Однако не бойтесь экспериментировать и читать хелп самостоятельно. Ладно, уже скоро полшестого утра по Москве, пойду спать. -_- |
Другие примитивы.
Вложений: 2
Другие примитивы.
Итак, вчера я рассказал как создавать кубики. Но кроме них в ФизикСе есть ещё и другие примитивы. Думаю, будет полезно с ними кратко познакомиться. Первым будет цилиндр: Цитата:
Теперь давайте изменим пример PhysXExample2.zip чтобы он создавал не только кубики, но и цилиндры, причём случайно. Я изменил код создания куба таким образом: Код:
Function CreatepxCube(x#,y#,z#) Если она равна единице, то создаётся куб, как бы это делали раньше, а если двойке - то физическим телом будет цилиндр. Как вы можете видеть, для него я выбрал радиус 1, высоту 3, 8 граней и массу 1. Но модель по-прежнему куб. Непорядок. Куб с физической моделью цилиндра - это жестоко. Пускай будет блитзевский цилиндр с 8 гранями: Код:
pxC\mesh = CreateCylinder(8) Код:
ScaleEntity pxC\mesh,1,3,1 Для порядку я решил переименовать тип из pxCube в pxBody т.к. обзывать цилиндр кубом не очень корректно. Делать это необязательно, но это сделает код более понятным. Теперь функции и тип выглядят таким образом: Код:
Type pxBody Код:
If KeyHit(57) Then CreatepxBody(0,10,0) Код:
UpdatepxBodies() Полный код примера вы найдёте в аттаче "PhysXExample3.zip" Продолжаем тему примитивов. На очереди у нас сфер. Ну, тут всё предельно просто: Цитата:
Код:
Function CreatepxBody(x#,y#,z#) Запускаем, давим на пробел - теперь у нас ещё и шарики появляются. Но есть у сферы одна важная особенность, делающая её отличной от других примитивов. Если помните, при создании физического цилиндра мы указывали количество его граней. То есть в предыдущем примере получился никакой не цилиндр, а 8-угольная призма. Это происходит потому, что каждый объект в ФизикСе (да и во многих других движках) имеет конечное количество точек. Чем больше точек, тем более округлую поверзность можн осмоделлировать, но тем больше будут затраты на расчёт физики. В-общем, тут полная аналогия с полигональной графикой: чем больеш треугольников - тем круглее и точнее поверхность, но тем больеш нагрузка на видеокарту. Так вот, физ.сфера является исключением из этого правила! Она действительно математически является сферой (т.е. идеально круглой). Насколько мне известно, для таких объектов в Физиксе обработка происходит по несколько другим алгоритмам. За счёт того, что сферу можно легко описать лишь её радиусом, вычисления получаются во много раз быстрее, чем если то же самое делать из точек и треугольников, как цилиндр. Поэтому запомните: если вам нужно создать физический объект, имеющий шарообразную форму или близкую к ней, используйте физический примитив-сферу: это ускорит расчёт физики, чем если сферу моделировать как другие тела. Теперь перейдём к капсуле. Думаю, сначала надо рассказать, что же такое капсула. Она предстваляет из себя примерно такую штуку: По сути, это цилиндр с закруглёнными краями. Чем-то напоминает пилюлю. Создаётся вот такой командой: Цитата:
Глядите на схему, чтобы лучше понять. Попробуем у нас создавать капсулу высотой 3, радиусом 1 и массой 1: Код:
pxB\body = pxBodyCreateCapsule(3,1,1) Код:
Function CreateCapsule(height#,radius# ) Код:
Function CreatepxBody(x#,y#,z#) А теперь - внимание! - капсула, подобно сфере, тоже является исключением из правил и рассчитывается иными алгоритмами. Потому не имеет угловатостей и рассчитывается быстрее того же цилиндра с приемлемым количеством сегментов. Помните об этом. Полный код примера вы найдёте в аттаче "PhysXExample4.zip" Что-то уже поздновато -_- Думаю, на сегодня всё. Завтра я расскажу о хуллах и тримешах. Продолжайте экспериментировать. |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 2
Хулл, или как сделать физическую модель в 3D Studio max'е
В прошлые разы я рассказал об основных физических примитивах на PhysX Wrapper'е, однако только ими дело не ограничивается. Можно (куда ж без этого?) создавать физические тела произвольной формы. Для этого существуют Hull, Trimesh и Compound. Итак, начнём с Хулла. Хулл - это выпуклый физический объект. Обратите внимание: выпуклый. Напомню что такое выпуклое тело. В-общем, не стану грузить определениями, они объясняют заумным языком простые вещи. Скажу проще: внутри выпуклого тела нельзя найти такие две точки, что если через них провести прямую, то часть её окажется снаружи: Если это всё-таки возможно, то такое тело выпуклым не будет: То есть выпуклое тело - такое, в котором нет вмятин. Надеюсь, вы меня поняли. Все примитивы, рассмотренные ранее были выпуклыми телами. По сути, оони все (кроме сферы и капсулы) являются хуллами. Но мы можем создать хулл и произвольной формы, имея набор точек (т.к. из набора точек можно построить выпуклое тело). Его можно считать, скажем, с трёхмерной полигональной модели, а это значит, что по сути хуллы можно редактировать в 3Д Максе или любом другом трёхмерном редакторе, лишь бы он умел сохранять в блитзопонятный формат. Вот, сделал в Максе от балды такую модельку: Чем-то напоминает пресс-папье. Как видите, эта модель выпуклая. Теперь давайте используем её в Блитзе. Возьмём за основу Пример 3 (ищите в аттачах выше). В ём мы могли создавать различные физические объекты по нажатию пробела. Но давайте-ка их выкинем от греха подальше и заменим их на вышеуказанный объект. Я сохранил ту модель в "PressPapier.b3d" и теперь код создания физ. объекта будет выглядеть примерно так (в самом начале проги я загрузил модель в глобальную переменную PressPapierMesh, при создании только копирую её): Код:
Global PressPapierMesh = LoadMesh("PressPapier.b3d") Код:
Function CreatepxBody(x#,y#,z#) Цитата:
nvert - количество вершин, из которых будет состоять хулл. mass - масса Количество треугольников, которое получится в конечном хулле не должно превышать 512 ! Так что не надо пытаться строить хуллы из High-Poly моделей. Итак, самая главная заноза в заднице при создании хулла - это банк вершин. Если вы не умеете работать с банками в Блитзе - лучше почитайте о них, потому без базовых навыков работы с ними придётся туговато. Структура банка вот такая: Цитата:
Но не будем заморачиваться. В примрах к врапперу уже есть функция, которая сама создаёт банк из 3Д-модели и из него физическое тело. Просто я хотел чтобы вы знали структуру банка и в случае чего могли написать его генерацию самостоятельно. Как выяснится дальше, нижеследующая функция далеко не всегда идеальна: Код:
Function BodyCreateHull%(mesh%, mass#) Но тут есть одна неприятность. Дело в том, что если использовать (в Максе или в Блитзе) различные трансформации вроде поворотов и масштабов, то координаты вершин непосредственно не затрагиваются, а меняются некие параметры объекта, на основе которых перед рендером он будет отскейлен и повёрнут как надо. Но ведь мы считываем информацию непосредственно с вершин модели! А это означает, что вышеописанные трансформации никакого эффекта не дадут. Очень часто на форумах пишут о том, что тело из модели создаётся неправильно и т.п. - в 90% случаев причиной как раз является то, что я только что описал. Как же быть?
В моей модели я уже всё сделал по-вышеописанному, так что проблем быть не должно. Однако помните об этом, это очень важно. Так-с, теперь наконец можно непосредственно создать хулл. Пусть у него будет масса 1 (о да, я знаю, что у меня богатая фантазия на выбор массы:-D ), тогда код создания будет выглядеть так: Код:
BodyCreateHull(PressPapierMesh, 1) Но мы каждый раз при создании физ. объекта генерируем хулл из вершин. Это отнимает немного времени, но всё же оно достаточно значительное: до 1 мс. Но можно очень легко избавиться от этого: в самом начале создадим хулл, а потом будем только копировать его. Копирование займёт гораздо меньше времени, чем создание. Итак, в самом начале рядом с загрузкой модели создадим ещё одну глобальную переменную, в которую мы создадим хулл и откуда будем копировать тело: Код:
Global PressPapierHull = BodyCreateHull(PressPapierMesh, 1) Цитата:
Итаке, вместо того, чтобы заново рассчитывать тело при создании очередной "промокашки": Код:
pxB\body = BodyCreateHull(PressPapierMesh, 1) Код:
pxB\body = pxCopyBody(PressPapierHull) А делать мы будем вот что: отключим телу коллизию с другими телами, дабы оно не влияло на их перемещение. Делается это при помощи команды Цитата:
stat - показывает, нужно ли включить или отключить коллизию. 1 - вкл, 0 - выкл. body - тело, к которому этот флаг применяется. В нашем коде отключение коллизии для PressPapierHull будет выглядеть примерно так: Код:
pxBodySetFlagCollision(Int PressPapierHull, 0) Полный код примера вы найдёте в аттаче "PhysXExample5.zip" Но теперь нужно рассказать ещё об одной важной вещи. Затраты на обработку физических тел всё-таки ощутимо больше, чем на обработку моделей, состоящих из такого же количества вершин и граней. Поэтому нелишним будет упрощать физические модели насколько это возможно. Посмотрим на нашу "промокашку". В её модели 76 треугольников и она кажется вполне округлой. Я намеренно сделал такую высокую сегментацию, чтобы вы могли оценить разницу. Соответственно, и Hull, созданный из неё тоже будет состоять из эквивалентного числа граней. Но это явно избыточно. Думаю, если она будет немного более угловатой, игрок ничего не заметит. Кроме того, при экспорте модели для последующего создания из неё хулла нужно следовать определённым правилам (ищите выше), что не всегда удобно при моделировании. Лучше физическую модель делать отдельно. Кстати! (Это очень важно!) У визуальной модели и у физической модели центры должны находиться в одних и тех же координатах! Для удобства лучше делать их в одном и том же max-файле, чтобы физ. модель совпадала с визуальной насколько это возможно. Вот, сделал аналогичную модель в Максе, она состоит из меньшего числа полигонов: Её я сохранил в файл "PressPapierPhys.b3d" Кстати! Т.к. нам нужны лишь координаты вершин, то такую информацию, как нормали, текстуры, материалы и т.п. можно (и нужно) не сохранять! Вот теперь загрузим модельку из этого файла, создадим хулл уже из неё и саму модель удалим, т.к. она нам больше не понадобится (если вы внимательно читали всё что я писал ранее, то код должен быть понятен): Код:
HullMesh = LoadMesh("PressPapierPhys.b3d") Всегда упрощайте физическую модель когда это не в ущерб конечному результату. Тут намного больше возможностей, ведь игрок не видит физическую модель, а это значит что даже если она будет совсем уж угловатая, пользователь этого не заметит. Конечно же, делать физ. модель для бочки треугольной не стоит: она просто не сможет катиться по земле. Во всём нужно знать меру. Полный код примера вы найдёте в аттаче "PhysXExample6.zip" В другой раз я расскажу о тримешах и компаундах. Хотел описать их в этом посте, но тема вышла уж очень обширной. Напомнию, все вопросы и предложения можно запостить здесь. |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Trimesh или физическая модель невыпуклых тел.
В прошлый раз я рассказал о хуллах - выпуклых телах (см. предыдущий пост - как отличить выпуклое тело от невыпуклого). А что делать, если нам надо создать физическую модель невыпуклого тела? Например, физ. модель для комнаты, для кастрюли, стула, где есть множество внутренних пространств? Для этого в Физиксе существуют такие понятия, как Тримеш (Trimesh) и Компаунд (Compound) Компаунд - это как бы связка из нескольких хуллов (выпуклых тел). Например стул можно представить в качестве нескольких прямоугольных параллепипедов (в народе - "собрать из кубиков"), я тут попытался изобразить: То есть невыпуклый объект представляется как совокупность выпуклых. Затраты на расчёт такого объекта будут совсем небольшими (по сравнению с Тримешем). То есть если объект можно представить в виде кубиков, цилиндров и прочих выпуклых объектов то целесообразно использовать его. Если же это невозможно (например, если надо сделать (физически честную! естественно для небольших ведёрок в экшене лучше сделать просто хулл) модель ведра, то будет очень проблематично выстраивать "частокол" из "досочек" по периметру, да и вычислительные затраты будут большими) то можно использовать Тримеш. Тримеш - это физическая модель невыпуклого тела. То есть по-честному рассчитываются всякие спадины, дырки и прочие "впуклости". Само собой, особенно, если тело динамическое, это влечёт намного большие вычислительные затраты по сравнению с хуллами и компаундами. Так что прежде чем использовать тримеш, лучше подумать нельзя ли обойтись иными способами. Я бы рекомендовал использовать его для создания физ. модели уровня (т.к. статика жрёт меньше ресурсов), а для динамических объектов - использовать хуллы и компаунды. Существует очень важный недостаток тримешей, ограничивающий их применение. Дело в том, что в физиксе нету коллизии trimesh-to-trimesh. А это значит что два тела-тримеша будут просто проваливаться друг сквозь друга. Поэтому тримеш используем только для статики. Итак, этот пост будет посвящён тримешу. Сделал я в Максе такую модельку: Эдакая совдеповкская кастрюля. Это яркий пример где целесообразно использовать тримеш. Сделаем это. Возьмём за основу пример 6. Переделаем функцию создания объекта таким образом: Код:
Function CreatepxBody(x#,y#,z#, fig = 0) Теперь надо разобраться с кастрюлей: Код:
Global PotMesh = LoadMesh("Pot.b3d") В функции создания объекта дописываем копирование сетки. Код:
Case 2 ; кастрюля Цитата:
Vbank - банк вершин, Fbank - банк треугольников, MESH_NBVERTICES - количество вершин, MESH_NBFACES - количество треугольников, mass - масса. Структура Vbank'а идентична тому, который используется при создании хулла. Структура Fbank'а выглядитследующим образом: Цитата:
Код:
Function BodyCreateMesh(mesh%) Из ентити она создаёт Тримеш с массой 0 (т.е. статическое тело) Но мы всё-таки попробуем создать динамический тримеш. Поэтому модифицируем функцию таким образом: Код:
Function BodyCreateMesh(mesh%, mass#=0) Теперь создадим такой тримеш в самом начале прямо из модели кастрюли и отключим ему коллизию, как мы это делали с хуллом: Код:
Global PotHull = BodyCreateMesh(PotMesh, 5) При создании кастрюли просто копируем это тело: Код:
pxB\body = pxCopyBody(PotHull) Код:
If KeyHit(57) Then CreatepxBody(0,20,0,1) Кстати, внимательные догадались, что теперь хуллы создаются чуть выше ;) На всякий случай оnодвинем камеру подальше, а то великовата вышла кастрюля :-D Код:
PositionEntity cam,0,50,-50 (я ещё сделал рандомное назначение цвета для промокашек - думаю, не стоит объяснять как это делается) Как видите, вся невыпуклость кастрюли налицо. :super: Однако, попробуйте нажимать TAB несколько раз. Видите, одна кастрюля проваливается в другую? :( Поэтому, повторюсь, тримеши надо использовать только для статических объектов. При генерации тримеша подставим массу 0: Код:
Global PotHull = BodyCreateMesh(PotMesh, 0) Но как и в случае с хуллами, здесь уместна всякого рода оптимизация, в частности, создание тримеша не из той модели, которую будет видеть игрок, а из специально созданной для этого упрощённой. Вот что соорудил я: Кстати, при экспорте из Макса физической модели не обязательно хранить инфу о текстурах, нормалях и т.п. - так экономится размер файла :cool: Код:
PotMeshPhys = LoadMesh("PotPhys.b3d") Создавать тримеш можно и из незамкнутых поверхностей. Просто сквозь эти дырки можно будет провалиться. Но, например, если убрать нижнюю сорону дна у кастрюли - никто и не заметит, т.к. снизу вряд ли туда заскочит хулл ;) В-общем, смотрим по ситуации. Полный код примера вы найдёте в аттаче "PhysXExample7.zip" Следующий пост будет посвящён компаундам. З.Ы. К сожалению, теперь буду писать с частотой максимум раз в неделю т.к. с 11-го уже опять школа :( З.З.Ы. !! Не забывайте про ResetXForm! !! Надеюсь, этот пост оказался вам полезен. |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Компаунды, или дешёвые невыпуклые тела с коллизией.
В предыдущих постах я рассказывал о Хуллах и Тримешах. Теперь настала очередь компаундов. Итак, напомню что такое компаунды: это группа выпуклых физических тел, крепко соединённых вместе. То есть, например, если нам надо сделать стул, чтобы какой-нибудь шарик мог прокатиться у него между ножек (невыпуклая геометрия) мы можем сделать для него тримеш (тормознуто, отсутствие коллизии с другими тримешами), а можем описать его как группу из нескольких прямоугольных параллелепипедов ("боксов", "кубиков") и это будет обрабатываться значительно быстрее тримеша: Теория закончилась, переходим к практике. Возьмём за основу пример 2. Там можно было по нажатию на пробел создавать кубики. Попробуем соорудить вместо них что-то типа столов. Для начала нам потребуется моделька стола. Её можно было бы создать в максе, но, я думаю, связь между компаундом и моделью будет яснее, если мы соберём эту подельку прямо в блитзе, из кубиков: Код:
Global TableMesh = CreateCube() Теперь надо создать для этой модели физическое тело - компаунд. Так как мы собирали модельку из кубиков, то и компаунд собирать будем из таких же кубиков. Опять немного теории. Создать Compound несколько сложнее, чем простое тело. Сначала создаётся КомпаундДескриптор. Это своего рода контейнер информации о геометрии будущего компаунда. В него заносятся выпуклые физические тела, затем поворачиваются и располагаются как надо в соответствии с тем, что мы хотим получить. Затем из Дескриптора создаётся непосредственно Компаунд, к которому можно прикладывать силу, указывать координаты и т.п., т.е. всё, что можно творить с обычным физическим телом. Итак, Цитата:
Создадим дескрипторв нашей проге: Код:
Desc = pxCreateCompoundDesc() Чтобы добавить в него кубик, нужна команда: Цитата:
Desc - дескриптор, куда добавлять кубик. dx, dy, dz - масштаб по осям для кубика (аналогично pxBodyCreateCube, смотрите выше подробное описание если что-то упустили) Обратите внимание: массу указывать не надо. Как бы это не физическое тело, а фигура, не имеющая массы (возможно, потому она и называется Shape а не Body). Она будет общая для всего компаунда и указывается при его (компаунда, не дескриптора) создании. Центр массы будет рассчитан там же. Итак, добавим по очереди кубики в наш дескриптор, чтобы они соответствовали модели стола. Так как мы модель стола собирался из кубиков, то все параметры для позиционирования и для масштабирования у нас есть, осталось только использовать их. Создадим столешницу, напомню, что при создании модели код выглядел так: Код:
Global TableMesh = CreateCube() Код:
Sh = pxCompoundAddCubeShape(Desc,3,0.3,1.5) Цитата:
Собственно, аналог PositionEntity, только для Shape'ов дескриптора. Ничего сложного тут нет. Теперь переместим и наш шейп туда, где он должен стоять (а стоять он должен там же, где и столешница-меш, то есть в 0,3.3,0) : Код:
pxCompoundSetShapePos(Sh,0,3.3,0) Код:
Sh = pxCompoundAddCubeShape(Desc,0.3,1.5,0.3) Цитата:
Вот теперь давайте создадим физическое тело нашего стола: Код:
Global TableBody = pxCreateCompound(Desc,1) Так как это обыкновенное тело, с ним можно вытворять всё то же что и с любым другим телом. Давайте отключим ему коллизию (как в более ранних примерах мы это делали чтобы тело-эталон не мешалось): Код:
pxBodySetFlagCollision TableBody,0 Код:
pxC\mesh = CreateCube() Код:
pxC\mesh = CopyEntity(TableMesh) Код:
pxBodySetPosition pxC\body,x+Rnd(-10,10),y,z+Rnd(-2,2) Однако обратите внимание на такую вещь (вы, наверное, уже заметили): если два компаунда создать "друг в друге" (если слишком часто нажимать на пробел), то сложные компоунды иногда не смогут "выскочить" друг из друга, как это ранее легко делали хуллы и так и останутся слипшимися. Это не есть гуд, но что поделать. Так что следите за тем, где вы создаёте компаунды. Полный код примера вы найдёте в аттаче "PhysXExample8.zip" (там я переименовал типы и функции из Cube в Table чтобы они соответствовали своему названию ;)) Тема компаундов достаточно обширна. Здесь я изложил только основы. В следующий раз я продолжу рассказывать о компаундах, в частности мы рассмотрим добавление в него других примитивов и хуллов. Следующий пост ждите примерно через неделю ;) |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 3
Другие примитивы и хуллы в компаунде.
Итак, в прошлый раз я рассказал о том, как сделать компаунд из кубиков. Теперь надо рассказать о других примитивах, там всё по аналогии. Итак, давайте возьмём за основу пример 8 и попробуем усложнить столик, добавив в него новые примитивы. Для начала давайте уберём ножки стола, сначала из меша, теперь он создаётся вот так: Код:
Global TableMesh = CreateCube() Код:
Desc = pxCreateCompoundDesc() В прошлый раз я создавал меш капсулы вот этой фуцнкцией, в данном случае она тоже пригодится (кстати я её маленько подправил): Код:
Function CreateCapsule(height#,radius# ) Код:
leg = CreateCapsule(1.5,1.0) Цитата:
Desc - дескриптор, куда добавлять, radius и height - радиус и высота капсулы (см. выше пост про примитивы = полная аналогия). Возвращает хендл созданного шейпа. Теперь к дескриптору компаунда приделаем шейп-капсулу с теми же параметрами: Код:
Sh = pxCompoundAddCapsuleShape(Desc,1.0,1.5) Цитата:
Используем эту функцию чтобы повернуть наш "столик" на рандомный угол при создании: Код:
pxBodySetRotation pxT\body,Rnd(-10,10),Rnd(-10,10),Rnd(-10,10) Теперь совсем рандомщина :cool: Давайте теперь сделаем капсулу не такую толстую: Код:
leg = CreateCapsule(1.5,0.5) Код:
sph = CreateSphere() Цитата:
Создадит теперь и сферу-шейп и расположим её так же, как и меш: Код:
Sh = pxCompoundAddCapsuleShape(Desc,0.5,1.5) Полный код примера вы найдёте в аттаче "PhysXExample9.zip" Так, со сферами и с капсулами разобрались. Далее по списку - цилиндер. Цитата:
Desc - дескриптор, куда добавлять, radius и height - радиус и высота цилиндра, nbEdge - количество граней цилиндра. Полная аналогия с pxBodyCreateCylinder (см. выше). Возвращает - кто бы мог подумать? - хендл созданного шейпа. Заюзаем на практике. За основу я взял Пример 9. Уберите оттуда всё, что связано с капсулой и сферой, они тут не понадобятся. Теперь заюзаем цилиндр вместо ножки стола. Создадим его в меше: Код:
leg = CreateCylinder(8) Далее создаём цилиндр в дескрипторе (опять же, скайлим его точно так же и позиционируем точно так же, как и меш): Код:
Sh = pxCompoundAddCylinderShape(Desc,1,1.5,8) Код:
pxBodySetRotation pxT\body,Rnd(-100,100),Rnd(-100,100),Rnd(-100,100) Полный код примера вы найдёте в аттаче "PhysXExample10.zip" Ну что ж, теперь - самое интересное - Хулл! Потому как хулл может быть произвольной формы, то и возможностей он даёт гораздо больше. Как и обычное тело-хулл, хулл-шейп создаётся из массива точек. Рассмотрим такую команду: Цитата:
Структура vbank'а - такая же как и при создании хулла-тела, то есть такая: Цитата:
Код:
Function CompoundCreateAddHullShape%(Desc, mesh%) Внимание! Перед импортом меша из Макса, проделайте действия, описанные в посте №4 про хуллы, чтобы избежать глюков (ResetXForm и т.д.)! Теперь опробуем на деле. Возьмём Пример 10. Уберём из него создание меша и компаунда стола и перепишем его опять. Но для начала нам понадобится моделька чтоыб из неё делать хулл. Далеко ходить не надо, используем модельку из поста №4. Загрузим её(предварительно убрав все "упоминания" о старом объекте): Код:
Global TableMesh = LoadMesh("PressPapier.b3d") Тперь загрузим-ка упрощённый меш для создания из него хула (надеюсь, понятно почему?): Код:
TableMeshPhys = LoadMesh("PressPapierPhys.b3d") Код:
Desc = pxCreateCompoundDesc() Код:
FreeEntity TableMeshPhys Код:
Sh = CompoundCreateAddHullShape(Desc,TableMeshPhys) Код:
Sh = CompoundCreateAddHullShape(Desc,TableMeshPhys) Да, чуть не забыл, чтобы повернуть шейп в дескрипторе, нужно использовать эту команду: Цитата:
Итак, поднимем новый шейп относительно первого, перевернём его чтобы креглешом смотрел вверх и ещё на 90°: Код:
pxCompoundSetShapeRot(sh,0,90,180) Код:
TableMesh2 = CopyMesh(TableMesh) В-общем-то, должно быть понятно. Запускаем, тестим, теперь у нас компоунд из нескольких хуллов: Полный код примера вы найдёте в аттаче "PhysXExample11.zip" Собственно, хуллов, как и примитивов, в дескрипторе (-> в компаунде) можно создавать несколько и комбинировать различными путями. Возможно в середине недели напишу "довесочек" типа создания уже реального предмета из хуллов. В следующую субботу напишу статью, наконец, про силы и импульсы - это самые шишечки :cool: На сегодня у меня всё, спасибо за внимание. |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Луч, силы, импульсы.
Теперь дела пойдут повеселее! Будем толкать наши физические тела! Итак, для начала немного подготовимся. Возьмём за основу пример №2, тот, где можно было создавать кубики. Немного переделаем функцию создания куба, чтобы она ещё и возвращала ссылку на его тип: Код:
Function CreatepxCube.pxCube(x#,y#,z#) Код:
Cube.pxCube = CreatepxCube(0,10,0) Код:
EntityColor Cube\mesh,200,0,0 Код:
If KeyHit(15) Then CreatepxCube(0,10,0) Цитата:
body - тело, на которое применяется сила (импульс, ...), vx, vy, vz - вектор силы (чем он "длиннее" - тем большая сила приложится), mode - режим. Если с первыми четырьмя аргументами всё более-менее ясно, то на последнем остановимся поподробней. Итак, этот параметр указывает, что именно делать с телом: 0 - приложить силу. По заветам товарища Ньютона, a=F/m, ускорение, которое получает материальная точка, прямо пропорционально приложенной силе и обратно пропорционально массе, т.е. чем большую силу приложат к телу, тем большее оно приобретёт ускорение, но чем больше масса тела, тем это самое ускорение будет меньше. Собсно, этой командой мы прилагаем силу, а тело, в зависимости, от своей массы, приобретает то или иное ускорение. При одной и той же силе тело массой 1 улетит дальше чем тело с массой 10. На деле будет выглядеть, как будто тело куда-то тянут или толкают. Используется для постоянного толкания тела. Например, для перемещения персонажа. 1 - приложить импульс. Здесь происходит резкий толчок. Ускорение так же зависит от массы. По определению импульса, P=m*V, то есть импульс, применённый на тело с аргументом-вектором P, будет выглядеть так, как будто в тело врезалось тело с массой m с мгновенной скоростью V, но так, что вектор mV = вектору P. Очень удобно, если по каким-то причинам нужно смоделировать столкновение тел, а моделировать одно их них нельзя или не желательно. Например, выстрел пулей в ящик можно сымитировать, описав пулю шариком и пульнуть им в ящик, чтобы они столкнулись. Но зачем нам моделировать ещё одно лишнее физическое тело, когда достаточно применить на ящик импульс, чтобы он отскочил? 2 - действие на скорость (или change of speed, как гласит документация). Это импульс, игнорирующий массу объекта. Эффект будет тот же, как если бы у тела, на которое применяют сиё, была бы масса 1. То есть если к двум телам (массы 1 и 100) сответственно применить импульс, скажем, 10, то первое подскочит, а второе еле дрогнет. А если к тем же самым телам применить точно такой же change of speed, то оба подпрыгнут совершенно одинаково. 3 - smooth impulse, сглаженный импульс. Тот же импульс что и с параметром 1, но применяется более плавно. Если честно, от обычного отличается не слишком сильно, хотя выглядит и правда мягче. Думаю, для имитации попадания пули подойдёт простой импульс, а для толчка ногой - сглаженный. 4 - smooth change of speed - собственно, тот же change of speed, но более мягкий. Аналогично паре 1 и 3. 5 - ускорение. Та же сила (параметр 0), но игнорирующая массу, аналогично паре 1 и 2. Кстати в дальнейшем слова "сила" и "импульс" часто будут синонимами (если не указано обратное) т.к. с нашей, юзерской точки зрения, это почти одно и то же. В теоретической физике вышеописанное - разные вещи, но на практике мы просто получим сообщение разного ускорения телу для разных случаев. Используйте то, что будет более удобно в конкретной ситуации. Естественно никто не станет кратковременно применять огромную силу на тело, чтобы заставить его подпрыгнуть, и не станет пытаться толкать тело вперёд маленькими импульсами, когда можно долговременно применять на него силу. И незачем использовать change of speed, когда придётся вычислять значение исходя из массы вручную, когда impulse учтёт эту массу автоматом. Фактически, чаще всего используются только режимы 0 (сила) и 1 (импульс), остальные - крайне редко. Ну что ж, заставим наше тело подпрыгивать по нажатию пробела. Для этого применим на него импульс с вертикальным вектором 10: Код:
If KeyHit(57) pxBodyAddForce Cube\body,0,10,0,1 Теперь попробуем применять силу. Заставим тело толкаться в разные стороны по нажатию клавиш WASD. Код:
If KeyDown(17) pxBodyAddForce Cube\body,0,0,10,0 Добавим другие клавиши: Код:
If KeyDown(17) pxBodyAddForce Cube\body,0,0,10,0 Помните игру Ballance? Собственно, с PhysX'ом вот так всё просто. Вставить сюда вместо кубика шар, убрать прыжки, загрузить уровень в trimesh - и будет вам Ballance :cool: Попробуем (табуляцией) засыпать наш кубик другими кубами. Теперь можно подпрыгнуть из груды, распихивать их. Конечно, для распихивания сила явно маловата, но ведь можно и увеличить! Вот, собственно, и всё о pxBodyAddForce. Теперь настала пора познакомиться с её сестрой-близнецом: Цитата:
body - тело, на которое применяется сила (импульс, ...), vx, vy, vz - вектор силы,px,py,pz - локальные координаты точки куда прилагать силу, mode - режим. Как вы помните, в pxBodyAddForce сила применялась к центру массы тела. Тут же можно применить силу в какую угодно точку тела, указав её локальные координаты. Это очень удобно, т.к. если тело перевернётся, то не придётся заново вычислять координаты точки куда прилагать силу в соответствии с поворотом тела, т.к. она локальна. Все остальные параметры 100% совпадают с pxBodyAddForce. Попробуем заюзать на практике. Давайте сначала уберём всё, что было ранее связано с WASD: Код:
If KeyDown(17) pxBodyAddForce Cube\body,0,0,10,0 Код:
If KeyDown(57) pxBodyAddForceAtLocalPos Cube\body,0,10,0,1,1,1,0 Кстати, можно указывать позицию не только на поверхности тела, но и за его пределами. Теперь рассмотрим другую команду: Цитата:
body - тело, на которое применяется сила (импульс, ...), vx, vy, vz - локальный вектор силы, px,py,pz - локальные координаты точки куда прилагать силу, mode - режим. Собственно, это аналог предыдущей функции, но теперь и сила применяется локально. То есть если мы укажем вектор 0,0,1, то тело будет двигаться вперёд. Есви оно перевернётся, то оно будет двигаться туда, где перед у тела. Если pxBodyAddForce - это в некоторой степени аналог TranslateEntity, то pxBodyAddLocalForce - аналог MoveEntity. Так, как же нам может оно здесь пригодиться? Давате включим фантазию. Представим, что наш кубик - это катер на воздушной подушке над ледяной пустыней. У такого катера сзади 2 огромных вентилятор - один справа, другой слева. Когда работают оба, катер движетя вперёд. Когда один из них (например, правый) снижает обороты или вовсе замолкает, то левый поворачивает весь катер вправо. Или как, например, если обе гусеницы танка крутятся одинаково, то танк едет прямо, а если одна едет быстрее другой, то танк поворачивается в ту сторону, которая едет медленней. Дык вот, сделаем нашему кубику такие "двигатели". Пускай левый работает по клавише A, а правый - по клавише D. Для этого будем применять силы в тех точках, где бы располагались "турбины". Пускай они располагаются на 10 влево и вправо соответствующий. Так далеко - чтобы результат был более явным. Сила каждого - 10 по оси Z. Вот что у меня вышло: Код:
If KeyDown(30) pxBodyAddLocalForceAtLocalPos Cube\body,0,0,10,-10,0,0,0 Код:
ScaleEntity pxC\Mesh, 1,1,5 |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Теперь последнее и самое интересное
Цитата:
body - тело, на которое применяется сила, vx, vy, vz - глобальный вектор силы, px,py,pz - глобальные координаты точки куда прилагать силу, mode - режим. А чем же эта функция так интересна? А тем, что позволяет вытворять очень интересные вещи. Вернёмся к первому посту. В нём я упомянул, что во многих экшенах можно стрелять по физическим объектам и они отскакивают от пуль, да вы, наверное, и сами не раз замечали и на первых похар баловались эжтой возможностью. Так вот, это всё делается при помощи команды pxBodyAddForceAtPos. Но всё по порядку. Для начала опять уберём всё что связано с созданием кубика, движением кубика силами, его подпрыгиванием и масштабированием, то есть вернём его в исходное состояние. Проще взять обратно пример №2. Теперь дадим возможность игроку свободно летать по сцене. Далеко ходить не надо, уже давно существуют удобные функции управления движением камеры и её поворотом, вам, наверное, они хорошо известны: Код:
Function control(ent) ; Функция управления WASD Код:
control cam : mouselook cam Код:
HidePointer()
Как пикать? Конечно, первое, что приходит в голову - это CameraPick. Но это надо выставлять всем кубикам пикмоды по полигонам (а это тормоз), потом получив меш - перебирать все типы (от полного перебора тоже можно уйти, но это опять же гемор) пока не найдём пикнутый и затем наконец применять силу. Но ведь в физиксе уже есть встроенный пик по лучу! Давайте ознакомимся с ним поближе. Для начала надо создать луч. Это делает команда: Цитата:
Создадим тоже луч, пусть он будет глобальный: Код:
Global ray = pxCreateRay() Итак, сначала пикаем. Для этого нужно переместить луч в позицию камеры. Это просто: Цитата:
Аргументы: ray - хендл луча x,y,z - координаты для него. Итак, я устанавливаю луч в позицию камеры: Код:
pxRaySetPosition(ray, EntityX(cam,1), EntityY(cam,1),EntityZ(cam,1)) Цитата:
Аргументы: ray - луч nx,ny,nz - нормализованный вектор направления (т.е. длина вектора = 1) Итак, как же получить нужный нам вектор, имея только поворот камеры? Нам поможет блитзовский TFormVector: Код:
TFormVector 0,0,1,cam, 0 Она трансформирует вектор из одной матрицы в другую, т.е. например как в данном случае у камеры был локальный вектор 0,0,1, но камера повёрнута, и нам нужно узнать глобальное направление того вектора. Тогда помогает эта функция. четвёртый и пятый её аргументы - это исходный ентити и конечный ентити, чтобы указывать, что относительно чего трансформировать. Если выставить 0 - то будет считаться за мировую систему координат (т.е. глобальные значения). Короче благодаря ей мы нашли направление куда смотрит камера. Значения снимаются при помощи TFormedX()#,TFormedY()#,TFormedZ()#. Для порядку а также чтоб легче было дебажить запишем их в паременные: Код:
DirX# = TFormedX() Код:
pxRaySetDir(ray,DirX,DirY,DirZ) Цитата:
Аргументы: ray - луч mode - режим: 0 - тыкать все объекты 1 - тыкать только динамические объекты 2 - тыкать только статику Получаем хендл тела в переменную (чтобы потом в случае чего опять его не пикать) и проверяем на нуль: Код:
Body = pxRayGetBody(ray,1) Цитата:
Аргументы: ray - луч mode - режим: 0 - тыкать все объекты 1 - тыкать только динамические объекты 2 - тыкать только статику Так как мы будем применять силу на то, что ткнули, целесообразно получать координаты только динамики. Получим значения в переменные: Код:
PosX# = pxRayGetPickX(ray, 1) Код:
pxBodyAddForceAtPos body, DirX*10,DirY*10,DirZ*10, PosX, PosY,PosZ,2 Дадим возможность игроку стрелять по левой кнопке мыши: Код:
If MouseHit(1) Then Shoot() Чего-то не хватает. Ну что ж, последний штрих: прицел. Код:
Color 200,0,0 Извините что картинок маловато, но тут уже скринами ничего не покажешь: нужно видеть в динамике. Ну что ж, рассказ о силах и импульсах закончен! У лучей есть ещё некоторые фичи, о них я буду рассказывать далее по ходу. Полный код примера вы найдёте в аттаче "PhysXExample12.zip" У меня всё, надеюсь, материал оказался вам полезен. У меня на сегодня всё ;) З.Ы. Полез опечатки исправлять - при сохранении пишет что пост слишком короткий, увеличьте до 4 символов. Пришлось разрезать на 2 поста, тогда глюк прошёл. СабЗиро, где ты?.. :( |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Сочленения
Итак, сочленения. По-буржуйски - джойнты (joints) Сочленения - это объекты, которые могут определённым образом "сцеплять" тела. То есть например, в том же пресловутом Ragdoll'е все физ. тела для костей соединены между собой сочленениями. Если бы этого не было, что все части тела разлетелись бы кто куда и никаког орегдолла не получилось бы. Джойнтов (во враппере, по крайней мере) существует достаточно много, рассмотреть их в одной статье не удастся, поэтому начну с самых основных. Сферический джойнт. Это самый распространённый вид сочленения. По сути, это шарнир, то есть имеет лимит в виде конуса. Чтобы лучше понять, как он будет работать, посмотрите на эту картинку: Синий и красный цилиндры - это два тела, соединённые шарниром. Сферические части - это и есть собственно шарнир. Как видите, тела могут вращаться друг относительно друга только в пределах серого конуса. Такой тип сочленений находит широкое применение. Например, с его помощью можно имитировать соединённые вместе звенья цепи, или сделать верёвки из тех же звеньев, или имитировать разные подвешенные предметы (болтающаяся под потолком лампочка) и т.д. То есть применять можно везде где тела могут свободно вращаться. Переходим к практике. Возьмём за основу Пример 2. Будем создавать теперь уже не один кубик, а два вытянутых "кирпича", да ещё исоединённые между собой джойнтами. Хочу сразу оговориться. Так как в каждом таком объекте уже будет 2 тела, а не одно, то пришлось бы создавать новые Field'ы для новых тел и мешей, менять обработку... Но лучше поступим по-иному. Будем создавать сначала два pxCube'а, а потом между ними - джойнт. Итак, напишем функцию, которая создаёт два куба типа pxCube, один чуть повыше другого. Понадобится также заставить функцию CreatepxCube возвращать созданный тип, чтобы мы могли к ним потом обратиться и создавать кубы более продолговатые: Код:
Function CreateBunch(x#,y#,z#) Цитата:
Аргументы: body1, body2 - тела, которые будем сцеплять. x, y, z - координаты джойнта при его создании. nx, ny, nz - нормализованный вектор направления оси джойнта. Попробую объяснить проще: помните серый конус с картинки выше? Эти параметры как раз указывают, куда будет направлен этот конус. Например, при 0,1,0 цилиндр будет направлен вверх. Внимание! Тела body1, body2 должны быть динамическими (т.е. иметь массу <> 0). Если вы хотите привязать тело к статике, то подставьте 0 вместо статического тела. Привяжем 2 наших кубика один к другому: Код:
pxJointCreateSpherical Cube1\Body,Cube2\Body,x,y,z,0,1,0 Теперь по пробелу уже создаём связку: Цитата:
Обратите внимание: два сцеплённых друг с другом тела не коллизятся друг с другом! Можно включить коллизию. Цитата:
Попробуем и в нашей проге это сделать. Так как нам теперь нужен хендл джойнта, то получим его в переменную и на него применим вышеописанную команду: Код:
Joint = pxJointCreateSpherical (Cube1\Body,Cube2\Body,x,y,z,0,1,0) Код:
Cube1.pxCube = CreatepxCube(x#,y#+6,z#) Итак, уберём включение коллизии, вернём расстояние между кубами на 5 и рассмотрим, как можно по-иному ограничить вращение. Цитата:
Аргументы: joint - хендл джойнта angle - угол лимита в градусах hardn - жёсткость лимита. Диапазон: [0;1] По умолчанию: 1. restit - упругость лимита. Диапазон: [0;1] По умочанию: 0. *Очень странно, но при любых значениях hardn и restit получается один и тот же эффект. :4to: При значениях за пределами указанного диапазона лимит отключается вовсе. Use it wisely. Укажем лимит в 90°, остальные параметры оставим как по дефолту и посмотрим, что выйдет: Код:
pxJointSphericalSetLimitAngle(Joint, 90,1, 0) *Это важно: в школьной геометрии (11-й класс) углом конуса считается угол при вершине его осевого сечения, что логически верно. Но в PhysX'е указывается только половина этого угла. Взгляните на картинку: угол, задавемый параметром angle вышеописанной функции обозначен синим: Будьте бдительны и не ведитесь на дезинформацию минобразования РФ =-))) Тела крутятся вокруг своей оси. А что, если это например нога человека? Ведь она же не может крутиться на 360° ? Это дело тоже можно ограничить. Цитата:
joint - джойнт mintwist,maxtwist - минимально и максимально возможное кручение spr - упругость. Диапазон: [0;бесконечность), по умолчанию: 0 damp - "вязкость" targetVal - значение, к которому будет стремиться поворот. Создадим ограничение +- 10° с некоторой упругостью и вязкость. Целевое значение пусть будет 0 Код:
pxJointSphericalSetLimitTwist(Joint,-10,10,10,1,0) Рассмотрим ещё одну команду для сферического джойнта: Цитата:
joint - джойнт spr - эластичность. По дефолту 0, может принимать значения от 0 до бесконечности. damp - вязкость targetVal - значение к которому стремиться. По дефолту 0. Установим эту самую растяжимость: Код:
pxJointSphericalSetLimitSpring(Joint, 10, 1, 0) Цитата:
Аргументы: joint - джойнт Чтобы на практике заюзать это, нужно где-то хранить хендлы всех созданных джойнтов. Для этого я создал соответствующий тип, функцию удаления всех джойнтов и при создании новой пары кубов создаю новый тип и в него заношу хендл нового джойнта: Код:
Type Joint Код:
If KeyHit(42) Then FreeAllJoints() Кстати, обратите внимание на то, что при уничтожении джойнта коллизия между двумя телами снова включается и они "выскакивают" друг из друга. Поэтому если вы собрались в процессе игры разрушать некоторые джойнты, то коллизию всё-таки лучше включить. Полный код примера вы найдёте в аттаче "PhysXExample13.zip" В этот раз хотел написать ещё и про хиндж, да не успеваю :( в СЗИП СПБГУТД на этой неделе будет тестирование, по итогам которого соответственно повышаются шансы на поступление, так что надо готовиться. В-общем, кто учился в 11-м классе - тот поймёт. Не могу сказать точно, будет ли следующий пост через неделю. Если нет - напишу позже. На сегодня у меня всё ;) |
Материалы
Вложений: 1
Материалы в PhysX
Так как АВТОМАТ по учебным обстоятельствам не может часто повествовать, то я решил помочь ему в этом нелёгком деле :) . Я попробую рассказать вам о материалах, которые можно назначать на физические тела. Собственно материал - это коэффициенты трения и упругости тела. Что означают эти термины я думаю все помнят из школьного курса физики :) . Для создания материала служит след функция: Код:
pxCreateMaterial() Код:
pxMaterialSetDyFriction(mat%, fric#) Код:
pxMaterialSetStFriction(mat%, fric#) Код:
pxMaterialSetRestitution(mat%,rest#) То есть, чем большие значения мы зададим для трения, тем медленнее тело будет скользить, для упругости - тем больше оно будет отскакивать (как резиновое). Давайте попробуем создать свой материал. Создание окна, камеры, света, физического мира (и т.п.) подробно описывать, я думаю, нет смысла, тут и так всё понятно: Код:
Graphics3D 800,600,32,2 Код:
gorka_body=pxBodyCreateCube(10,0.2,10,0) Должно получится приблизительно так: Для большей наглядности создадим зеркальный пол: Код:
plane=CreatePlane() Код:
mat_stat1=pxCreateMaterial() Код:
pxMaterialSetToBody(gorka_body,mat_stat1) Код:
Type pxCube Код:
mat_dyn1=pxCreateMaterial() Код:
While Not KeyDown(1) Как видите, кубики себя по разному ведут, в динамике это хорошо видно. Но даже из скрина понятно, что 3ий кубик скользит быстрее остальных. Я думаю принцип понятен, а если поиграться с коэффициентами, то будет ещё понятнее ))))) Ещё в PhysX есть такая очень полезная штука, как комбинированный режим коэффициентов. Для этого существуют специальные функции: Код:
pxMaterialSetFrictionCombineMode(mat%, mode%) Код:
mat_dyn4=pxCreateMaterial() Код:
While Not KeyDown(1) Полный код приведёт в прикреплённом файле. Также ещё существуют так называемые анизотропные материалы: Код:
pxCreateAnisotripicMaterial(body%, nx#, ny#, nz#) Для анизотропного материала можно применять особый тип трения: Код:
pxMaterialSetFrictionV(mat%, sfric#, dfric#) Надеюсь я смог доходчиво обьяснить изложенный материал и если меня не закидают помидорами :) и АВТОМАТ не будет против, то я буду продолжать помогать ему писать учебник:). Впереди ещё много интересного (магниты, ткани, мягкие тела и тд) :super: |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Магниты
Итак, продолжим нашу экскурсию в удивительный мир PhysX :) Сегодня тема пойдёт о магнитах. Штука эта намного полезнее, чем пожет показаться на первый взгляд. Почти в любой игре, в которой используется физика используются и магниты. Любые эффекты, типа телекинеза, гравипушки, чёрной дыры и даже взрывов - это всё дело рук магнитов :) Так что постараемся подробнее разобраться в принципах их действия. Функций для работы с магнитами не много, но все полезные и часто используемые. Для начала научимся создавать магнит: Код:
pxCreateMagnet(minforce#, middleforce#, maxforce#) Код:
pxMagnetActivate(mdata%, mmode%, fmode%) И собсно функция для позиционирования магнита: Код:
pxMagnetSetPosition(mdata%, pos_x#, pos_y#, pos_z#) Код:
Graphics3D 800,600,32,2 Код:
For i=0 To 15 Код:
mag1=pxCreateMagnet(1,5,10) ; создаём магнит Код:
pxMagnetActivate(mag1,0,0) Теперь сделаем, чтобы магнит можно было двигать стрелочками на клаве и чтоб он активировался только когда держим пробел. Это пишем до цикла (создаём синий шар, чтоб мы могли видеть перемещение магнита): Код:
sp =CreateSphere () If KeyDown (205) x# = x#+0.2 If KeyDown (203) x# = x#-0.2 If KeyDown (200) z# = z#+0.2 If KeyDown (208) z# = z#-0.2 If KeyDown(57) pxMagnetActivate(mag1,0,0) pxMagnetSetPosition(mag1, x, y, z) PositionEntity sp, x,y,z [/code] Попробуем сделать, чтобы по enter магнит менял полярность (отталкивал) и шарик менял цвет. В итоге получаем: Код:
x#=-10 Код:
explode=pxCreateMagnet(0,-20,-50) Код:
explode_sp=CreateSphere() Код:
If KeyHit(18) And boom=0 Код:
If KeyHit(15) Ниже приведу список функций, которые интуитивно понятны и не требуют разьяснения: Код:
pxMagnetSetMinRadius(mdata%,minradius#) Код:
pxMagnetGetPositionX(x#) Код:
pxMagnetDelete(mdata%) И последнее, что я хотел рассказать. Те, кто уже в уме представляет себе, как он будет делать в своей игре взрывы и телекинез:) , спросит у меня - а как же быть, если в игре не нужно притягивать/отталкивать все предметы, а наоборот - лишь единичные? А с помощью pxBodySetFlagMagniteble(body%, stat%) и масок, отвечу я вам. Остановимся поподробнее. Итак, функция pxBodySetFlagMagniteble() позволяет нам ставить любому телу флаг (stat), т.е. если stat=0 - магниты не влияют на тело и stat=1 - соответственно влияют. Но такой способ не всегда удобен. Потому существуют так называемые маски: Код:
pxMagnetSetMask(mdata%,mask%) Изменим немного функцию создания кубиков, добавим случайное разделение на металл(белый с маской 2) и неметалл(красный с маской 1): Код:
Function CreatepxCube(x#,y#,z#) Код:
pxMagnetSetMask(mag1,2) Полный код как обычно в аттаче |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Хауди хоу, мои невероятные друзья, я снова с вами! Впрочем, это немного не из моего репертуара. После долгого перерыва статьи в этом топике продолжаются, постараюсь писать почаще. Сегодня на повестке дня Хиндж.
Hinge. Это тоже очень распространённый вид сочленения. Это уже не шарнир (как сферический джойнт), а рычаг, который имеет лимит в виде плоского сектора. Посмотрите на картинку: Как видно из неё, красный цилиндр уже не может свободно болтаться, а может только вращаться в серой плоскости. Применяется там, где требуется болтание только в одной плоскости, например, маятник от часов, или дверь в косяке. Приступим к практике. Возьмём предыдущий пример (13-й, если мне память не изменяет) и заменим сферический джойнт на хиндж, а заодно увидим разницу в динамике. Находим строчвку, где сцепляли два куба джойнтами. Нам необходимо создать хиндж, значит нам нужна команда: Цитата:
Параметры идентичны pxJointCreateSpherical, а именно: body1, body2 - тела, которые будем сцеплять. x, y, z - координаты джойнта при его создании. nx, ny, nz - нормализованный вектор направления оси джойнта. Внимание! Тела body1, body2 должны быть динамическими (т.е. иметь массу <> 0). Если вы хотите привязать тело к статике, то подставьте 0 вместо статического тела. Теперь с чистой совестью заменяем Сферикал на Хиндж: Код:
Joint = pxJointCreateHinge (Cube1\Body,Cube2\Body,x,y,z,0,1,0) Таак, крутится-то не в той плоскости. Меняем вектор направления оси хинджа: Код:
Joint = pxJointCreateHinge (Cube1\Body,Cube2\Body,x,y,z,0,0,1) Продолжаем разговор. Как и в случае со сферическим джойнтом, два сцеплённых друг с другом тела не коллизятся друг с другом! Поэтому вы имеете счастье наблюдать, как два тела спокойно проходят друг сквозь друга. Коллизию, как вы уже догадались, можно при необходимости врубить, вот так: Цитата:
юзаем: Код:
pxJointHingeSetCollision(joint) А вот так ограничивается угол: Цитата:
joint - джойнт, с которым будет производиться манипуляция min - минимальный угол max - максимальный угол Посему коллизию убираем, а юзаем лимит: Код:
pxJointHingeSetLimit(joint,-30,30) А вот растяжимость менять можно: Цитата:
joint - джойнт spr - эластичность. По дефолту 0, может принимать значения от 0 до бесконечности. targetVal - значение к которому стремиться. По дефолту 0. Варнинг! Когда я попытался запустить это, у меня вылезла ошибка, мол, Function not found. Произошло это от того, что функция в хелпе есть, а в деклзе - нет. Путём ковыряния в либе Hex-редактором (да простят меня афторы: ведь не корысти ради, а народа для!) я нашёл решение траблы. Если у вас будет то же самое, то делаем такие действия: лезем в папку с блицом -> userlibls -> Blitzpx.decls (открывать Виндосовским Блокнотом) и где-нибудь (желательно рядом с остальными командами, имеющими отношение к джойнтам) прописываем такую строчку: Код:
pxJointHingeSetLimitSpring(joint%, spr#, targetVal#):"_pxJointHingeSetSpring@12" З.Ы. Написал Рендеру, обещал в ближайшей версии исправить. Юзаем: Код:
pxJointHingeSetLimitSpring(joint,10,0) Код:
pxDeleteJoint (joint) Полный код примера вы найдёте в аттаче "PhysXExample14.zip" Следующий пост будет посвящён регдоллу ;) (напишу пост после получения аттестата) |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 3
Ragdoll
Знакомая картина, правда? Для того, чтобы игрок не наблюдал торчащие из стены ноги, существует технология под названием Ragdoll (рас. англ. - "тряпичная кукла"). Суть её такова: все кости трупа являются физическими объектами, в результетае этого они не станут свободно проходить сквозь стены (как если использовать анимацию смерти). Плюс к этому мы получаем всегда разные позы трупов (а не всего лишь 2-3 разных анимации), ну и отскакивание их от пуль, гранат и вообще реалистичное поведение. Хорошие примеры игр, где используется регдолл: Half-Life 2, Painkiller, ну и вообще почти все современные игры (кроме, разве что, стратегий, хотя и там встречается) не обходятся без регдолла. Другое дело, что частенько регдолл сделан паршиво, чисто "для галочки", как, например, в Сталкере или CoD4, или отключены такие плюшки, как реагирование регдоллов на выстрелы (Crysis). Политкорректность, мать вашу :wild: Давайте вообще делать игры только про пушистых кроликов, причём не менее 50% из них должны быть афроамериканцами, женщинами, людьми с нетрадиционной сексуальной ориентацией, чтобы избежать обвинений в дискриминации, и стрелять они друг в друга должны исключительно водяными пистолетиками? Сумасшедшим американским мамашам очень понравится. Создаём простейший регдолл Ну, начнём с простого. Суть данного семпла такова: можно создавать регдоллы в сцене. Пока всё. Как будем делать? Регдолл делается так: 1. Для каждой кости создаётся физическое тело (я буду делать Hull). Можно также делать и сферы, кубы и всё прочее (только не Тримеш), но это всё несерьёзно) ИМХО лучше использовать хуллы. Главное не переборщите с их детализацией, и всё будет ок. 2. Создаются джойнты в тех местах, где кости должны быть соединены. Джойнты настраиваются соответствующим образом. Кроме того, для плечей и голеней лучше использовать Сферические джойнты, т.к. эти сутставы у человека могут крутиться в разные стороны, а вот колени и локти - только в одной плоскости, поэтому для них используем Хиндж. 3. Все кости отпарентиваются (!!!). Это очень важно, т.к. даже если физические тела будут созданы как надо, то представьте, что будет при обновлении (установка кости в позицию и в поворот соотв. тела): обновили чайлд, затем обновили родитель, а т.к. чайлд к нему привязан, то он ушёл с той позиции, где он должен быть, в неизвестное направление, т.к. сдвинулся и повернулся его парент. Ну, а если у того чайлда были тоже чайлды, то понятно, что данный эффект усилится ещё больше и на выходе мы получим не регдолл, а чёрт знает что. Но для начала нам нужна модель с костями. В качестве подопытного я выбрал спецназовца из Counter-Strike: Condition Zero. Можно было бы взять и из 1.6, но там нет русского спецназа :-) Для того, чтобы упростить тутор, я сделал для него новый скелет, попрошче. Уважаемые про-моделлеры, я знаю, что модель ужасна и скелет тоже, но статья не об этом. Кроме того, нам понадобится физическая модель для каждой кости: Обратите внимание: центр каждого объекта совпадает координатами и коротом с центром каждой кости! Это важно (угадайте с одного раза почему) Кроме того, я решил назвать объекты, из которых потом будем делать хуллы, таким образом: его имя - это имя кости, к которой он тоностися + "px". Например, если кость называлась "Bone01", то меш для её физики будет называться "Bone01px". Это нужно для того, чтобы можно было приставив к имени "px" найти нужный меш. В-общем, вооружаемся методом copy-paste (многим из читателей не привыкать ;)) и дописывам где надо px. И да, экспортил без костей. З.Ы. Если возникнут трудности с выставлением центров объектов в максе, могу уточнить. Пишите в обсуждении. Так, модельки нашли. Теперь, наконец, пишем код. Нафиг предыдущие примеры, пишем заново. Итак: Код:
Graphics3D 800,600,32,2 Код:
pxCreateWorld(1,"http://forum.boolean.name/") Код:
pxRenderPhysic(30,0) Код:
Spetsnaz = LoadAnimMesh("Spetsnaz.b3d") хендлы самоуцй модели человека и модели для создания физики. Кроме того, ещё нужны координаты, где етот регдолл создавать. Меш человека копируется. Код:
Function CreateRagdoll(man, px,x#,y#,z#) Код:
If KeyHit(57) Then CreateRagdoll(Spetsnaz, SpetsnazPX,Rand(-100,100),50,Rand(0,100)) Думаю, нам потребуется тип для регдолла. Описываем его: Type Ragdoll Field Mesh ; Моделька Field Joints[100] ; Хендлы джойнтов Field Bodies[100] ; Хендлы тел Field Bones[100] ; Хендлы костей End Type Собсно, так как регдолл - это несколько тел, а не одно, как в предыдущих примерах, то мы создаём уже массив для хендлов тел и ентитей. И плюсь ещё храним хендлы джойнтов, на случай, если нам придётся удалять "куклу" (а джойнты тоже надо удалить). Теперь при создании регдолла создаём новый объект типа Регдолл и пихаем сразу туда хендл модельки. Код:
R.Ragdoll = New Ragdoll Код:
Function BodyCreateHull%(mesh%, mass#) Код:
Function CreateHullsForAllChilds(mesh, pxmesh) А как же с джойнтами? Давайте разбираться. Если у кости был парент (другая кость), то, создав для такой кости хулл, его нужно присоединить к хуллу парента. Отсюда видно, что нужно передавать ещё и хендл родительского хулла, если таковой есть. Назовём его batya :ok: Переделываем функцию, чтобы создавала нам и джойнты: Код:
Function CreateHullsForAllChilds(mesh, pxmesh, batya=0) В таком случае в функцию передаём и хендл регдолла. При создании хулла ищем свободную ячейку массива и суём под этим номером тело и кость. Поскольку джойнтов меньше, чем тел, то и искать ячейку опять надо заново: Код:
Function CreateHullsForAllChilds(mesh, pxmesh, R.Ragdoll, batya=0) Код:
Function CreateRagdoll(man, px,x#,y#,z#) Ну, это уже к физиксу не относится :) Но для полноты картины примеду и эту функцию: Код:
Function DeparentAllChilds(mesh) Применяем при создании регдолла. Код:
Function CreateRagdoll(man, px,x#,y#,z#) Цитата:
Function CreateRagdoll(man, px,x#,y#,z#) man = CopyEntity(man) PositionEntity man,x,y,z R.Ragdoll = New Ragdoll If EntityClass (man) <> "Mesh" Then R\Mesh = GetChild(man,1) EntityParent R\Mesh,0 FreeEntity man Else R\Mesh = Man End If CreateHullsForAllChilds(R\Mesh, px, R) DeparentAllChilds(R\Mesh) End Function Так, а как быть с обновлением? Ну, тут уже намного всё проще. Если вы внимательно читали всё предыдущее, то и сами догадаетесь, как это сделать. В-общем, перебираем все регдоллы: Код:
Function UpdateRagdolls() Код:
Function UpdateRagdolls() Смотри: надо бы им и повороты задать рандомныя. Делаем: Код:
If KeyHit(57) Then CreateRagdoll(Spetsnaz, SpetsnazPX,Rand(-100,100),50,Rand(0,100), Rand(0,360), Rand(0,360), Rand(0,360)) Код:
Function CreateRagdoll(man, px,x#,y#,z#, pitch#,yaw#,roll#) Вообще, конечно, для более реалистичного регдолла нужно настраивать отдельно массы для каждой кости и использовать разные виды сочленений для разных суставов, но об этом я расскажу в ближайших постах. Но пок крайней мере мы получили вполне себе физический спецназ, который ни за что не станет торчать ногами из стены :-D Полный код примера а также все необходимые для запуска ресурсы вы найдёте в аттаче "PhysXRagdollExample1.zip" PhysX Remote Debugger Недавно Рендер познакомил меня со штукой с тким названием. Очень полезная вещь, скажу я вам, а именно она визуализирует всё, что творится в физическом мире. Пользоваться так: запускаем сначала дебаггер, а затем вашу игру с PhysX'ом, можно прямо из Блица. Настройки там элементарные, а польза впечатляет: если вы раньше недоумевали, почему ваш персонаж натыкается на невидимую стену, то теперь вы без турда её увидите. И да, иногда наблюдать забавно. Весёлые картинки: Сам дебаггер лежит в аттаче "RemoteDebugger.zip" Ragdoll и анимация Прочитав (надеюсь) внимательно этот пост, вы спросите: а нафига мы отпарентиваем кости в блице? Не проще ли это сделать в Максе? Ответ прост: сохранение привязок даёт нам возможность использовать анимацию! Давайте сейчас и попробуем. После загрузки меша загрузим в него анимацию: Код:
Walk = LoadAnimSeq(Spetsnaz, "Walk.b3d") И попробуем сделать так, чтобы человеки некоторое время анимировались (т.е. бегали, стреляли в игре). Для этого я завёл новый тип для "живых" спецназовцев. Код:
Type Man Код:
Function CreateMan(Mesh, pxMesh, x#,y#,z#) Теперь при нажатии на пробел создаём не сами регдоллы, а Man'ов: If KeyHit(57) Then CreateMan(Spetsnaz, SpetsnazPX,Rand(-100,100),50,Rand(0,100)) Вот они, кросавчеги, шлёпають: Теперь надо переработать функцию создания регдолла, чтобы она не копировала меш, а создавала регдолл прямо из него. И да, нудно отключить анимацию меша, при физическом взаимодействии она нам не нужна. Думаю, вы и сами с этим справитесь, вот так выглядит переделанная функция: Код:
Function CreateRagdoll(man, px) Function RagDollAllMen() For M.Man = Each Man CreateRagdoll(M\Mesh, M\pxMesh) Delete M Next End Function Ну, и вызываем по левому шифту. Код:
If KeyHit(42) Then RagDollAllMen() Код:
Field PickPivot Код:
M\PickPivot = CreateSphere() При удалении Man'ов не забываем удалять и пивот: Код:
FreeEntity M\PickPivot Код:
Function Shoot(cam, x,y) Вызываем в цикле по нажатию мышки: Код:
If MouseHit(1) Then Shoot(cam, MouseX(), MouseY()) Полный код примера и ресурсы для его запуска вы найдёте в аттаче "PhysXExample2.zip" Для тех, кто просто мимо проходил, есть откомпиленная версия в аттаче "PhysXExample2bin.zip" - оп! Чегой-то в аттач загружать наотрез отказывается: Загрузка файла прошла неудачно. Админы, ау! Пришлось положить сюда. На этом всё. По всем вопросам пишите в обсуждение. Надеюсь, кому-то этот пост оказался полезен. В ближайших постах я расскажу, как настроить регдолл получше, а также, как делаь "капсюльную" физику игрока. |
Ответ: Учебник по PhysX Wrapper для Blitz3D
Вложений: 1
Физическое передвижение игрока с видом как от первого, так и от третьего лица.
(моя свободная наработка, скорее всего финальная для примера версия, так как думаю соблюдены все физ. моменты и условия по ходьбе, прыжке и приседании) Полное физическое взаимодействие с другими объектами и землёй. Мне она сравнима с той, что была в игре TES IV Oblivion. Камера от третьего лица не заходит за объекты, а "выталкивается" перед ними: идёт проверка двух лучей по двум сторонам ширины игрока. (смена вида камеры - клавиша С) Скачать: PhysX-B3D-Movement.zip (исходник, exe и библиотеки прилагаются) |
Часовой пояс GMT +4, время: 19:03. |
vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot