'Pygame mouse position not precise enough to rotate the 3D scene

I need to rotate a 3D scene or a character using the mouse position: for example, if I move the mouse to the right, I want to turn to the right / the character should turn right.

I've seen lots of games place the mouse in the center of the screen, then get its position relatively to the previous frame, update the game accordingly and move the mouse back in the center.

I tried to replicate this but I ran into the issue that pygame does not rotate reliably: with higher framerates, I tend to get a slower rotation. This is not a problem when rotating a character in a 2D game, but this is concerning when a 3D scene rotates differently if there are more objects to draw.

I used this simple code to make sure the problem actually came from this:

import pygame
from pygame.locals import *

pygame.init()
WIDTH, HEIGHT = (640, 480)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
font = pygame.font.SysFont('consolas', 20)
clock = pygame.time.Clock()

angle = 0
time_passed = 0

pygame.mouse.set_pos((WIDTH//2, HEIGHT//2))
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            exit()

    # I did not use the MOUSEMOTION event because it used to glitch a bit
    x, y = pygame.mouse.get_pos()
    angle += (WIDTH//2 - x) * 360 / WIDTH # 360° every WIDTH pixels
    pygame.mouse.set_pos((WIDTH//2, HEIGHT//2))

    screen.fill(0)
    text = font.render('Angle: %d°' %angle, True, (255, 255, 255))
    w, h = text.get_size()
    screen.blit(text, (WIDTH//2 - w//2, HEIGHT//2 - h//2))
    pygame.display.flip()

    time_passed = clock.tick(60)/1000

I think either the mouse or the OS tweak the travelled distance according to the mouse speed, so I did the following tests at similar and relatively constant speed:

Going say 5 centimeters with the framerate limited to 60 FPS did not result in the same final angle as with 120 FPS for example, even when moving the mouse at the same speed, with the same mouse with high DPI.


Do I need to implement a tick system for example, where I only update the position every few frames? I think the problem wouldn't be solved with slower framerates then, and maybe the movement would not be as responsive as I want.

Or perhaps there is an alternative to pygame.mouse.get_pos() / event.pos, getting the raw, more precise position from the mouse?

I would appreciate any help.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source