diff --git a/Minecraft.Client/Input.cpp b/Minecraft.Client/Input.cpp index 6996192c..7fce9360 100644 --- a/Minecraft.Client/Input.cpp +++ b/Minecraft.Client/Input.cpp @@ -128,17 +128,23 @@ void Input::tick(LocalPlayer *player) player->interpolateTurn(tx * abs(tx) * turnSpeed, ty * abs(ty) * turnSpeed); #ifdef _WINDOWS64 - // Mouse look (added after stick-based turn) + // Mouse look is now handled per-frame in Minecraft::applyFrameMouseLook() + // to eliminate the 20Hz tick delay. Only flush any remaining delta here + // as a safety measure. if (iPad == 0 && KMInput.IsCaptured()) { - float mouseSensitivity = 0.5f; float rawDx, rawDy; KMInput.ConsumeMouseDelta(rawDx, rawDy); - float mdx = rawDx * mouseSensitivity; - float mdy = -rawDy * mouseSensitivity; - if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) - mdy = -mdy; - player->interpolateTurn(mdx, mdy); + // Delta should normally be 0 since applyFrameMouseLook() already consumed it + if (rawDx != 0.0f || rawDy != 0.0f) + { + float mouseSensitivity = 0.5f; + float mdx = rawDx * mouseSensitivity; + float mdy = -rawDy * mouseSensitivity; + if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) + mdy = -mdy; + player->interpolateTurn(mdx, mdy); + } } #endif diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 5e1a2119..ff8346be 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1223,6 +1223,53 @@ void Minecraft::createPrimaryLocalPlayer(int iPad) } } +#ifdef _WINDOWS64 +void Minecraft::applyFrameMouseLook() +{ + // Per-frame mouse look: consume mouse deltas every frame instead of waiting + // for the 20Hz game tick. Apply the same delta to both xRot/yRot AND xRotO/yRotO + // so the render interpolation instantly reflects the change without waiting for a tick. + if (level == NULL) return; + + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (localplayers[i] == NULL) continue; + int iPad = localplayers[i]->GetXboxPad(); + if (iPad != 0) continue; // Mouse only applies to pad 0 + + if (!KMInput.IsCaptured()) continue; + if (localgameModes[iPad] == NULL) continue; + + float rawDx, rawDy; + KMInput.ConsumeMouseDelta(rawDx, rawDy); + if (rawDx == 0.0f && rawDy == 0.0f) continue; + + float mouseSensitivity = 0.5f; + float mdx = rawDx * mouseSensitivity; + float mdy = -rawDy * mouseSensitivity; + if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) + mdy = -mdy; + + // Apply 0.15f scaling (same as Entity::interpolateTurn / Entity::turn) + float dyaw = mdx * 0.15f; + float dpitch = -mdy * 0.15f; + + // Apply to both current and old rotation so render interpolation + // reflects the change immediately (no 50ms tick delay) + localplayers[i]->yRot += dyaw; + localplayers[i]->yRotO += dyaw; + localplayers[i]->xRot += dpitch; + localplayers[i]->xRotO += dpitch; + + // Clamp pitch + if (localplayers[i]->xRot < -90.0f) localplayers[i]->xRot = -90.0f; + if (localplayers[i]->xRot > 90.0f) localplayers[i]->xRot = 90.0f; + if (localplayers[i]->xRotO < -90.0f) localplayers[i]->xRotO = -90.0f; + if (localplayers[i]->xRotO > 90.0f) localplayers[i]->xRotO = 90.0f; + } +} +#endif + void Minecraft::run_middle() { static __int64 lastTime = 0; diff --git a/Minecraft.Client/Minecraft.h b/Minecraft.Client/Minecraft.h index 30760300..caec4702 100644 --- a/Minecraft.Client/Minecraft.h +++ b/Minecraft.Client/Minecraft.h @@ -217,6 +217,9 @@ public: static Minecraft *GetInstance(); void run_middle(); void run_end(); +#ifdef _WINDOWS64 + void applyFrameMouseLook(); // Per-frame mouse look to reduce input latency +#endif void emergencySave(); diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 472d10a6..0cbfd90f 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -1077,6 +1077,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, // Render game graphics. if(app.GetGameStarted()) { + pMinecraft->applyFrameMouseLook(); // Per-frame mouse look (before ticks + render) pMinecraft->run_middle(); app.SetAppPaused( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 && ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad()) ); }