diff --git a/Camera.h b/Camera.h index 3805b76..0e822c5 100644 --- a/Camera.h +++ b/Camera.h @@ -25,7 +25,7 @@ public: const float originalspeed = 0.08f; float speed = 0.08f; float sensitivity = 0.001f; - float FOV = 90.0f; + float FOV = 100.0f; float screenshake = 0.0f; Camera() {} @@ -66,8 +66,10 @@ public: } glm::mat4 GetProjectionMatrix(float nearPlane, float farPlane) { - return glm::perspective(glm::radians(FOV), - (float)window->width / (float)window->height, nearPlane, farPlane); + // FOV is horizontal; convert to the vertical angle glm expects so wide screens aren't stretched + const float aspect = (float)window->width / (float)window->height; + const float vfov = 2.0f * atan(tan(glm::radians(FOV) * 0.5f) / aspect); + return glm::perspective(vfov, aspect, nearPlane, farPlane); } void Step(float dt) { diff --git a/Engine.cpp b/Engine.cpp index d904206..3331f7c 100644 --- a/Engine.cpp +++ b/Engine.cpp @@ -146,7 +146,7 @@ void Engine::Initiate() testcharacter->name = "character"; testcharacter->castshadow = true; testcharacter->body->t.ScaleBy({ 0.5f, 1.8f, 0.5f }); - testcharacter->body->t.TranslateTo({ 0.0f, 0.9f, 0.0f }); + testcharacter->body->t.TranslateTo({ 0.0f, 10.0f, 0.0f }); testcharacter->body->SetRig(testrig); // Base animator (weight 0) � walk, looped @@ -208,7 +208,7 @@ void Engine::Initiate() if (lan && lan->IsActive()) { std::mt19937 rng(std::random_device{}()); std::uniform_real_distribution spread(-8.0f, 8.0f); - testcharacter->body->t.TranslateTo({ spread(rng), 0.9f, spread(rng) }); + testcharacter->body->t.TranslateTo({ spread(rng), 10.0f, spread(rng) }); } // ------------------------------------------------------------------------ @@ -328,16 +328,6 @@ void Engine::Initiate() grappleText->t2d.size = { 0.0f, 0.0f, 560.0f, 40.0f }; grappleText->BindToRenderSystem(); - ImageBox* testgui = new ImageBox(); - testgui->tex = magic; - testgui->Color = { 1.0f, 1.0f, 1.0f }; - testgui->Opacity = 0.8f; - testgui->rounding = 0.1f; - testgui->t2d.center = { 0.0f, 1.0f }; - testgui->t2d.position = { 0.0f, 1.0f, 10.0f, -10.0f }; - testgui->t2d.size = { 0.0f, 0.0f, 744.0f / 2.0f, 914.0f / 2.0f }; - testgui->BindToRenderSystem(); - // ------------------------------------------------------------------------ // World meshes // ------------------------------------------------------------------------ @@ -447,22 +437,24 @@ void Engine::Initiate() // body = 50 damage (two shots). Respawns the target when it dies. // Movement tuning const float airControl = 0.5f; // fraction of ground acceleration available in the air - const float grappleRange = 8.0f; // metres from the player + const float grappleRange = 12.0f; // metres from the player const float grappleSpeed = 28.0f; // launch speed toward the hit point const float grappleCooldownTime = 2.5f; // seconds float grappleCooldown = 0.0f; // Slide (key Left Ctrl): locked direction, committed for slideDuration, speed eases from // slideBoost x ground top speed down to 1x by slideFalloffTime. - const float slideBoost = 2.0f; - const float slideDuration = 1.2f; - const float slideFalloffTime = 1.05f; + const float slideBoost = 1.5f; + const float slideDuration = 0.8f; + const float slideFalloffTime = 0.7f; const float slideDropY = 0.65f; // standing centre height (0.9) minus lying (0.25) const float standEyeOffset = 0.7f; // eyes 0.7 above body centre (body centre 0.9, height 1.8) const float slideEyeOffset = 0.25f; // lying down, eyes just above the floor float eyeOffset = standEyeOffset; bool sliding = false; float slideTime = 0.0f; + const float slideCooldownTime = 1.0f; // seconds after a slide ends before the next + float slideCooldown = 0.0f; glm::vec3 slideDir(0.0f); // Shockwave (key Q): projectile that explodes on the first surface it touches. The blast @@ -550,7 +542,7 @@ void Engine::Initiate() // Random respawn spot on the floor (NPC and player), kept clear of every map prop std::mt19937 respawnRng(std::random_device{}()); - const float respawnHeight = 10.0f; // respawn in the air and drop to the floor + const float respawnHeight = 15.0f; // respawn in the air and drop to the floor auto RandomSpawn = [&]() -> glm::vec3 { std::uniform_real_distribution spreadX(-37.0f * mapScale, 37.0f * mapScale), spreadZ(-17.0f * mapScale, 17.0f * mapScale); const float margin = 1.5f; @@ -628,11 +620,18 @@ void Engine::Initiate() while (!glfwWindowShouldClose(window->handle)) { frame++; - const float deltatime = 1.0f / float(fps); + // Real frame time (clamped so a hitch can't tunnel through colliders), scaled so the game + // plays at the speed it had when it ran one 1/fps step per frame on a 144 Hz display. + // refDt is the step the movement constants were tuned at. + static double prevFrameTime = glfwGetTime(); + const double frameNow = glfwGetTime(); + const float deltatime = glm::clamp(float(frameNow - prevFrameTime), 0.001f, 1.0f / 60.0f) * (144.0f / float(fps)); + prevFrameTime = frameNow; + const float refDt = 1.0f / float(fps); const glm::vec3 camFront = camera->t.GetFrontVector(); const glm::vec3 camRight = camera->t.GetRightVector(); - const float walkScale = 0.5f; // WASD speed relative to the original 3.9 + const float walkScale = 0.6f; // WASD speed relative to the original 3.9 const float movespeed = 3.0f * 1.3f * walkScale; // -------------------------------------------------------------------- @@ -730,12 +729,13 @@ void Engine::Initiate() characterBody->linearvelocity.z += dashDir.z * 18.0f; camera->AddScreenshake(0.04f); } - if (buffer.GetCode() == GLFW_KEY_LEFT_CONTROL && buffer.IsPress() && !sliding && IsGrounded()) { + if (buffer.GetCode() == GLFW_KEY_LEFT_CONTROL && buffer.IsPress() && !sliding && slideCooldown <= 0.0f && IsGrounded()) { const glm::vec3 horizVel(characterBody->linearvelocity.x, 0.0f, characterBody->linearvelocity.z); if (glm::length(inputVel) > 0.0f || glm::length(horizVel) > 1.0f) { slideDir = glm::length(inputVel) > 0.0f ? glm::normalize(inputVel) : glm::normalize(horizVel); sliding = true; slideTime = 0.0f; + slideCooldown = slideDuration + slideCooldownTime; // Lie down feet-first: tilt the model's up axis to point back along the slide. // Hitbox is the same box, rotated with it; drop it so the feet stay on the floor. @@ -785,7 +785,7 @@ void Engine::Initiate() } if (buffer.GetCode() == GLFW_KEY_SPACE && buffer.IsPress()) { if (!sliding && IsGrounded()) - characterBody->linearvelocity.y = 12.0f; + characterBody->linearvelocity.y = 11.0f; } } @@ -797,9 +797,10 @@ void Engine::Initiate() // Movement + damping grappleCooldown = std::max(grappleCooldown - deltatime, 0.0f); + slideCooldown = std::max(slideCooldown - deltatime, 0.0f); const float accel = 40.0f; const float damp = 0.75f; - const float groundTopSpeed = movespeed * accel * deltatime * damp / (1.0f - damp); + const float groundTopSpeed = movespeed * accel * refDt * damp / (1.0f - damp); if (sliding) { // Direction is locked; speed eases from boost x down to 1x (quadratic: drops fastest early) slideTime += deltatime; @@ -816,10 +817,13 @@ void Engine::Initiate() } } else if (IsGrounded()) { - characterBody->linearvelocity.x *= damp; - characterBody->linearvelocity.z *= damp; - characterBody->linearvelocity.x += inputVel.x * accel * deltatime; - characterBody->linearvelocity.z += inputVel.z * accel * deltatime; + // Drag and acceleration rescaled so the top speed is the same at any frame rate + const float dampStep = std::pow(damp, deltatime / refDt); + const float accelScale = refDt * damp * (1.0f - dampStep) / (deltatime * dampStep * (1.0f - damp)); + characterBody->linearvelocity.x *= dampStep; + characterBody->linearvelocity.z *= dampStep; + characterBody->linearvelocity.x += inputVel.x * accel * deltatime * accelScale; + characterBody->linearvelocity.z += inputVel.z * accel * deltatime * accelScale; } else if (glm::length(inputVel) > 0.0f) { // Air: no ground drag so momentum (grapple, dash) carries. Input only adds @@ -895,7 +899,7 @@ void Engine::Initiate() ); } // First person: camera at the eyes (eased down while sliding) - eyeOffset += ((sliding ? slideEyeOffset : standEyeOffset) - eyeOffset) * 0.3f; + eyeOffset += ((sliding ? slideEyeOffset : standEyeOffset) - eyeOffset) * (1.0f - std::pow(0.7f, deltatime / refDt)); camera->t.TranslateTo(testcharacter->body->t.GetTranslation() + glm::vec3(0.0f, eyeOffset, 0.0f)); Sound* music = static_cast(mainf->GetFirstChildOfName("music")); diff --git a/Gui.h b/Gui.h index 59d464d..819edf9 100644 --- a/Gui.h +++ b/Gui.h @@ -109,8 +109,11 @@ public: // Measure line height float lineHeight = 0.0f; - for (auto& [key, ch] : font->Characters) + float ascent = 0.0f; // glyphs are drawn up from the baseline, so lines start this far below the top + for (auto& [key, ch] : font->Characters) { lineHeight = glm::max(lineHeight, (float)ch.Size.y * fontsize); + ascent = glm::max(ascent, (float)ch.Bearing.y * fontsize); + } const float lineSpacing = lineHeight * 1.2f; const float totalBlockH = lineSpacing * (float)lines.size(); @@ -145,7 +148,7 @@ public: // Horizontal start: textCenter.x controls where the line sits inside the box float x = boxLeft + (t2d.pixelsize.x - lineWidth) * textCenter.x; - float y = blockTop + lineSpacing * (float)i; + float y = blockTop + ascent + lineSpacing * (float)i; // baseline for (char c : lines[i]) { TextCharacter ch = font->Characters[(unsigned char)c]; @@ -225,9 +228,9 @@ class Crosshair { public: glm::vec3 Color = { 0.0f, 1.0f, 0.0f }; float Opacity = 1.0f; - float Length = 11.2f; // arm length - float Thickness = 2.8f; // arm width - float Gap = 5.6f; // distance from center to the start of each arm + float Length = 9.0f; // arm length + float Thickness = 2.2f; // arm width + float Gap = 4.5f; // distance from center to the start of each arm bool Dot = false; // center dot bool Visible = true; diff --git a/Physics.cpp b/Physics.cpp index d9a870a..b185766 100644 --- a/Physics.cpp +++ b/Physics.cpp @@ -64,8 +64,10 @@ void Physics::Step(float dt) if (!body->lockRotation) body->angularvelocity += (ComputeInvInertiaTensor(body) * body->torque) * dt; - body->linearvelocity *= PHYSICS_LINEAR_DAMPING; - body->angularvelocity *= PHYSICS_ANGULAR_DAMPING; + // The damping constants are per 1/60 s step; rescale so drag doesn't depend on frame rate + const float steps = dt * 60.0f; + body->linearvelocity *= std::pow(PHYSICS_LINEAR_DAMPING, steps); + body->angularvelocity *= std::pow(PHYSICS_ANGULAR_DAMPING, steps); body->force = glm::vec3(0.0f); body->torque = glm::vec3(0.0f);