fix: controller cursor speed no longer scales with framerate

When framerate was uncapped (vsync off, high-end hardware), the controller cursor in the inventory and creative menus moved way too fast. Basically unusable unless you switched to the dpad. The cursor update was tied to how often the screen redraws, so the faster the game ran, the faster the cursor flew.

Now the cursor moves a smaller distance per frame at higher framerates, so the actual on-screen speed stays the same whether you're at 60 FPS or 600 FPS.

While fixing this I also found an old workaround that was rounding the cursor position to whole pixels every frame and nudging it by 1 pixel to keep it from getting stuck. That nudge was pointing the wrong way on the vertical axis, which made up/down movement feel broken once the per-frame distance got small. Removed the rounding and the nudge. The cursor can now hold a fractional position between frames, and the part of the code that actually draws the cursor still snaps it to whole pixels on screen.

Fixes #3
This commit is contained in:
itsRevela
2026-04-10 21:38:43 -05:00
parent 8f41d34325
commit a191df1f2e
2 changed files with 23 additions and 33 deletions

View File

@@ -29,6 +29,7 @@ IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu()
m_pointerPos.y = 0.0f;
m_bPointerDrivenByMouse = false;
m_iLastMouseTickTimeNs = -1;
}
IUIScene_AbstractContainerMenu::~IUIScene_AbstractContainerMenu()
@@ -266,6 +267,21 @@ void IUIScene_AbstractContainerMenu::UpdateTooltips()
void IUIScene_AbstractContainerMenu::onMouseTick()
{
// Frame-rate independent cursor input, normalized to a 60Hz reference frame.
const int64_t kRefFrameNs = 1000000000LL / 60;
const int64_t kMinDeltaNs = 1000000LL;
const int64_t kMaxDeltaNs = 100000000LL;
int64_t iNowNs = System::nanoTime();
float fFrameScale = 1.0f;
if ( m_iLastMouseTickTimeNs > 0 )
{
int64_t iDeltaNs = iNowNs - m_iLastMouseTickTimeNs;
if ( iDeltaNs < kMinDeltaNs ) iDeltaNs = kMinDeltaNs;
if ( iDeltaNs > kMaxDeltaNs ) iDeltaNs = kMaxDeltaNs;
fFrameScale = static_cast<float>(iDeltaNs) / static_cast<float>(kRefFrameNs);
}
m_iLastMouseTickTimeNs = iNowNs;
Minecraft *pMinecraft = Minecraft::GetInstance();
if( pMinecraft->localgameModes[getPad()] != nullptr)
{
@@ -422,10 +438,10 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
// The SD/splitscreen scenes are approximately 0.6 times the size of the fullscreen on
if(!RenderManager.IsHiDef() || app.GetLocalPlayerCount() > 1) fInputScale *= 0.6f;
fInputX *= fInputScale;
fInputY *= fInputScale;
fInputX *= fInputScale * fFrameScale;
fInputY *= fInputScale * fFrameScale;
#ifdef USE_POINTER_ACCEL
#ifdef USE_POINTER_ACCEL
m_fPointerAccelX += fInputX / 50.0f;
m_fPointerAccelY += fInputY / 50.0f;
@@ -1269,36 +1285,8 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
vPointerPos.x -= m_fPointerImageOffsetX;
vPointerPos.y -= m_fPointerImageOffsetY;
// Update pointer position.
// 4J-PB - do not allow sub pixel positions or we get broken lines in box edges
// problem here when sensitivity is low - we'll be moving a sub pixel size, so it'll clamp, and we'll never move. In that case, move 1 pixel
if(fInputDirX!=0.0f)
{
if(fInputDirX==1.0f)
{
vPointerPos.x+=0.999999f;
}
else
{
vPointerPos.x-=0.999999f;
}
}
if(fInputDirY!=0.0f)
{
if(fInputDirY==1.0f)
{
vPointerPos.y+=0.999999f;
}
else
{
vPointerPos.y-=0.999999f;
}
}
vPointerPos.x = static_cast<float>(floor(vPointerPos.x + 0.5f));
vPointerPos.y = static_cast<float>(floor(vPointerPos.y + 0.5f));
// Keep sub-pixel float state so deltas <1px accumulate across frames; the renderer
// truncates to integer pixels when emitting the Iggy mouse event.
m_pointerPos = vPointerPos;
adjustPointerForSafeZone();

View File

@@ -145,6 +145,8 @@ protected:
int m_iConsectiveInputTicks;
int64_t m_iLastMouseTickTimeNs;
// Used for detecting quick "taps" in a direction, should jump cursor to next slot.
enum ETapState
{