corrected moving ship added moving camera

This commit is contained in:
Ariari04 2025-12-29 18:59:06 +06:00
parent 47fe0c4719
commit 96deb8b393
2 changed files with 783 additions and 718 deletions

211
Game.cpp
View File

@ -1,4 +1,4 @@
#include "Game.h" #include "Game.h"
#include "AnimatedModel.h" #include "AnimatedModel.h"
#include "BoneAnimatedModel.h" #include "BoneAnimatedModel.h"
#include "Utils.h" #include "Utils.h"
@ -21,11 +21,11 @@ namespace ZL
Vector4f generateRandomQuaternion(std::mt19937& gen) Vector4f generateRandomQuaternion(std::mt19937& gen)
{ {
// Распределение для генерации случайных координат кватерниона // Распределение для генерации случайных координат кватерниона
std::normal_distribution<> distrib(0.0, 1.0); std::normal_distribution<> distrib(0.0, 1.0);
// Генерируем четыре случайных числа из нормального распределения N(0, 1). // Генерируем четыре случайных числа из нормального распределения N(0, 1).
// Нормализация этого вектора дает равномерное распределение по 4D-сфере (т.е. кватернион единичной длины). // Нормализация этого вектора дает равномерное распределение по 4D-сфере (т.е. кватернион единичной длины).
Vector4f randomQuat = { Vector4f randomQuat = {
(float)distrib(gen), (float)distrib(gen),
(float)distrib(gen), (float)distrib(gen),
@ -37,25 +37,25 @@ namespace ZL
} }
// --- Основная функция генерации --- // --- Основная функция генерации ---
std::vector<BoxCoords> generateRandomBoxCoords(int N) std::vector<BoxCoords> generateRandomBoxCoords(int N)
{ {
// Константы // Константы
const float MIN_DISTANCE = 3.0f; const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Работаем с квадратом расстояния const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Работаем с квадратом расстояния
const float MIN_COORD = -100.0f; const float MIN_COORD = -100.0f;
const float MAX_COORD = 100.0f; const float MAX_COORD = 100.0f;
const int MAX_ATTEMPTS = 1000; // Ограничение на количество попыток, чтобы избежать бесконечного цикла const int MAX_ATTEMPTS = 1000; // Ограничение на количество попыток, чтобы избежать бесконечного цикла
std::vector<BoxCoords> boxCoordsArr; std::vector<BoxCoords> boxCoordsArr;
boxCoordsArr.reserve(N); // Резервируем память boxCoordsArr.reserve(N); // Резервируем память
// 1. Инициализация генератора псевдослучайных чисел // 1. Инициализация генератора псевдослучайных чисел
// Используем Mersenne Twister (mt19937) как высококачественный генератор // Используем Mersenne Twister (mt19937) как высококачественный генератор
std::random_device rd; std::random_device rd;
std::mt19937 gen(rd()); std::mt19937 gen(rd());
// 2. Определение равномерного распределения для координат [MIN_COORD, MAX_COORD] // 2. Определение равномерного распределения для координат [MIN_COORD, MAX_COORD]
std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD); std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD);
int generatedCount = 0; int generatedCount = 0;
@ -65,53 +65,53 @@ namespace ZL
bool accepted = false; bool accepted = false;
int attempts = 0; int attempts = 0;
// Попытка найти подходящие координаты // Попытка найти подходящие координаты
while (!accepted && attempts < MAX_ATTEMPTS) while (!accepted && attempts < MAX_ATTEMPTS)
{ {
// Генерируем новые случайные координаты // Генерируем новые случайные координаты
Vector3f newPos( Vector3f newPos(
(float)distrib(gen), (float)distrib(gen),
(float)distrib(gen), (float)distrib(gen),
(float)distrib(gen) (float)distrib(gen)
); );
// Проверка расстояния до всех уже существующих объектов // Проверка расстояния до всех уже существующих объектов
accepted = true; // Предполагаем, что подходит, пока не доказано обратное accepted = true; // Предполагаем, что подходит, пока не доказано обратное
for (const auto& existingBox : boxCoordsArr) for (const auto& existingBox : boxCoordsArr)
{ {
// Расчет вектора разности // Расчет вектора разности
Vector3f diff = newPos - existingBox.pos; Vector3f diff = newPos - existingBox.pos;
// Расчет квадрата расстояния // Расчет квадрата расстояния
float distanceSquared = diff.squaredNorm(); float distanceSquared = diff.squaredNorm();
// Если квадрат расстояния меньше квадрата минимального расстояния // Если квадрат расстояния меньше квадрата минимального расстояния
if (distanceSquared < MIN_DISTANCE_SQUARED) if (distanceSquared < MIN_DISTANCE_SQUARED)
{ {
accepted = false; // Отклоняем, слишком близко accepted = false; // Отклоняем, слишком близко
break; // Нет смысла проверять дальше, если одно нарушение найдено break; // Нет смысла проверять дальше, если одно нарушение найдено
} }
} }
if (accepted) if (accepted)
{ {
// 2. Генерируем случайный кватернион // 2. Генерируем случайный кватернион
Vector4f randomQuat = generateRandomQuaternion(gen); Vector4f randomQuat = generateRandomQuaternion(gen);
// 3. Преобразуем его в матрицу вращения // 3. Преобразуем его в матрицу вращения
Matrix3f randomMatrix = QuatToMatrix(randomQuat); Matrix3f randomMatrix = QuatToMatrix(randomQuat);
// 4. Добавляем объект с новой случайной матрицей // 4. Добавляем объект с новой случайной матрицей
boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix });
generatedCount++; generatedCount++;
} }
attempts++; attempts++;
} }
// Если превышено максимальное количество попыток, выходим из цикла, // Если превышено максимальное количество попыток, выходим из цикла,
// чтобы избежать зависания, если N слишком велико или диапазон слишком мал. // чтобы избежать зависания, если N слишком велико или диапазон слишком мал.
if (!accepted) { if (!accepted) {
std::cerr << "Предупреждение: Не удалось сгенерировать " << N << " объектов. Сгенерировано: " << generatedCount << std::endl; std::cerr << "Предупреждение: Не удалось сгенерировать " << N << " объектов. Сгенерировано: " << generatedCount << std::endl;
break; break;
} }
} }
@ -312,10 +312,23 @@ void Game::drawShip()
renderer.LoadIdentity(); renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
// Используем зафиксированную камеру для отрисовки, если lock активен
// Это обеспечит правильную ориентацию модели относительно начальной позиции камеры
if (shipMoveLockActive) {
Matrix3f inverseLockedCamera = InverseMatrix(lockedCameraMat);
renderer.RotateMatrix(inverseLockedCamera);
} else {
renderer.RotateMatrix(Environment::inverseShipMatrix);
}
renderer.RotateMatrix(rotateShipMat); renderer.RotateMatrix(rotateShipMat);
// Компенсируем начальный поворот модели на 90° вокруг Y, чтобы модель смотрела в направлении движения
// Модель изначально повёрнута на M_PI / 2.0 вокруг Y, поэтому добавляем компенсацию
Vector4f correctionQuat = QuatFromRotateAroundY(M_PI / 2.0f);
renderer.RotateMatrix(correctionQuat);
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
renderer.DrawVertexRenderStruct(spaceship); renderer.DrawVertexRenderStruct(spaceship);
@ -484,60 +497,83 @@ void Game::processTickCount() {
float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0]; float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0];
float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1]; float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1];
if (isDraggingShip) {
// Движение корабля
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
{
float velocity = sqrtf(diffx * diffx + diffy * diffy);
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold Environment::shipVelocity = velocity * delta / 100;
{
float velocity = sqrtf(diffx * diffx + diffy * diffy); // SDL screen Y направлен вниз, поэтому инвертируем diffy
// Движение по -Z (вперёд в локальных координатах камеры)
// diffx: вправо = положительное, влево = отрицательное
// diffy: вниз = положительное, вверх = отрицательное (в SDL)
// Для логики: вверх на экране (отрицательный diffy) = движение вперёд (-Z)
// Вправо на экране (положительный diffx) = поворот вправо (вокруг +Y)
Environment::shipVelocity = velocity * delta / 100; // Вычисляем угол поворота вокруг Y (горизонтальный поворот)
// atan2(x, z) где x - это -diffx (инвертированное для правильного направления), z - это -diffy (вверх/вниз, инвертированное)
// Вверх (diffy < 0) должно давать угол 0 (вперёд), вправо (diffx > 0) должно давать правильное направление
float angleY = atan2(-diffx, -diffy); // угол поворота вокруг Y (инвертируем diffx для правильного направления)
Vector3f dir = { -diffx, 0, diffy }; // Создаём кватернион для поворота вокруг Y (горизонтальный поворот)
float angle = atan2(dir.v[0], -dir.v[2]); // [-pi, pi] Vector4f quat = { 0, sin(angleY * 0.5f), 0, cos(angleY * 0.5f) };
Vector4f quat = { 0, sin(angle * 0.5f), 0, cos(angle * 0.5f) }; rotateShipMat = QuatToMatrix(quat);
rotateShipMat = QuatToMatrix(quat);
// Фиксируем ориентацию камеры при первом реальном движении
rotateShipMat = QuatToMatrix(quat); if (!shipMoveLockActive) {
lockedCameraMat = Environment::shipMatrix;
shipMoveLockActive = true;
/* }
float rotationPower = sqrtf(diffx * diffx + diffy * diffy); }
else
//std::cout << rotationPower << std::endl; {
Environment::shipVelocity = 0;
float deltaAlpha = rotationPower * delta * M_PI / 500000.f; }
Vector3f rotationDirection = { diffy, diffx, 0 };
rotationDirection = rotationDirection.normalized();
Vector4f rotateQuat = {
rotationDirection.v[0] * sin(deltaAlpha * 0.5f),
rotationDirection.v[1] * sin(deltaAlpha * 0.5f),
rotationDirection.v[2] * sin(deltaAlpha * 0.5f),
cos(deltaAlpha * 0.5f) };
Matrix3f rotateMat = QuatToMatrix(rotateQuat);
Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat);
Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix);
*/
} }
else else if (isDraggingCamera) {
{ // Вращение камеры
Environment::shipVelocity = 0; if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
{
float rotationPower = sqrtf(diffx * diffx + diffy * diffy);
float deltaAlpha = rotationPower * delta * M_PI / 500000.f;
Vector3f rotationDirection = { diffy, diffx, 0 };
rotationDirection = rotationDirection.normalized();
Vector4f rotateQuat = {
rotationDirection.v[0] * sin(deltaAlpha * 0.5f),
rotationDirection.v[1] * sin(deltaAlpha * 0.5f),
rotationDirection.v[2] * sin(deltaAlpha * 0.5f),
cos(deltaAlpha * 0.5f) };
Matrix3f rotateMat = QuatToMatrix(rotateQuat);
Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat);
Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix);
// Обновляем начальную позицию для плавного вращения
Environment::tapDownStartPos.v[0] = Environment::tapDownCurrentPos.v[0];
Environment::tapDownStartPos.v[1] = Environment::tapDownCurrentPos.v[1];
}
} }
} }
if (fabs(Environment::shipVelocity) > 0.01f) if (fabs(Environment::shipVelocity) > 0.01f)
{ {
Vector3f velocityDirection = { 0,0, -Environment::shipVelocity*delta / 1000.f }; // Получаем локальное перемещение в координатах камеры
Vector3f localMove = MultMatrixVector(rotateShipMat, Vector3f{0, 0, -Environment::shipVelocity * delta / 1000.f});
// Выбираем матрицу камеры: зафиксированную (если lock активен) или текущую
Matrix3f camMat = shipMoveLockActive ? lockedCameraMat : Environment::shipMatrix;
Vector3f velocityDirectionAdjusted = MultMatrixVector(rotateShipMat, velocityDirection); // Переводим локальное перемещение в мировые координаты
Vector3f worldMove = MultMatrixVector(camMat, localMove);
Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; Environment::shipPosition = Environment::shipPosition + worldMove;
} }
lastTickCount = newTickCount; lastTickCount = newTickCount;
@ -564,7 +600,7 @@ void Game::update() {
} }
else if (event.type == SDL_MOUSEBUTTONDOWN) { else if (event.type == SDL_MOUSEBUTTONDOWN) {
// 1. Обработка нажатия кнопки мыши // 1. Обработка нажатия кнопки мыши
int mx = event.button.x; int mx = event.button.x;
int my = event.button.y; int my = event.button.y;
@ -573,16 +609,29 @@ void Game::update() {
int uiX = mx; int uiX = mx;
int uiY = Environment::height - my; int uiY = Environment::height - my;
if (false) if (mx < 400 && my > 400)
{ {
Environment::tapDownHold = true;
isDraggingShip = true;
isDraggingCamera = false;
// Сбрасываем lock при новом нажатии, чтобы новый drag снова фиксировал камеру при первом движении
shipMoveLockActive = false;
// Координаты начального нажатия
Environment::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y;
// Начальная позиция также становится текущей
Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y;
} }
else else
{ {
Environment::tapDownHold = true; Environment::tapDownHold = true;
// Координаты начального нажатия isDraggingShip = false;
isDraggingCamera = true;
// Координаты начального нажатия
Environment::tapDownStartPos.v[0] = event.button.x; Environment::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y; Environment::tapDownStartPos.v[1] = event.button.y;
// Начальная позиция также становится текущей // Начальная позиция также становится текущей
Environment::tapDownCurrentPos.v[0] = event.button.x; Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y; Environment::tapDownCurrentPos.v[1] = event.button.y;
} }
@ -590,18 +639,30 @@ void Game::update() {
} }
else if (event.type == SDL_MOUSEBUTTONUP) { else if (event.type == SDL_MOUSEBUTTONUP) {
// 2. Обработка отпускания кнопки мыши // 2. Обработка отпускания кнопки мыши
isDraggingVolume = false; isDraggingVolume = false;
Environment::tapDownHold = false; Environment::tapDownHold = false;
Environment::shipVelocity = 0; Environment::shipVelocity = 0;
// Сбрасываем lock камеры при отпускании мыши
shipMoveLockActive = false;
// Если была повернута камера, синхронизируем ориентацию корабля с камерой
// Корабль должен смотреть в том же направлении, что и камера (без дополнительного вращения)
if (isDraggingCamera) {
rotateShipMat = Matrix3f::Identity();
}
isDraggingShip = false;
isDraggingCamera = false;
} }
else if (event.type == SDL_MOUSEMOTION) { else if (event.type == SDL_MOUSEMOTION) {
// 3. Обработка перемещения мыши // 3. Обработка перемещения мыши
int mx = event.motion.x; int mx = event.motion.x;
int my = event.motion.y; int my = event.motion.y;
if (Environment::tapDownHold) { if (Environment::tapDownHold) {
// Обновление текущей позиции, если кнопка удерживается // Обновление текущей позиции, если кнопка удерживается
Environment::tapDownCurrentPos.v[0] = event.motion.x; Environment::tapDownCurrentPos.v[0] = event.motion.x;
Environment::tapDownCurrentPos.v[1] = event.motion.y; Environment::tapDownCurrentPos.v[1] = event.motion.y;
} }

4
Game.h
View File

@ -69,6 +69,10 @@ private:
bool isDraggingVolume = false; bool isDraggingVolume = false;
bool isDraggingShip = false;
bool isDraggingCamera = false;
bool shipMoveLockActive = false;
Matrix3f lockedCameraMat = Matrix3f::Identity();
float musicVolume = 1.0f; float musicVolume = 1.0f;
float volumeBarMinX = 1190.0f; float volumeBarMinX = 1190.0f;
float volumeBarMaxX = 1200.0f; float volumeBarMaxX = 1200.0f;