Глава 29. Простая физика — DCE 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Глава 29. Простая физика — DCE



 

DCE — самое элементарное, что есть из физики в GLScene. Есть, правда, еще FPS Manager, но мы о нем здесь рассказывать не будем. Демка по нему: Demos\behaviours\FPSMovement. Для начала подумаем, зачем он нам? Он нужен для просчета простейшей физики и для обработки столкновений (чтобы персонажи не проходили сквозь стены ну и просто для определения столкновений). Переходим к основной части, постараюсь объяснить всё коротко и ясно.

Как сделать так чтобы персонаж не проходил сквозь стены? В начале поместим на форму GLDCEManager из вкладки GLScene Utils. Далее в GLScene Editor’e выбираем нужный объект, справа над списками нажимаем на Behaviours (если их нет, то нажмите на панели инструментов editor’а кнопку с голубой сеткой), далее смотрим в списке, там есть две нужные нам вещи — это DCE static collider и DCE dynamic collider. Static добавляем для статичных объектов, которые в нашей сцене двигаться не будут, но в них будет упираться персонаж — здания, стены и другие препятствия. Dynamic нужен, соответственно, для динамичных объектов, которые будут перемещаться по сцене, и упираться в статичные и другие динамичные объекты. Когда мы добавили коллайдер, то его строчка появляется в списке Behaviours, нажимаем на нее и в Object Inspector’е видим настройки коллайдера

Свойство Описание
Active Включен или выключен
BounceFactor Прыгучесть
Friction Трение с данным объектом
Manager Менеджер, управляющий столкновениями
Shape Форма объекта.
Size Размеры (если форма csEllipsoid или csBox)
Solid Если стоит True, то объект будет «непроходимым», иначе проходимым.

 

В DCE dynamic collider появляются UseGravity — подчинять ли этот объект гравитации или нет, и SlideOrBounce — будет ли объект при столкновении с другими объектами отскакивать от них. Shape выбирать не придётся, на всех динамических объектах устанавливается форма csEllipsoid.

Всё вышесказанное также можно всё сделать с помощью кода: Для статичных объектов:

With GetOrCreateDCEStatic(имя нужного объекта) do

begin
Manager:= GLDCEManager1;
BounceFactor:= 0.75;
Friction:= 10;
Shape:= csFreeform;
end;

Для динами чных объектов это делается так:

with GetOrCreateDCEDynamic(имя нужного объекта) do

begin

Manager:=GLDCEManager1;

BounceFactor:=0.75;

Friction:=10;

Shape:=csFreeform;

end;

 

На Glactor наложить DCE dynamic collider не получится, поэтому помещаем его в dummy и уже на него накладываем DCE dynamic collider и подгоняем размеры.

Для передвижения динамического объекта по сцене запишем в каденсере:


var

Force: TAffineVector;

Force:= NullVector;

if IsKeyDown(Ord('w')) then Force[2]:= 200 else

if IsKeyDown(Ord('s')) then Force[2]:= -200;

if IsKeyDown(Ord('a')) then Force[0]:= 200 else

if IsKeyDown(Ord('d')) then Force[0]:= -200;

GetOrCreateDCEDynamic(Player).ApplyAccel(Force);

 

Прекрасная демка есть в Demos\behaviours\DCEDemo.

Статья о DCE: http://www.glscene.ru/content.php?article.104


Глава 30. Физика ODE

 

Open Dynamics Engine — физический движок, который использовался в таких известных играх как BloodRayne2, Stalker, Racer и во многих других.

Есть два различных варианта работы с ODE. В GLScene имеются два встроеных компонента для работы с ODE: GLODEManager и GLODEJointList, но можно работать с чистым ODE. Многие начинающие программисты выбирают встроенные компоненты. Но этот «лёгкий» путь оказывается вовсе не таким. Во-первых, с помощью этих компонентов нельзя написать сложной физики, в лучшем случае выйдет простенькая аркадная гонка. Во-вторых, любому программисту даже при использовании ODEManager’а все равно придется использовать немного (а может и много) чистого кода ODE. Ну и, наконец, физика — второй по сложности расчетов элемент 3D игры после графики, а ODE на VCL работает медленнее, чем без VCL. По этим причинам мы не будем рассматривать здесь встроенные компоненты. К тому же, это ненамного сложнее.

Сразу скажу — ODE это не часть GLScene, она не была написана для GLScene, и ODEManager и GLODEJointList является лишь надстройкой над самой библиотекой. Следовательно, можно написать свои компоненты или просто модули, совершенно независимые от встроенных в GLScene.

Clear ODE

 

Для для всех проектов с ODE понадобится модуль ODEImport и, зачастую, ODEGL. Главная цель ODE — рассчитать и обработать взаимодействие предметов в трехмерном мире. Базовое понятие ODE — Мир (dxWorld), в котором находятся все тела. Его необходимо создать в начале. Для этого необходима глобальная переменная типа PdxWorld. Собственно создается мир просто:

var

World: PdxWorld;

World:= dWorldCreate;

dWorldSetGravity(World,0,-9.8,0); // также установили гравитацию

Для мира можно настроить много параметров (притяжение и т.д.), которые можно найти в официальной документации. Одной программе вряд ли понадобиться более одного мира. В мире могут быть несколько подпространств (dxSpace), нам хватит одного (здесь и далее ODE-переменные делаем глобальными):

var

Space: PdxSpace;

Space:=dHashSpaceCreate(nil);

Идем дальше. Реальные твердые объекты имеет форму, в ODE она представлена геометрией (dxGeometry). Создание геометрии:

var

Geom: PdxGeom;

Geom:= dCreateXXX(Space; параметры);


где XXX — вид: Sphere, Box, Plane (бесконечная плоскость), Cylinder, Capsule (цилиндр с полусферами на концах), Ray (луч), TriMesh (типа GLFreeForm), Convex (группа точек) и HeightField (типа GLTerrainRenderer); Space — пространство столкновений; параметры геометрии — конкретные параметры для данной геометрии (радиус для сферы и т.д.). Столкновения геометрий обрабатываются, если они в одном пространстве. Этого уже достаточно для задания неподвижных объектов.

Чтобы придать объекту динамические характеристики, в ODE есть тела (dxBody). Они не имеют формы и размера, но у них есть масса, скорость и т.д. Тела не взаимодействуют друг с другом, на них лишь действуют силы. Создать новое тело также просто:

var

Body: PdxBody;

Body:= dBodyCreate(World);

После создания тело нужно присоединить к геометрии, к одному телу можно присоединить много геометрий для задания объектов сложной формы:

dGeomSetBody (Geom, Body);

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

var

ContactGroup: TdJointGroupID

ContactGroup:= dJointGroupCreate(100);

 

После создания всех этих вещей можно приступить к определению столкновений. Это делается в OnProgress каденсера. Запишем в него следующий код (physics_time — глобальная переменная с текущим временем в физике, ODE_TIMESTEP — константа, шаг времени в расчетах, обычно ~0,01).

// Синхронизируем положение и поворот объекта с

// рассчитанными таковыми параметрами геометрии

PositionSceneObject(obj,Geom);

// За время, потраченное на одну прорисовку сцены,

// можно несколько раз просчитать физику. Это и делаем

while physics_time<newTime do

begin

// Проверяем в nearCallback все возможные пары геометрий

// в нашем пространстве на предмет наличия столкновений

dSpaceCollide(Space,nil,nearCallback);

// Проверили, накопили информацию -- просчитываем мир

dWorldQuickStep(World,ODE_TIMESTEP);

// После просчета очищаем группу контактов

// (она заполнялась в dSpaceCollide)

dJointGroupEmpty(СontactGroup);

// А время идет!

physics_time:=physics_time+ODE_TIMESTEP;

end;

 

Вот типичный код процедуры nearCallback с комментариями.

procedure nearCallback(data:Pointer; o1,o2:PdxGeom); cdecl;

const

cCOL_MAX = 12; // Максимальное обрабатываемое количество

// точек столкновения для двух тел

// Остальные точки игнорируются

var

i: integer;

b1,b2: PdxBody;

numc: integer;

contacts: array[0..cCOL_MAX-1] of TdContact;

contact: TdJointID;

begin

b1:=dGeomGetBody(o1);

b2:=dGeomGetBody(o2);

// Если у проверяемых геометрий есть тела, и они

// соединены сочленением, то ODE позаботится о них сам

if (Assigned(b1))and(Assigned(b2)and(dAreConnected(b1,b2)<>0)) then

Exit;

 

// Устанавливаем параметры для будущих контактов,

// которые повлияют на то, как "разойдутся" тела

// mu -- трение, bounce -- пружинистость

for i:=0 to cCOL_MAX-1 do

begin

contacts[i].surface.mode:=dContactBounce;

contacts[i].surface.mu:=0.7;

contacts[i].surface.mu2:=0;

contacts[i].surface.bounce:=0;

contacts[i].surface.bounce_vel:=0;

end;

 

// Проверяем геометрии на наличие точек соприкосновения и

// получаем их количество. В каждой точке будет создан контакт.

// Контакт -- сочленение, созданное между телами

// для обработки столкновения тел

numc:=dCollide(o1,o2,cCOL_MAX,contacts[0].geom,SizeOf(TdContact));

// Если тела столкнулись (есть хоть одна точка столкновения)

if (numc>0) then

for i:=0 to numc-1 do

begin

// Создаем контакт

contact:=dJointCreateContact(World,ContactGroup,@contacts[i]);

// И соединяем им тела

dJointAttach(contact,b1,b2);

// Далее ODE будет оперировать телами в соответствии

// с ранее установленными параметрами контакта

// (mu, bounce и т.д.)

end;

end;

Чтобы лучше понять механизм проверки столкновений, приведу выдержку из документации ODE:

«Допустим, у нас есть две машины, перемещающиеся по поверхности земли. Каждая машина состоит из множества Геометрий. Если всю Геометрию машин разместить в одном пространстве, то время необходимое на определение столкновения между машинами будет пропорционально общему количеству Геометрии (или даже количеству геометрии в квадрате в зависимости от типа пространства).

Увеличить скорость определения столкновений можно, представив каждую машину своим пространством. Геометрию машин разместить в пространстве-машин (car-spaces), а пространства машин разместить в пространстве высшего уровня. На каждом шаге времени dSpaceCollide будет вызываться для пространства высшего уровня. Здесь будет происходить проверка на пересечение пространств-машин между собой (а именно между ограничивающими параллелепипедами) и, если пересечение произошло, будет вызываться функция обратного вызова (nearCallback). Функция обратного вызова затем может проверить геометрию в пространствах-машин с помощью функции dSpaceCollide2. Если машины находятся не рядом, то функция обратного вызова не будет вызываться. Таким образом не будет впустую тратиться время на ненужный тест».

Немного поясню.

procedure dSpaceCollide (const Space: PdxSpace; data: pointer; callback: TdNearCallback);

Определяет геометрию, которая потенциально может столкнуться в пространстве и вызывает nearCallback. Эта процедура определяет, что делать со столкнувшейся геометрией. Указатель data может использоваться для передачи данных, установленных пользоывателем, а если таких данных нет, устанавливайте его в nil.

procedure dSpaceCollide2 (o1, o2: PdxGeom; data: pointer; callback: TdNearCallback);

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

Также упомянем сочленения (dxJoint). Они позволяют прикреплять одно тело к другому каким-либо образом. Как, например, в простейшем случае создается машина? Очень просто: для корпуса создается Box, для колес — четыре Sphere. Потом к этому Box’у по углам прикрепляются Sphere’ы. Типы сочленений:

Ball соединение «шарик в разъеме»
Hinge «сгибание» (типа циркуля)
Hinge2 «сгибание 2» (одно тело может вращаться, типа локоть + кисть или колесо автомобиля с подвеской)
Slider «скользящее» соединение (поршень в насосе)
Universal «универсальное» соединение (Hinge с двумя осями сгибания)
Fixed «фиксированное» соединение
Contact предотвращает проникновение геометрий (рассмотрено ранее)

 

Подробнее о них см. в документации (ссылка в конце статьи). Упомянем только, что, подобно другим объектам, сочленения создаются через dJointCreate***, а их параметры устанавливаюся и получаются через dJointGet*** и dJointSet***. Тела соединяются через dJointAttach. Некоторые параметры сочленений (не все они есть у всех видов!):

Параметр Описание
dParamBounce Упругость сочленения
dParamLoStop, dParamHiStop Пределы поворота или смещения
dParamVel Желаемая скорость (например, в Hinge —скорость вращения)
dParamFMax Hinge2: максимальная сила или вращающий момент, который двигатель будет использовать для достижения желаемой скорости
dParamFudgeFactor Hinge2: это надстроечный показатель, используется для масштабирования избыточной силы. Если в сочленении видны скачки движения, его значение должно быть уменьшено
dParamCFM, dParamStopERP, dParamStopCFM, dParamSuspensionERP, dParamSuspensionCFM Параметры уменьшения ошибок. Можно сказать, определяют жесткость и упругость сочленения (силу реакции при попытке какого-то параметра выйти за пределы LoStop - HiStop)


Последнее — об уничтожении объектов. Все объекты в конце программы должны быть уничтожены вызовом

dXXXDestroy(объект);

где XXX — тип объекта (Body, World, Space, Joint и т.д.).


Полезнейшие ссылки:

http://ode.org/doc/russian — официальная документация по ODE на русском языке. Она сильно устарела, но основы остались те же

http://glscene.ru/download.php?view.564 — простой и понятный пример для начинающих

http://glscene.ru/download.php?view.382 — более полный пример, специально для шутеров

http://glscene.ru/content.php?article.85 — статья к предыдущему примеру

http://glscene.ru/download.php?view.233 — навороченный пример гонок

http://opende.sourceforge.net/wiki —хороший англоязычный обновляемый мануал

 


Глава 31. Физика Newton

 

Физический движок Ньютон не сложен в изучении. К тому же, если вы разобрались с ODE, вам будет нетрудно освоить и Ньютон — во многом они похожи (и поэтому опишем его короче).

Для работы с ньютоном необходимы Newton.dll и NewtonImport.pas. Как и другие физические движки, Ньютон имеет мир, который создается так:

var

NewtonWorld: PNewtonWorld;

NewtonWorld:= NewtonCreate(nil, nil);

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

NewtonDestroy(NewtonWorld);

Симулируется мир командой (ее необходимо прописать в каденсере):

NewtonUpdate(NewtonWorld, DeltaTime);

Твердое тело в ньютоне имеет тип PNewtonBody. При его создании указываются мир, в который оно вставляется, и коллизия (геометрическая оболочка тела, по которой определяются столкновения). Существует 10 типов коллизий: Null (прозрачная коллизия), Box, Sphere, Cone, Capsule, Cylinder, ChamferCylinder (цилиндр со сглаженными краями), ConvexHull (выпуклая оболочка), TreeCollision ( для GLFreeform полигональная коллизия произвольной сложности (только для статических объектов)), CompoundCollision — составная коллизия.

Создается коллизия функцией NewtonCreateXXX, где XXX — тип коллизии.

Масса тела в ньютоне задается процедурой

NewtonBodySetMassMatrix(NewtonBody, mass, Ixx, Iyy, Izz);

здесь Ixx, Iyy, Izz — моменты инерции для каждой оси.

Имитация силы тяжести реализуется callback функцией:

procedure ForceAndTorqueCallback(const body: PNewtonBody); cdecl;

var

Mass: Single;

Inertia: TVector3f;

Force: TVector3f;

begin

NewtonBodyGetMassMatrix(Body, @Mass, @Inertia.x, @Inertia.y,@Inertia.z);

Force:= V3(0, -9.8 * Mass, 0);

NewtonBodyAddForce(Body, @Force.x);

end;

Эта функция постоянно вызывается для тел, которым она назначена. Назначить ее можно так:

NewtonBodySetForceAndTorqueCallBack(NewtonBody, ForceAndTorqueCallBack);

Cоздадим, к примеру, куб:

procedure NewCube;

var

NewtonBody: PNewtonBody;

Collision: PNewtonCollision;

Matrix: TMatrix4f;

begin

Collision:= NewtonCreateBox(NewtonWorld, 1, 1, 1, nil);

NewtonBody:= NewtonCreateBody(NewtonWorld, Collision);

NewtonReleaseCollision(NewtonWorld, Collision);

NewtonBodySetMassMatrix(NewtonBody, 3, 0.5, 0.5, 0.5);

NewtonBodySetForceAndTorqueCallBack(NewtonBody,

ForceAndTorqueCallBack);

end;

Чтобы увидеть, что получилось, в каденсер добавляем:

var

Matrix: TMatrix4f;

NewtonUpdate(Newton,deltaTime);

NewtonBodyGetMatrix(NewtonBody, @Matrix[0,0]);

GLCube.Matrix:=Matrix;

Для изменения положения тела в пространстве, необходимо изменить матрицу трансформации:

var

Matrix: TMatrix4f;

NewtonBodyGetMatrix(NewtonBody, @Matrix[0,0]);

Matrix[3,0]:= x; Matrix[3,1]:= y; Matrix[3,2]:= z;

NewtonBodySetMatrix(NewtonBody, @Matrix[0,0]);

 

Ссылки:

Официальный сайт: http://www.newtondynamics.com

Пример на Newton 2.10: http://www.glscene.ru/download.php?view.471

И статья: http://www.glscene.ru/content.php?article.105

Статья о т.н. материалах Newton: http://www.glscene.ru/content.php?article.108



Поделиться:


Последнее изменение этой страницы: 2016-04-19; просмотров: 456; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.116.36.192 (0.077 с.)