diff --git a/src/Character.cpp b/src/Character.cpp index ca4f3a5..82452de 100644 --- a/src/Character.cpp +++ b/src/Character.cpp @@ -72,6 +72,23 @@ void Character::setPathPlanner(PathPlanner planner) { pathPlanner = std::move(planner); } +void Character::clearPath() { + walkTarget = position; + requestedWalkTarget = position; + pathWaypoints.clear(); + currentWaypointIndex = 0; + onArrivedCallback = nullptr; +} + +void Character::setDirectWalkTarget(const Eigen::Vector3f& target) { + Eigen::Vector3f normalized(target.x(), 0.f, target.z()); + walkTarget = normalized; + requestedWalkTarget = normalized; + pathWaypoints.clear(); + currentWaypointIndex = 0; + onArrivedCallback = nullptr; +} + AnimationState Character::resolveActiveState() const { if (animations.count(currentState)) return currentState; diff --git a/src/Character.h b/src/Character.h index 3baa0be..2035441 100644 --- a/src/Character.h +++ b/src/Character.h @@ -42,6 +42,8 @@ public: std::function onArrived = nullptr); void setPathPlanner(PathPlanner planner); + void clearPath(); + void setDirectWalkTarget(const Eigen::Vector3f& target); void draw(Renderer& renderer); void drawShadowDepth(Renderer& renderer); void drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera); @@ -49,6 +51,7 @@ public: // Public: read by Game for camera tracking and ray-cast origin Eigen::Vector3f position = Eigen::Vector3f(0.f, 0.f, 0.f); float facingAngle = 0.0f; + float targetFacingAngle = 0.0f; // Per-character tuning — set after construction, before first update float walkSpeed = 3.0f; @@ -89,7 +92,6 @@ private: std::vector pathWaypoints; size_t currentWaypointIndex = 0; PathPlanner pathPlanner; - float targetFacingAngle = 0.0f; std::function onArrivedCallback; static constexpr float WALK_THRESHOLD = 0.05f; diff --git a/src/Environment.h b/src/Environment.h index 90ab3f6..d564f20 100644 --- a/src/Environment.h +++ b/src/Environment.h @@ -15,7 +15,7 @@ namespace ZL { using std::max; #endif - constexpr float DEFAULT_ZOOM = 36.f; + constexpr float DEFAULT_ZOOM = 4.f; class Environment { public: diff --git a/src/Game.cpp b/src/Game.cpp index 27d83f8..e4ee4a6 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -144,7 +144,7 @@ namespace ZL std::cout << "Load resurces step 4" << std::endl; - currentLocation = std::make_unique(renderer, inventory); + currentLocation = std::make_unique(renderer, inventory, "forest"); currentLocation->setup(); @@ -374,6 +374,13 @@ namespace ZL //space.clearTextRendererCache(); } + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { + SDL_SetRelativeMouseMode(SDL_TRUE); + } + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { + SDL_SetRelativeMouseMode(SDL_FALSE); + } + #ifdef __ANDROID__ if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) { Environment::exitGameLoop = true; @@ -468,8 +475,8 @@ namespace ZL handleMotion(ZL::UiManager::MOUSE_FINGER_ID, mx, my); if (currentLocation) { - currentLocation->handleMotion(ZL::UiManager::MOUSE_FINGER_ID, event.motion.x, event.motion.y, mx, my); - } + currentLocation->handleMotion(ZL::UiManager::MOUSE_FINGER_ID, event.motion.xrel, event.motion.yrel, mx, my); + } } if (event.type == SDL_MOUSEWHEEL) { @@ -532,6 +539,7 @@ namespace ZL case SDLK_a: case SDLK_d: case SDLK_u: + case SDLK_i: if (currentLocation) currentLocation->handleKeyDown(event.key.keysym.sym); break; @@ -568,6 +576,7 @@ namespace ZL case SDLK_s: case SDLK_a: case SDLK_d: + case SDLK_i: if (currentLocation) currentLocation->handleKeyUp(event.key.keysym.sym); break; default: break; diff --git a/src/Location.cpp b/src/Location.cpp index 984e655..005672d 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -32,6 +32,9 @@ namespace ZL void Location::setup() { + + carPosition = { 5.4005, 0, -3.811283 }; + auto playerTexture0 = std::make_shared(CreateTextureDataFromPng("resources/e/male_packed0_diffuse.png", CONST_ZIP_FILE)); auto playerTexture1 = std::make_shared(CreateTextureDataFromPng("resources/e/male_packed1_diffuse.png", CONST_ZIP_FILE)); auto playerTexture2 = std::make_shared(CreateTextureDataFromPng("resources/e/male_packed2_diffuse.png", CONST_ZIP_FILE)); @@ -95,8 +98,8 @@ void Location::setup() salesperson->walkSpeed = 3.0f; salesperson->rotationSpeed = 8.0f; salesperson->modelScale = 0.01f; - salesperson->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())); - salesperson->position = Vector3f{ 10, -0, -10 }; + salesperson->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(0, Eigen::Vector3f::UnitY())); + salesperson->position = Vector3f{ -8.31099, -0, -3.56868 }; salesperson->setTarget(salesperson->position); auto policeTexture0 = std::make_shared(CreateTextureDataFromPng("resources/e/police_packed0_diffuse.png", CONST_ZIP_FILE)); @@ -280,39 +283,12 @@ void Location::setup() carWheelMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix()); carWheelMesh.RefreshVBO(); - /* - auto girlfriendTexture = std::make_shared(CreateTextureDataFromPng("resources/e/female_packed0_diffuse.png", CONST_ZIP_FILE)); - girlfriend = std::make_unique(); - girlfriend->loadAnimation(AnimationState::STAND, "resources/e/woman_stand_idle001.txt", CONST_ZIP_FILE); - girlfriend->loadAnimation(AnimationState::WALK, "resources/e/woman_run001.txt", CONST_ZIP_FILE); - girlfriend->setTexture(girlfriendTexture); - girlfriend->walkSpeed = 3.0f; - girlfriend->rotationSpeed = 8.0f; - girlfriend->modelScale = 0.01f; - girlfriend->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())); - girlfriend->position = Vector3f{ -10, -0, -10 }; - girlfriend->setTarget(girlfriend->position);*/ - - } void Location::setupNavigation() { std::vector obstacles; - /*obstacles.reserve(gameObjects.size() + interactiveObjects.size()); - - for (const auto& item : gameObjects) { - const LoadedGameObject& gameObj = item.second; - obstacles.push_back({ &gameObj.mesh.data, Eigen::Vector3f::Zero() }); - } - - for (const InteractiveObject& intObj : interactiveObjects) { - if (!intObj.isActive) { - continue; - } - obstacles.push_back({ &intObj.mesh.data, intObj.position }); - }*/ if (locationId == "forest") { navigation.build(obstacles, "resources/config2/navigation2.json", CONST_ZIP_FILE); @@ -495,13 +471,14 @@ void Location::setup() renderer.PushMatrix(); renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - //renderer.TranslateMatrix({ 0, -6.f, 0 }); - + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix()); renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix()); Eigen::Vector3f camTarget = inCar ? carPosition : (player ? player->position : Eigen::Vector3f::Zero()); renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() }); + renderer.TranslateMatrix({ 0, -1.3f, 0 }); + if (roomMesh.data.PositionData.size() > 0) { @@ -581,8 +558,8 @@ void Location::setup() for (auto& npc : npcs) npc->draw(renderer); //#ifdef SHOW_PATH - drawDebugNavigation(); - drawDebugForbidden(); + //drawDebugNavigation(); + //drawDebugForbidden(); //#endif renderer.PopMatrix(); @@ -777,9 +754,52 @@ void Location::setup() return navigation.setAreaAvailable(areaName, available); } + bool Location::isCarFootprintWalkable(const Eigen::Vector3f& center, float rotation) const + { + if (!navigation.isReady()) return true; + + constexpr float carLength = 7.3f; + constexpr float carWidth = 2.8f; + constexpr float sampleSpacing = 0.35f; + + const float halfLength = carLength * 0.5f; + const float halfWidth = carWidth * 0.5f; + const int longSteps = max(1, static_cast(std::ceil(carLength / sampleSpacing))); + const int widthSteps = max(1, static_cast(std::ceil(carWidth / sampleSpacing))); + + const Eigen::Vector3f forward(-std::sin(rotation), 0.f, -std::cos(rotation)); + const Eigen::Vector3f right(std::cos(rotation), 0.f, -std::sin(rotation)); + + for (int i = 0; i <= longSteps; ++i) { + const float fl = (static_cast(i) / static_cast(longSteps)) * 2.0f - 1.0f; + const Eigen::Vector3f lineOffset = forward * (fl * halfLength); + for (int j = 0; j <= widthSteps; ++j) { + const float fw = (static_cast(j) / static_cast(widthSteps)) * 2.0f - 1.0f; + const Eigen::Vector3f sample = center + lineOffset + right * (fw * halfWidth); + if (!navigation.isWalkable(sample)) { + return false; + } + } + } + return true; + } + void Location::update(int64_t delta) { + const Eigen::Vector3f playerPosBefore = player ? player->position : Eigen::Vector3f::Zero(); + if (player) { + if (!inCar) { + player->targetFacingAngle = cameraAzimuth; + if (keyForward) { + player->attackTarget = nullptr; + Eigen::Vector3f forward(std::sin(cameraAzimuth), 0.f, -std::cos(cameraAzimuth)); + player->setDirectWalkTarget(player->position + forward * 5.0f); + } else if (wasKeyForward) { + player->clearPath(); + } + wasKeyForward = keyForward; + } player->update(delta); dialogueSystem.update(static_cast(delta), player->position); if (!roofObjectKey.empty()) { @@ -824,25 +844,55 @@ void Location::setup() carVelocity = max(-carMaxReverseSpeed, min(carMaxSpeed, carVelocity)); + const float oldRotation = carRotation; if (std::abs(carVelocity) > 0.01f) { float speedFactor = carVelocity / carMaxSpeed; if (keyLeft) carRotation += carTurnRate * dt * speedFactor; if (keyRight) carRotation -= carTurnRate * dt * speedFactor; } + if (navigation.isReady() && !isCarFootprintWalkable(carPosition, carRotation)) { + carRotation = oldRotation; + } float targetSteer = (keyLeft ? 1.f : 0.f) - (keyRight ? 1.f : 0.f); targetSteer *= carMaxSteerAngle; float steerLerp = min(1.f, dt * 8.f); carSteeringAngle += (targetSteer - carSteeringAngle) * steerLerp; - Eigen::Vector3f forward(-std::sin(carRotation), 0.f, -std::cos(carRotation)); - carPosition += forward * carVelocity * dt; + const Eigen::Vector3f forward(-std::sin(carRotation), 0.f, -std::cos(carRotation)); + const Eigen::Vector3f totalDelta = forward * carVelocity * dt; + const float totalDist = totalDelta.norm(); + const float maxSubstep = 0.2f; + const int substeps = max(1, static_cast(std::ceil(totalDist / maxSubstep))); + const Eigen::Vector3f stepDelta = totalDelta / static_cast(substeps); + for (int s = 0; s < substeps; ++s) { + Eigen::Vector3f candidate = carPosition + stepDelta; + if (navigation.isReady() && !isCarFootprintWalkable(candidate, carRotation)) { + carVelocity = 0.f; + break; + } + carPosition = candidate; + } if (player) { player->position = carPosition; } } + if (player && !inCar && navigation.isReady() && !navigation.isWalkable(player->position)) { + const Eigen::Vector3f slideX(player->position.x(), playerPosBefore.y(), playerPosBefore.z()); + const Eigen::Vector3f slideZ(playerPosBefore.x(), playerPosBefore.y(), player->position.z()); + Eigen::Vector3f resolved; + if (navigation.isWalkable(slideX)) { + resolved = slideX; + } else if (navigation.isWalkable(slideZ)) { + resolved = slideZ; + } else { + resolved = playerPosBefore; + } + player->position = resolved; + } + // Check if player reached target interactive object if (targetInteractiveObject && player) { float distToObject = (player->position - targetInteractiveObject->position).norm(); @@ -915,9 +965,14 @@ void Location::setup() std::cout << "[CLICK] Player position: (" << player->position.x() << ", " << player->position.y() << ", " << player->position.z() << ")" << std::endl; - targetInteractiveObject = clickedObject; - player->setTarget(clickedObject->position); - std::cout << "[CLICK] Player moving to object..." << std::endl; + float distance = (player->position - clickedObject->position).norm(); + + if (distance < 5.0) + { + targetInteractiveObject = clickedObject; + } + //player->setTarget(clickedObject->position); + //std::cout << "[CLICK] Player moving to object..." << std::endl; } else { @@ -949,6 +1004,7 @@ void Location::setup() } } + /* else if (rayDir.y() < -0.001f && player) { // Otherwise, unproject click to ground plane for Viola's walk target float t = -camPos.y() / rayDir.y(); @@ -959,7 +1015,7 @@ void Location::setup() { player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z())); } - } + }*/ else { std::cout << "[CLICK] No valid target found" << std::endl; } @@ -970,29 +1026,24 @@ void Location::setup() { } - void Location::handleMotion(int64_t fingerId, int eventX, int eventY, int mx, int my) + void Location::handleMotion(int64_t fingerId, int dx, int dy, int mx, int my) { if (dialogueSystem.blocksGameplayInput()) { dialogueSystem.handlePointerMoved( static_cast(mx), Environment::projectionHeight - static_cast(my) ); + return; } - if (rightMouseDown) { - int dx = eventX - lastMouseX; - int dy = eventY - lastMouseY; - lastMouseX = eventX; - lastMouseY = eventY; + const float sensitivity = 0.005f; + const float inclinationSign = invertCameraY ? -1.0f : 1.0f; + cameraAzimuth += dx * sensitivity; + cameraInclination += inclinationSign * dy * sensitivity; - const float sensitivity = 0.005f; - cameraAzimuth += dx * sensitivity; - cameraInclination += dy * sensitivity; - - const float minInclination = M_PI * 30.f / 180.f; - const float maxInclination = M_PI * 0.5f; - cameraInclination = max(minInclination, min(maxInclination, cameraInclination)); - } + const float minInclination = M_PI * 10.f / 180.f; + const float maxInclination = M_PI * 0.5f; + cameraInclination = max(minInclination, min(maxInclination, cameraInclination)); } void Location::handleKeyDown(int sdlKey) @@ -1002,8 +1053,10 @@ void Location::setup() case SDLK_s: keyBackward = true; break; case SDLK_a: keyLeft = true; break; case SDLK_d: keyRight = true; break; - case SDLK_u: - girlfriend->setTarget(player->position); + case SDLK_i: invertCameraY = !invertCameraY; break; + case SDLK_u: + std::cout << player->position << std::endl; + //girlfriend->setTarget(player->position); break; default: break; } diff --git a/src/Location.h b/src/Location.h index 9ef30cb..b87e8fe 100644 --- a/src/Location.h +++ b/src/Location.h @@ -85,6 +85,9 @@ namespace ZL bool rightMouseDown = false; int lastMouseX = 0; int lastMouseY = 0; + bool mouseInitialized = false; + bool wasKeyForward = false; + bool invertCameraY = false; void setup(); @@ -102,7 +105,7 @@ namespace ZL void handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my); void handleUp(int64_t fingerId, int mx, int my); - void handleMotion(int64_t fingerId, int eventX, int eventY, int mx, int my); + void handleMotion(int64_t fingerId, int dx, int dy, int mx, int my); void handleKeyDown(int sdlKey); void handleKeyUp(int sdlKey); @@ -115,13 +118,15 @@ namespace ZL protected: Renderer& renderer; - Inventory& inventory; + Inventory& inventory; private: std::string locationId; std::string roofObjectKey; bool roofVisible = true; std::vector> roofHideZones; + + bool isCarFootprintWalkable(const Eigen::Vector3f& center, float rotation) const; }; } // namespace ZL \ No newline at end of file