Игра Direct3D9: камера космического корабля

Я работаю над космическим симулятором Direct3D9, в котором мне нужно создать камеру, которая будет удерживать позицию и точку зрения космического корабля игрока. На данный момент я ограничил свой код только для перемещения вперед и назад, вверх и вниз, а также для стрельбы. Ниже мой код, и у него есть проблема. Все заключено в класс, и все векторы инициализируются в конструкторе D3DXVECTOR3 (0.0f, 0.0f, 0.0f), за исключением LocalUp (D3DXVECTOR3 (0.0f, 1.0f, 0.0f)) и LocalAhead (D3DXVECTOR3 (0.0f) , 0.0f, 1.0f)) и значения с плавающей запятой установлены на 0.0f;

D3DXVECTOR3 Position, LookAt ,PosDelta, PosDeltaWorld, WorldAhead, WorldUp, LocalUp,
LocalAhead, Velocity;
D3DXMATRIX View, CameraRotation;
float SpeedX, SpeedY, SpeedZ;

void Update(float ElapsedTime)
{
SpeedX = 0.0f;
SpeedY = 0.0f;

if(IsKeyDown('A'))
{
SpeedX = -0.02f;
Velocity.x -= SpeedX;
}
if(IsKeyDown('D'))
{
SpeedX = 0.02f;
Velocity.x += SpeedX;
}
if(IsKeyDown('X'))
{
SpeedZ += 0.01f;
Velocity.z += SpeedZ;
}
if(IsKeyDown('Z'))
{
SpeedZ -= 0.01f;
Velocity.z -= SpeedZ;
}
if(IsKeyDown('W'))
{
SpeedY = 0.02f;
Velocity.y += SpeedY;
}
if(IsKeyDown('S'))
{
SpeedY = -0.02f;
Velocity.y -= SpeedY;
}

D3DXVec3Normalize(&Velocity, &Velocity);

PosDelta.x = Velocity.x * SpeedX;
PosDelta.y = Velocity.y * SpeedY;
PosDelta.z = Velocity.z * SpeedZ;

D3DXMatrixRotationYawPitchRoll(&CameraRotation, 0, 0, 0);
D3DXVec3TransformCoord(&WorldUp, &LocalUp, &CameraRotation);
D3DXVec3TransformCoord(&WorldAhead, &LocalAhead, &CameraRotation);
D3DXVec3TransformCoord(&PosDeltaWorld, &PosDelta, &CameraRotation);

Position += PosDeltaWorld;
LookAt = Position + WorldAhead;

D3DXMatrixLookAtLH(&View, &Position, &LookAt, &WorldUp);
}

Функции «D3DXMatrixPerspectiveFovLH» и «IDirect3DDevice9 :: SetTransform» вызываются в другой части приложения. Поскольку они работают нормально, я больше не буду говорить о них.

Проблема заключается в том, что всякий раз, когда скорость по оси Z достаточно велика, и я двигаюсь в поперечном направлении, по отдельности или в одно и то же время, скорость по оси Z камеры будет уменьшаться. Более того, после того, как скорость почти равна 0, а затем я нажимаю клавишу, которая увеличивает скорость, смысл вектора инвертируется, а затем возвращается в нормальное состояние. Это также происходит при изменении смысла вектора на довольно высоких скоростях (например, нажатие X, затем немедленное нажатие «Z»). Кто-нибудь может объяснить мне, почему это происходит и как я могу решить эту проблему?

Я также задам еще один вопрос: как я могу медленно уменьшить скорость и скорость по оси Y, если ни одна клавиша не нажата? Я хочу, чтобы в игре был реализован эффект инерции.

Если кто-нибудь сможет мне помочь, пожалуйста, ответьте!

РЕДАКТИРОВАТЬ: НОВЫЙ КОД:

void NewFrontiers3DEntityPlayer::OnFrameUpdate(float ElapsedTime)
{
State.SpeedX = 0.0f;
State.SpeedY = 0.0f;
if(IsKeyDown(State.Keys[CAM_STRAFE_LEFT]))
State.SpeedX = -0.02f;
if(IsKeyDown(State.Keys[CAM_STRAFE_RIGHT]))
State.SpeedX = 0.02f;
if(IsKeyDown(State.Keys[CAM_MOVE_FORWARD]))
{
State.SpeedZ += 0.01f;
}
if(IsKeyDown(State.Keys[CAM_MOVE_BACKWARD]))
{
State.SpeedZ -= 0.01f;
}
if(IsKeyDown(State.Keys[CAM_MOVE_UP]))
State.SpeedY = 0.02f;
if(IsKeyDown(State.Keys[CAM_MOVE_DOWN]))
State.SpeedY = -0.02f;

State.Velocity.x = State.SpeedX;
State.Velocity.y = State.SpeedY;
State.Velocity.z = State.SpeedZ;

D3DXVec3Normalize(&State.Velocity, &State.Velocity);

State.PosDelta.x = State.Velocity.x * ElapsedTime;
State.PosDelta.y = State.Velocity.y * ElapsedTime;
State.PosDelta.z = State.Velocity.z * ElapsedTime;

D3DXMatrixRotationYawPitchRoll(&State.CameraRotation, 0, 0, 0);
D3DXVec3TransformCoord(&State.WorldUp, &State.LocalUp, &State.CameraRotation);
D3DXVec3TransformCoord(&State.WorldAhead, &State.LocalAhead, &State.CameraRotation);
D3DXVec3TransformCoord(&State.PosDeltaWorld, &State.PosDelta, &State.CameraRotation);

State.Position += State.PosDeltaWorld;
State.LookAt = State.Position + State.WorldAhead;

D3DXMatrixLookAtLH(&State.View, &State.Position, &State.LookAt, &State.WorldUp);

return;
}

«Состояние» — это структура, которая содержит всю информацию о камере.

0

Решение

Я предполагаю, что ваша скорость меняется, когда вы движетесь в нескольких направлениях одновременно, потому что вы нормализуете свою скорость.

Например, перемещение по Z:

Velocity = (0, 0, 0.01)
Speed = (0,0, 0.01)
Normalized Velocity = (0, 0, 1)
PosDelta = (0, 0, 0.01)

и движется в X + Z:

Velocity = (0.02, 0, 0.01)
Speed = (0.02, 0, 0.01)
Normalized Velocity = (0.897, 0, 0.435)
PosDelta = (0.018, 0, 0.0044)

Что касается вашей инверсии направления, я предполагаю, что это может быть частично связано с вашим относительно странным методом использования скорости / скорости (см. Ниже) и, возможно, из-за неточности поплавков. Что касается последнего пункта, что, по вашему мнению, выводит следующий код (не обращая внимания на потенциальную оптимизацию компилятора):

float test1 = 0f;

test1 += 0.1f;
test1 += 0.1f;
test1 += 0.1f;
test1 += 0.1f;
test1 += 0.1f;

test1 -= 0.1f;
test1 -= 0.1f;
test1 -= 0.1f;
test1 -= 0.1f;
test1 -= 0.1f;

printf("%g\n", test1);

Это (вероятно?) Не будет выводить очевидный ответ 0 поскольку 0.1 не может быть точно представлен в базе-2. Это печатает 1.49012e-008 в моей системе. Может случиться так, что вы близки к 0, но не совсем, что может привести к появлению инверсии координат. Вы можете избавиться от этого, округлив скорости с определенной точностью.

Ваш общий метод управления скоростью / скоростью / положением немного странный и может быть источником ваших трудностей. Например, я бы ожидал Velocity быть вектором, представляющим скорость игрока, а не нормализованным вектором, как у вас. Я хотел бы сделать что-то вроде:

SpeedX = 0.0f;
SpeedY = 0.0f;
SpeedZ = 0.0f

if(IsKeyDown('A')) SpeedX += -0.02f;
if(IsKeyDown('D')) SpeedX += 0.02f;
...

Velocity.x += SpeedX;
Velocity.y += SpeedY;
Velocity.z += SpeedZ;

D3DXVECTOR3 NormVelocity;
D3DXVec3Normalize(&NormVelocity, &Velocity);

//Round velocity here if you need to
Velocity.x = floor(Velocity.x * 10000) / 10000.0f;
...

float FrameTime = 1;  //Use last frame time here

PosDelta.x = Velocity.x * FrameTime;
PosDelta.y = Velocity.y * FrameTime;
PosDelta.z = Velocity.z * FrameTime;

Это избавляет вас от изменения скорости при движении в нескольких направлениях. Это также позволяет вам правильно компенсировать изменение частоты кадров, если вы установите FrameTime быть временем последнего кадра (или значением, полученным из него). Это также правильно останавливает игрока, когда он пытается двигаться в двух противоположных направлениях одновременно.

Что касается вашего последнего вопроса относительно затухания Y-скорости, есть несколько способов сделать это. Вы можете просто сделать что-то вроде:

Velocity.y *= 0.7f;

каждый кадр (отрегулируйте константу в соответствии с вашими потребностями). Более точной моделью было бы сделать что-то вроде:

if (Velocity.y > 0) {
Velocity.y -= 0.001f;    //Pick constant to suit
if (Velocity.y < 0) Velocity.y = 0;
}

Еще лучшим способом было бы использовать время последнего кадра для учета различной частоты кадров, например:

Velocity.y -= FrameTime * 0.2f;    //Pick constant to suit
1

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector