Есть камера, повёрнутая вокруг оси Y, что смотрит вверх, и вокруг оси Z (в движке она является одной из горизонтальных). При достижении края экрана необходимо взять вектор, повёрнутый так же, как текущий поворот вокруг Y, но параллельный террэйну, т.е. не учитывающий прочие повороты. Пробовал, как здесь приводить к единичному виду все повороты, кроме Y, но получалось что-то совсем не то. Если убирать только одну из ненужных горизонтальных осей, то нормально, но до первого разворота на 180 градусов - дальше начинает ездить в направлении глобальной оси X или Z, а не локального «в бок» или «прямо». Сейчас убрал обнуление осей, и теперь всегда берётся глобальная ось, хотя перемещение и идёт в локальных координатах (у translate есть дополнительный параметр с пространством перемещения):
void LevelCamera::VerticalTranslate(float amount)
{
Quaternion currentRot = _cameraNode->GetRotation();
Matrix3 rotation = currentRot.Inverse().RotationMatrix();
Vector3 rotated = rotation * Vector3(0,0,amount);
_cameraNode->Translate(rotated);
}
void LevelCamera::HorizontalTranslate(float amount)
{
Quaternion currentRot = _cameraNode->GetRotation();
Matrix3 rotation = currentRot.Inverse().RotationMatrix();
Vector3 rotated = rotation * Vector3(amount,0,0);
_cameraNode->Translate(rotated);
}
void LevelCamera::Update()
{
UI* ui = GetSubsystem<UI>();
Input* input = GetSubsystem<Input>();
Graphics *graphics = GetSubsystem<Graphics>();
if (ui->GetFocusElement())
return;
if(input->GetMousePosition().y_ < BORDER_OFFSET)
VerticalTranslate(CAMERA_VELOCITY);
if(input->GetMousePosition().y_ > graphics->GetHeight() - BORDER_OFFSET)
VerticalTranslate(-CAMERA_VELOCITY);
if(input->GetMousePosition().x_ < BORDER_OFFSET)
HorizontalTranslate(CAMERA_VELOCITY);
if(input->GetMousePosition().x_ > graphics->GetWidth() - BORDER_OFFSET)
HorizontalTranslate(-CAMERA_VELOCITY);
if (input->GetMouseButtonDown(MOUSEB_RIGHT))
{
IntVector2 mouseMove = input->GetMouseMove();
_yaw += MOUSE_SENSITIVITY * mouseMove.x_;
_pitch += MOUSE_SENSITIVITY * mouseMove.y_;
Vector3 hitPos;
Drawable* hitDrawable;
if(Raycast(250.0f, hitPos, hitDrawable, true))
{
_cameraNode->RotateAround(hitPos, Quaternion(_yaw, Vector3(0,1,0)), TS_WORLD);
if(abs((int)_pitch) < 20 )
{
if(_cameraNode->GetRotation().PitchAngle() < 45 && _pitch < 0)
_cameraNode->RotateAround(hitPos, Quaternion(_pitch, _cameraNode->GetDirection().CrossProduct(Vector3(0,1,0))), TS_WORLD);
if(_cameraNode->GetRotation().PitchAngle() > 10 && _pitch > 0)
_cameraNode->RotateAround(hitPos, Quaternion(_pitch, _cameraNode->GetDirection().CrossProduct(Vector3(0,1,0))), TS_WORLD);
}
}
_yaw = 0;
_pitch = 0;
}
int wheelMove = input->GetMouseMoveWheel();
if( wheelMove != 0 && abs((int)wheelMove) < 2)
{
Camera *camera = _cameraNode->GetComponent<Camera>();
float oldZoom = camera->GetZoom();
if(oldZoom > 0.5f && wheelMove < 0)
camera->SetZoom(oldZoom + wheelMove*WHEEL_SENSITIVITY);
if(oldZoom < 4 && wheelMove > 0)
camera->SetZoom(oldZoom + wheelMove*WHEEL_SENSITIVITY);
}
}