[Главная] [Новости] [Статьи] [Игры] [Проекты] | [Автор] | |||
Основы 3D-программирования DirectX8.1 в Delphi 6-7: теоретические и практические основы создания игр. | ||||
Камера замкнутого пространства |
||||
[Все уроки] | ||||
Постановка задачи: рассмотрение способов реализации 2-х основных видов камер - камеры замкнутого пространства (Indoor scene) и камеры открытого пространства (Outdoor scene). Их основное отличие - привязка камеры к определенной точке (объекту) и возможность свободного перемещения. | ||||
Сначала рассмотрим как реализована камера в игровой сцене 1 для замкнутого пространства, с привязкой к определенной точке и значительными ограничениями по перемещениям. | ||||
В модуле Main.pas класс игровой камеры описан в виде TD3DGameCamera = class(CD3DCamera) Как видим он является производным от стандартной камеры описанной в модуле D3DUtil.pas. Чтобы убедиться в этом наведите указатель мыши на слово CD3DCamera. Среда Delphi высветит название типа type D3DUtil.CD3DCamera. Если данной подсказки не появилось, то возможно модуль не скомпилирован, попробуйте дать команду Build All, а после этого Compile. После этого все модули будут перекомпилированы и указанная подсказка должна высвечиваться. |
||||
CD3DCamera - если заглянуть в реализацию этого класса, то можно увидеть, что основное назначение - это работа с плоскими спрайтами которые широко представлены в проекте Donuts3D. В остальном же - это камера свободного, но ограниченного пространства, с привязкой к объекту игрока и без возможностей свободного вращения. Есть возможность только переключения между тремя видами: со стороны, от третьего лица, от первого лица. Всё это наглядно показано в реализации проекта Donuts3D - выбранный игроком объект (самолет, вертолет, космический странник или что-то еще) перемещается, а вместе с ним и камера. | ||||
В задачи класса TD3DGameCamera входит некоторое расширение возможностей стандартной камеры. В уроке Отображение моделей и работа с камерой уже упоминалось то, каким образом можно подойти к решению поставленной задачи. Там же упоминались возможные трудности, которые могут возникнуть в этом случае. | ||||
Теперь же, когда все трудности уже разрешены мы рассмотрим окончательный вариант того, что можно получить. | ||||
Внутри класса игровой сцены описываем поле для камеры - SceneCamera: TD3DIndoorCamera; Не удивляйтесь, что в качестве имени класса взято TD3DIndoorCamera. Просто поскольку мы будем делать камеру уже не для 1 игровой сцены, а для 5-ой, то и класс камеры будет несколько иной, хотя и подобный TD3DGameCamera. Конкретные детали Вы решите для себя сами, мы же здесь затронем только основные особенности реализации. |
||||
В методе создания игровой сцены - constructor TGameScene5.Create; обнуляем поле камеры - SceneCamera := nil; |
||||
В методе - function TGameScene5.DoInitDeviceObjects создаем экземпляр класса игровой камеры - SceneCamera := TD3DIndoorCamera.Create; и
сразу же устанавливаем параметры ее вида и проецирования В методе - function TGameScene5.DoRestoreDeviceObjects устанавливаем еще ряд важных параметров - обработчик класса окна приложения, ссылка на D3DDevice, D3DInput, размеры окна отображения и ссылку на класс игровой сцены. with
g_d3DApp do |
||||
В методе уничтожения игровой сцены - function TGameScene5.DoDeleteDeviceObjects аккуратно очищаем ресурсы памяти от экземпляра класса камеры - SAFE_DELETE(SceneCamera); |
||||
В методе DoInvalidateDeviceObjects камера не нуждается, да и размещение установки дополнительных параметров внутри DoRestoreDeviceObjects сделано только для того, чтобы гарантировать действительные данные во всех передаваемых параметрах. Иначе может получиться, что например, окно класса приложения еще не до конца проинициализировано и указатель содержит мнимые данные, что естественно приведет к нежелательным ошибкам времени выполения. Это особенно актуально, когда класс игровой сцены запускается не посредством загрузчика TSceneLoader, а прямо из класса игрового приложения. | ||||
Итак, остаются только два метода - DoFrameMove и DoRender внутри которых для камеры нужно тоже описать какие-то действия - по аналогии со всеми игровыми объектами, которые проходят эти этапы - Create, DoInit, DoInvalidate, DoRestore, DoFrameMove, DoRender, DoDelete - (см. урок Вывод Заставки). | ||||
Поскольку камера не является отрисовываемым объектом, а сама задает параметры отрисовки являясь внутренним объектом устройства отображения DirectX, то внутри метода DoRender делать ничего не нужно. | ||||
Остается только DoFrameMove - Здесь всё просто - внутри метода UpdateInput, чтобы гарантировать обработку управления камерой пользователем и избежав двойного вызова метода m_DXInput.Update; о чём вы найдете строгий комментарий внутри метода UpdateInput класса камеры TD3DIndoorCamera - где-нибудь ближе к концу реализаций метода, а лучше вообще в самом конце вызываем лишь 2 метода - SceneCamera.UpdateInput; Первый из них производит обработку управления камерой, а второй выполняет фактические перемещения и изменения ориентации. |
||||
Всё. | ||||
Как видим, если разобраться, работа с камерой достаточно проста. Все особенности кроются в реализации класса камеры TD3DIndoorCamera (или как Вы там ее назовете - не суть важно). В секции interface этот класс камеры можно описать так - TD3DIndoorCamera
= class(CD3DCamera) m_UserInput: TCameraControl; function ScreenToVector(sx, sy: Integer): TD3DXVector3; procedure SetupBoundingBox(Box: TBoundingBox);
protected
vPos: TD3DXVector3;
m_qDown: TD3DXQuaternion; // Quaternion before button down
m_iWidth: Integer; // ArcBall's window width
m_pd3dDevice: IDirect3DDevice8; TimeLapsed: Single;
GameScene: TD3D_GDOListItem;
constructor Create;
procedure MoveCamera; |
||||
Что нужно сделать, чтобы заставить камеру вращаться вокруг определенной точки пространства, т.е. свободно изменять ориентацию, плюс обеспечить некоторую минимальную свободу перемещений. | ||||
В первую очередь, если Вы когда-нибудь заглядывали в dx-help нужно выставить начальное положение и ориентацию камеры. Всё это можно сделать уже на этапе ее создания, т.е. внутри метода constructor
TD3DIndoorCamera.Create;
D3DXQuaternionIdentity(m_qDown); //задаем
начальные ориентации - 2 кватерниона и матрица
D3DXQuaternionRotationMatrix(m_qNow, m_matRotation); //эти
методы здесь не нужны, но демонстрируют как
D3DXMatrixIdentity(m_matTranslation); //также
нужно выставить начальную матрицу положения и смещения ZeroMemory(@m_UserInput, SizeOf(m_UserInput)); //обнуляем поле состояний элементов управления камерой end; |
||||
При разрушении экземпляра класса камеры не забываем обнулить значимые поля - destructor
TD3DIndoorCamera.Destroy;
inherited Destroy; Это уменьшает счетчики Reference Counting которые учитываются в системных объектах DirectX работающих, как известно, на принципах технологии COM. Т. е. когда на объект ссылается другой объект счетчик ссылок увеличивается, а в момент когда не нуждается - уменьшается. Нужный системный объект DirectX остается в памяти пока его счетчик ссылок не достигнет нуля. После этого он сам удаляет себя из памяти. Всё это описано в dx-help в разделе посвященном особенностям COM-технологии. |
||||
Рассмотрим реализацию метода установки дополнительных параметров камеры - procedure
TD3DIndoorCamera.SetupCamera(ph_wnd: HWND; pd3dDevice: IDirect3DDevice8;
DXInput: TDXInput; const var GameScene := aGameScene; //ссылку на игровую сцену
// Set up the camera m_fRadiusTranslation := Radius;
// Set the projection matrix
D3DXMatrixPerspectiveFovLH(m_matProj, D3DX_PI/4, fAspect, //метода, а дальше используется только в готовом виде никак не изменяясь, поскольку параметры перспективы на //протяжении нашего приложения остаются неизменны!
//**
with BoundingBox do begin //данное
поле задает ограничивающий бокс для перемещений камеры в сцене end; |
||||
Самое интересное о том, как происходит управление камерой находится внутри метода - var procedure
TD3DIndoorCamera.UpdateInput;
begin
Windows.GetCursorPos(MouseCursorPos);
//m_DXInput.Update; //вызывается в TGameScene5.UpdateInput
m_UserInput.bCameraMove := CameraMove in m_DXInput.States;
else
D3DXMatrixTranslation(m_matTranslationDelta, 0.0, 0.0, 5*fDeltaY);
// Keep object in bounds in Z
if (vPos.z < Fzmax) then
//unlimited camera end;
if m_UserInput.bCameraMove then begin
D3DXMatrixTranslation(m_matTranslationDelta, -2*fDeltaX, 2*fDeltaY, 0.0);
// Keep object in bounds in X
if (vPos.x < Fxmax) then
// Keep object in bounds in Y
if (vPos.y > Fymax) then
{unlimited camera}
m_qDown:= m_qNow;
if m_UserInput.bCameraRotate then vCur := ScreenToVector(MouseCursorPos.X, MouseCursorPos.Y);
D3DXQuaternionAxisToAxis(qAxisToAxis, s_vDown, vCur); D3DXQuaternionMultiply(m_qNow, m_qNow, qAxisToAxis); D3DXMatrixRotationQuaternion(m_matRotation, m_qNow); end;
В чём смысл указанных выше строк кода? В ответ на нажатия определенных, заданных в приложении клавиш или действий мышью и ее кнопок происходит изменение положения и повороты камеры. Следует заметить, что камера должна вращаться не вокруг своей оси, как это будет происходить, если для нее просто задана матрица вращения, а вокруг некоторого центра. Реализация этого способа показана далее. |
||||
Магический метод, который приводит к действительным результатам перемещений и вращений камеры -
procedure TD3DIndoorCamera.MoveCamera; begin
D3DXQuaternionIdentity(TD3DXQuaternion(qEye)); //задаем
начальный кватернион положения глаза наблюдателя //ориентации
vEyePt.x := qEye.x; //задаем
полученные параметры в качестве положений глаза и точки зрения
vLookAtPt.x := vPos.x; vUpVec := D3DXVECTOR3( 0.0, 1.0, 0.0); //вектор Up камеры остается постоянным D3DXMatrixLookAtLH(matView, vEyePt, vLookatPt, vUpVec); //строим матрицу вида для левосторонней координатной //системы D3DXMatrixMultiply(matView, matView, m_matRotation); //перемножаем матрицу вида на матрицу момента вращения
// Set up view matrix
//SetViewParams |
||||
И остается показать реализацию 2-х дополнительных методов - procedure
TD3DIndoorCamera.SetupBoundingBox(Box: TBoundingBox);
Fxmax := Box.UpperRightCorner.x; Данный метод демонстрирует как экранные координаты переводятся в векторные
if m_bRightHanded then
z := 0.0;
if (mag > 1.0) then
// Return vector |
||||
Если Вы испробовали код описанный выше и разобрались в чём суть работы камеры у Вас должен возникнуть вопрос: Почему же данная камера не совсем удовлетворяет поставленной цели - она не вращается вокруг указанной точки пространства (0, 0, 0). Исправим этот недостаток - откройте реализацию метода procedure TD3DIndoorCamera.MoveCamera и найдите строку кода - D3DXMatrixMultiply(matView, matView, m_matRotation); закомментируйте ее и перекомпилируйте код. Поставленная задача решена. |
||||
Следует отметить, что конкретные параметры расположения и ограничения перемещений Вы должны подобрать самостоятельно для своей камеры. Выше приведены рабочие параметры без ограничения перемещений камеры. | ||||
[Назад] [Все уроки] | ||||
[Главная] [Новости] [Статьи] [Игры] | ||||
(c) Мега Информатик 2006-2008 |