#refined rotation precision
5 messages · Page 1 of 1 (latest)
yeah, I agree! there are already a few suggestions on this topic.
Agreed. I know I messaged on another suggestion post about this, however I appreciate more light shed on this topic as this would be an extremely helpful QOL improvement for base building, let alone other placements.
FYI, those with trackpads have the fine control aka continuous deltas, where as a mouse has a delta value of discrete ticks (±120) by default.
For the devs, if this helps:
A notched mouse wheel only reports discrete ticks (usually ±1 per notch), so you can’t magically turn it into true “continuous” deltas unless the hardware (e.g., a precision touchpad or a high-res wheel) actually sends finer steps. But in Unreal Engine 5 you can do two useful things:
- If the device does send finer deltas (precision touchpad / some hi-res wheels): read the float wheel delta UE gives you and scale it while Shift is held.
- If it doesn’t (most notched wheels): simulate smoothness by accumulating a target value per tick and easing toward it over several frames when Shift is held. That “feels” like continuous scroll even though the hardware is discrete.
UE5 Input Setup (works for both C++ & Blueprints)
- Create an Axis Mapping named MouseWheel bound to Mouse Wheel Axis (it yields ~+1 / −1 per notch; some devices can give fractional values).
- Create an Action Mapping named ShiftModifier bound to Left Shift (and/or Right Shift).
- With Enhanced Input, you can also make an IA_MouseWheel (Axis1D) and an IA_Shift (Digital), then query Shift state inside your processing. (Chorded actions are optional—checking key state is simpler.)
Sample code to maybe help generate implementation ideas:
Option A: True high-res (when available): scale the real delta - C++ (PlayerController / Pawn):
// .h
float ScrollSensitivityNormal = 1.0f;
float ScrollSensitivityShift = 0.15f; // smaller = finer control
bool bShiftHeld = false;
void OnMouseWheel(float AxisValue);
void OnShiftPressed();
void OnShiftReleased();
// .cpp
void AMyPC::SetupPlayerInputComponent(UInputComponent* IC)
{
IC->BindAxis("MouseWheel", this, &AMyPC::OnMouseWheel);
IC->BindAction("ShiftModifier", IE_Pressed, this, &AMyPC::OnShiftPressed);
IC->BindAction("ShiftModifier", IE_Released, this, &AMyPC::OnShiftReleased);
}
void AMyPC::OnShiftPressed() { bShiftHeld = true; }
void AMyPC::OnShiftReleased() { bShiftHeld = false; }
void AMyPC::OnMouseWheel(float AxisValue)
{
// AxisValue is a float; on notched mice it’s typically ±1 per tick.
const float sens = bShiftHeld ? ScrollSensitivityShift : ScrollSensitivityNormal;
const float delta = AxisValue * sens;
// Apply delta directly to whatever you’re controlling (zoom, inventory, etc.)
ZoomLevel = FMath::Clamp(ZoomLevel + delta, MinZoom, MaxZoom);
}
Blueprints: Bind Mouse Wheel Axis → get Axis Value (float). Gate it by a Branch that checks Is Key Down (LeftShift) and use two different multipliers (0.15 when Shift, 1.0 otherwise). Apply to your variable (e.g., ZoomLevel).
If the device provides fractional deltas, you’ll get true smooth scrolling here. If not, each wheel notch will still be ±1, just scaled smaller when Shift is held.
Option B — Simulated smooth (for plain notched wheels) When Shift is held and a tick arrives (±1), don’t apply it instantly. Instead, add to a “target” and then ease toward it over a short time in Tick. This makes each notch feel like a gentle glide. - C++ example:
// .h
float ShiftNotchAmount = 0.35f; // how much one notch contributes when Shift is held
float SmoothSpeedPerSecond = 10.0f; // higher = snappier
float ZoomCurrent = 30.0f; // example controlled value
float ZoomTarget = 30.0f;
bool bShiftHeld = false;
void OnMouseWheel(float AxisValue);
virtual void Tick(float DeltaSeconds) override;
// .cpp
void AMyPC::OnMouseWheel(float AxisValue)
{
if (FMath::IsNearlyZero(AxisValue)) return;
if (bShiftHeld)
{
// Accumulate into the target in small amounts per notch
ZoomTarget = FMath::Clamp(ZoomTarget + AxisValue * ShiftNotchAmount, MinZoom, MaxZoom);
}
else
{
// Normal mode: immediate step
ZoomTarget = FMath::Clamp(ZoomTarget + AxisValue * 1.0f, MinZoom, MaxZoom);
}
}
void AMyPC::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
// Smooth toward target each frame
ZoomCurrent = FMath::FInterpTo(ZoomCurrent, ZoomTarget, DeltaSeconds, SmoothSpeedPerSecond);
// Apply ZoomCurrent to camera FOV, spring arm length, or UI scroll, etc.
Camera->SetFieldOfView(ZoomCurrent);
}
Blueprints:
- Variables: ZoomCurrent, ZoomTarget, ShiftHeld?
- On Mouse Wheel Axis:
If ShiftHeld? = true → ZoomTarget += AxisValue * 0.35
Else → ZoomTarget += AxisValue * 1.0 - On Event Tick: ZoomCurrent = FInterp To(ZoomCurrent, ZoomTarget, DeltaSeconds, 10.0) → apply to your camera/UI.
This preserves responsiveness but “fills in the gaps” between notches so it feels continuous.
UX Tips
- Separate sensitivities: one for normal, one for Shift. Small Shift sensitivity (e.g., 0.1–0.35) gives fine control.
- Clamp your controlled value (zoom, index) to avoid overshoot.
- Acceleration curve: If you want “super fine” control, you can apply a curve (e.g., delta *= Curve->GetFloatValue(FMath::Abs(AxisValue)) * FMath::Sign(AxisValue)).
- UI vs gameplay: For UI scrolling, apply to SScrollBox/ListView offsets similarly; for gameplay (weapon wheel, zoom), the same pattern applies.
Final notes:
- If the hardware only sends one tick per notch, you cannot read “sub-tick” deltas that don’t exist. The best you can do is scale and smooth (Option B).
- If the hardware supports high-res deltas, UE’s Mouse Wheel Axis callback already gives you a float. Then Shift can simply scale it (Option A) for true precision.
Awesome job! 👍👍👍 And please don’t forget to add a function to disable the appearance of pop-up submenus when hovering the mouse over bookshelves, cupboards, or similar surfaces where you’re trying to place the item.