I ran into this question while trying to solve a similar issue myself, so I figured I'd share my solution.
Assuming you want to restrict a ScrollRect to whole pixels, you can do something like this:
public class PixelPerfectScrollRect : ScrollRect
{
protected override void LateUpdate ()
{
base.LateUpdate();
ensurePixelPerfectScroll();
}
void ensurePixelPerfectScroll ()
{
float diff = content.rect.height - viewport.rect.height;
float normalizedPixel = 1 / diff;
verticalNormalizedPosition = Mathf.Ceil(verticalNormalizedPosition / normalizedPixel) * normalizedPixel;
// can also do the same for horizontalNormalizedPosition, using rect.width instead of rect.height
}
}
Attach this to a GameObject and use it the same way you'd use a ScrollRect. This also assumes that every UI unit is one pixel; you'll have to scale normalizedPixel
if that isn't the case for your game.
This works because ScrollRects can move their content
RectTransforms up to diff
pixels away from their local-space origin. If ScrollRects used pixel values we could just round those to ints, but since ScrollRects deal in normalized positions, we need to normalize our desired pixel values. If, in "pixel space," you move from 0 to diff
in increments of 1, in "normalized space" you need to move from 0 to 1 in increments of 1/diff
. (You're dividing the entire space by diff
.) Therefore we need to make sure our normalized value is rounded to the nearest multiple of 1/diff
.