Visual Basic - мастерская разработчика
Библиотеки

DirectX

Обзоры
DirectDraw
Direct3D
DirectX Audio
DirectPlay
DirectInput
Fido Topics
SourceCode
Tools&Libs

OpenGL

Статьи и учебники
Fido Topics
SourceCode
Tools&Libs

Архив по Glide

Движки

Обзоры
Учебники
SourceCode
Downloads

Создание игр

Ваши игры

Обзорные статьи
Учебники
Fido Topics
SourceCode
Download

Stuff

Программер-Чат

Псевдо-FTP
Disclaimer
Оффтопик

 

Обработка столкновений в D3D:RM

Привет тебе читатель!

Эта статья посвящена просчитыванию столкновений в d3drm средствами DirectX7 под VB6. Оговорюсь сразу: написать действующий пример, пользуясь только этой статьей, не выйдет, а чтобы понять о чем идет речь и посмотреть все в действии надо скачать этот проект. Названия функций не претендуют на лексическую верность с точки зрения английского языка. А кое-где я находил оплошности походу написания статьи, но тратить время на испревление не стал, проект ведь работает 8-). Ну вот и все предостережения. Начнем…

Теория

Столкновения в d3drm происходят следующим образом: объявляется объект D3DRMRAY, устанавливаются две координаты Dir и Pos. Pos - это координаты начала луча, Dir - направление вектора, относительно Pos в котором нужно "щупать". Далее посредствам функции RayPick производится само щупанье, с указанием режима. Этих режимов всего пять. Чаще всего используется режим D3DRMRAYPICK_ONLYBOUNDINGBOXES, при его использовании метод RayPick считает проткнутыми все объекты, у которых оказались проткнутыми габаритные контейнеры (Box). Но для нашего случая, когда необходимо, чтобы человечек бегал по неровной поверхности, являющейся одним объектом (Direct3dRMFrame, или Direct3dRMeshBuilder), нам необходимо воспользоваться другим режимом.

Я воспользовался режимом D3DRMRAYPICK_IGNOREFURTHERPRIMITIVES. Честно говоря могу только догадываться, чем он отличается от остальных, но он позволяет сделать то что нам надо, и это главное! Далее необходимо получить список проткнутых объектов при помощи финкции GetPickFrame (или GetPickVisual, взависимости от того какие объекты мы хотим рассматривать: Direct3dRMFrame, или Direct3dRMeshBuilder). Далее я воспользовался методом GetName, для идентификации объектов. Ну вот и вся теория. Я думаю, что практика внесет больше ясности.

Практика

Итак посмотрим на код. Падение разных объектов у меня сделано следующим образом: Каждый объект, способный падать, снабжен функцией Turn, которая вызывается по-очереди для каждого объекта в функции earth главного окна:

Public Sub earth()
.
Arr.Turn
Kubb.Turn
Xlam.Turn
.
.
.
End Sub

сама функция earth вызывается при каждом обороте основного цикла "While Runing = True", находящегося в функции Run главного окна.
Функция Turn каждого объекта предназначена для создания одновременного движения всех объектов, то есть каждый объект, получив управление на себя, должени сделать свои чрезвычайно коротковременные задачи (такие как падение на один метр) и отдать управление дальше.
Класс NPCBody - это класс неуправляемых человечков (тех, что появляются при нажатии кнопки "u"), и функция Turn очень сложна в этом классе, потому что в нее вставлены мозги челвечков. Но нас интересует в ней только строка Call Falien - эта строка вызывает функцию того самого падения на один метр. Давайте посмотрим ее:

Public Function Falien()
Dim TempVector1 As D3DVECTOR
Call Tors.GetPosition(Scene, TempVector1) ' получаем координаты торса (живота) человечка в пространстве относительно сцены. Tors объавлен как Direct3dRMFrame.

H = GetPickedLen(TempVector1.x, TempVector1.y, TempVector1.z, 0, -1, 0, "World") 'А это, уж извините, очередная функция, упрощающая мне жизнь. Она находится в модуле Functions, ее мы еще рассмотрим. Эта функция возвращает координату Y того места, где произошло столкновение луча с объектом.

If H + 13 > TempVector1.y Then 'Если торс человечка находится ниже чем координата столкновения с землей, то падать уже не надо, и мы выходим из функции Falien, не сделав ничего.
GoTo 40
Else 'Если же дело обстоит иначе, то мы сдвигаем торс вниз относительно самого себя на один, и тоже уходим из функции.
Call Tors.SetPosition(Tors, 0, -1, 0)
End If
Call Tors.SetPosition(Tors, 0, -1, 0) ' а вот это глюка - пока не начал писать статью, не замечал, насколько я могу сейчас сообразить - она здесь вообще ни к чему, она просто удваивает скорость нашего падения. Вот такой я рассеяный… *-)
40
End Function

Далее следует рассмотреть функцию GetPickedLen, находящуюся в модуле Functions. Она является еще одним упрощением, для работы с классом RayPic1. Ее можно использовать в ваших собственных программах если вы сталкиваетесь с той же задачей, что и я - с необходимостью определять только координату Y столкновения. (Но для этого надо еще добавить в ваш проект класс RayPic1, и добавить в начало функции строку "Dim SP as Direct3DRMFrame3")

Public Function GetPickedLen(x0 As Single, y0 As Single, z0 As Single, x As Single, y As Single, z As Single, FrameName As String) As Single

Dim k As New RayPic1 ' Это еще один пустячок, от которого приятно. Это объявление класса, который призван помочь нам обращаться с RayPic.

Dim A13 As D3DVECTOR
Dim B13 As D3DVECTOR

A13.x = x0 'Это заполнение вектора Pos, для отправления его классу.
A13.y = y0
A13.z = z0
Call k.SetPos(A13) ' Это само отправление…

B13.x = x 'То же самое по поводу вектора Dir.
B13.y = y
B13.z = z
Call k.SetDir(B13)

Set SP = k.FindFrame(Scene, FrameName) ' Это вызов функции, которая и должна выполнить всю черную работу, и вернуть нам только найденный объект с заданным именем. SP здесь - просто Direct3DRMFrame3, объявленный публично в другом месте (мне казалось что так будет быстрее работать, но это не факт…)
If k.NonZero Then 'k.NonZero говорит нам, нашел ли мой класс вообще чего - нибудь.
If SP.GetName = FrameName Then ' Еще одна лишняя проверка, вспомните мою рассеяность.
GetPickedLen = k.VpositionY: GoTo 40 'Вот функция GetPickedLen возвращает нам Y координату столкновения (проткнутого места в объекте)
End If
End If
GetPickedLen = -1 ' Если не нашел, возвращает -1
40
End Function

Итак возрадуйтесь!! Нам осталось рассмотреть только одну функцию - FindFrame, это функция класса RayPick1.

Public Function FindFrame(ref As Direct3DRMFrame3, Name As String) As Direct3DRMFrame3
Dim i18 As Long
Dim tt As Long
Dim TF1 As Long
ra.Dir = Dir2 'Эти две строки приравнивают полученные до этого координаты из функций SetDir и SetPos, ra.pos = Pos2 'координатам объекта ra, объявленного как D3DRMRAY.
NonZero = False


Set arry = ref.RayPick(ref, ra, D3DRMRAYPICK_IGNOREFURTHERPRIMITIVES) 'Это и есть та самая функция, которая все и делает. А arry - это Direct3DRMPick2Array, коллекция непонятно чего, из которого нам надо получить коллекции фреймов. Причем этих коллекций может быть несколько, а зачем их несколько для меня тоже загадка.

If arry.GetSize > 0 Then
For TF1 = 0 To arry.GetSize - 1 ' Пробегая по элементам этой страшной коллекции, мы должна получить коллекции фреймов при помощи следующей функции:
Set TempFrame1 = arry.GetPickFrame(TF1, Picc )


tt = TempFrame1.GetSize - 1
' Prin = ""
For i18 = 0 To tt 'А этот цикл всего лишь ищет нужный фрейм, сравнивая названия.
If TempFrame1.GetElement(i18).GetName = Name Then
VpositionX = Picc.vPostion.x
VpositionY = Picc.vPostion.y
VpositionZ = Picc.vPostion.z


Set FindFrame = TempFrame1.GetElement(i18)
NonZero = True
GoTo 50
End If
Next i18
Next TF1
End If
50
End Function

Ну вот и все.

Для тех, кто не бросил на полпути
Господа, я согласен с вами: примерчик получился сложноват для понимания, но я старался как мог, расписывая его, и меня оправдывает тот факт, что когда я писал прогу, я не знал, что придется ее кому-то показывать.

Небольшое отступление

Все вышеизложенное касается только того момента, когда человечек свободно падает, а если он решил подобрать какой-нибудь кубик, он всегда будет находться на поверхности земли. Это достигается те ми же методами. Основная мысль при этом заключается в том, что вектор Pos устанавливается в заведомо более высокое, чем земля, положение, а вектор Dir направляется вниз, потом получается координата "протыкания" лучом земли, и человечик устанавливается на нужную высоту. Весь этот процесс происходит в функции LeftBedro_Turn класса NpcBody:

<…>
StepLen = GetPickedLen(TempVector.x, TempVector.y, TempVector.z, 0, -1, 0, "World")'Вот в этой строке мы получаем высоту земли на следующем шаге. TempVector - это и есть координаты следующего положения торса, а "0,-1,0" - это установка направления протыкания вниз. Ну а World - это просто название фрейма, столкновение с которым нас интересует.

If StepLen <> -1 Then ' если чел. Не нашел под собой земли, то функция GetPickedLen вернет -1
Call Tors.SetPosition(Scene, TempVector.x, StepLen + 13, TempVector.z) 'вот это установка человечка в положение следующего шага
Call Tors.SetPosition(Tors, 0, 0, -5)'а вот это - очередная багина (sorry),эта строка просто ускоряет бег человечка.

End If
<…>


Ну вот теперь точно конец!

Тем, кому ОЧЕНЬ надо, могу предложить свою помощь в понимании написанного, или просто в понимании данного вопроса. Пишите! Помогу, чем смогу.
and@profits.com.ru

Скачать проект: collide_s.zip (172k)
PS: Смотрите инструкции в файле readme.txt после распаковки!

Posted: 18.03.2k1
Autor: Volodin Andrey
<and@profits.com.ru>

 

 


Проект
Создание Народного Учебника по OpenGL

Участвовать!
Поиск
Найдите статью или файл:


Рассылка
Новости сайта
La Vision в вашем почтовом ящике








Программирование на С++ Delphi и Паскаль
Центр демо-искусства в России