简体   繁体   中英

Pygame - How to make mouse gesture movement smoother?

I wanted to be able to "scroll" around a pygame window using just mouse gestures, or in this case, "two fingers scrolling" (don't know the right term for this).

I managed to make an example implementation:

import pygame

pygame.init()

size = (width, height) = (800, 600)
screen = pygame.display.set_mode(size)


class SceneElement:
    def __init__(self, x, y, width, height, color):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.color = color

class Scene:
    def __init__(self):
        self.elements = [
            SceneElement(150, 150, 200, 200, (55, 55, 10, 0.3)),
            SceneElement(250, 300, 200, 200, (155, 200, 10, 0.5)),
        ]

    def render(self, offset):
        screen.fill((255, 255, 255))
        for element in self.elements:
            x = element.x + offset[0]
            y = element.y + offset[1]
            pygame.draw.rect(screen, element.color, (x, y, element.width, element.height))

scene = Scene()
offset = [0, 0]

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == 1027:
            if event.x == 0 and event.y == -1:
                print(event.x, event.y)
                offset[1] -= 10
            elif event.x == -1 and event.y == 0:
                offset[0] += 10
                print(event.x, event.y)
            elif event.x == 1 and event.y == 0:
                offset[0] -= 10
                print(event.x, event.y)
            elif event.x == 0 and event.y == 1:
                offset[1] += 10
                print(event.x, event.y)

    scene.render(offset)
    pygame.display.flip()

pygame.quit()

The above works. Here is a gif showing that it works . Now problem is, I don't think this is how this is supposed to be implemented. I didn't see any example or existing code online that did this.

So while the above works, it doesn't feel "smooth". Trying to move in a circle or diagonally feels very unnatural (as can be seen in the gif near the end). Is there a better way to do the above (moving around using mouse gestures) or is this the right implementation?

Mainly what you have to deal with here is that your diagonal movement is not normalized, to fix this the easiest would be to just use pygame.Vector2 for positions and such, it also has a normalized method that will do the normalizing for you:

scene = Scene()
offset = pygame.Vector2()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEWHEEL:
            direction = pygame.Vector2(event.x, event.y).normalize()
            offset += direction * 10

    scene.render(offset)
    pygame.display.flip()

This won't affect functionality of the rest of the code as vectors can be indexed just like lists, however, I'd suggest you use pygame.Vector2 for positions and velocities and accelerations and other physics related things in general as they are faster and far more convenient.

Also use constants instead of some arbitrary integer values for event types and other stuff as it makes reading the code a lot easier.

Thanks to Matiiss's answer, I managed to find a clue on how to do this:

import pygame

pygame.init()

size = (width, height) = (800, 600)
screen = pygame.display.set_mode(size)


class SceneElement:
    def __init__(self, x, y, width, height, color):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.color = color


class Scene:
    def __init__(self):
        self.elements = [
            SceneElement(150, 150, 200, 200, (55, 55, 10, 0.3)),
            SceneElement(250, 300, 200, 200, (155, 200, 10, 0.5)),
        ]

    def render(self, offset):
        screen.fill((255, 255, 255))
        for element in self.elements:
            x = element.x + offset[0]
            y = element.y + offset[1]
            pygame.draw.rect(screen, element.color, (x, y, element.width, element.height))


scene = Scene()
offset = pygame.Vector2()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEWHEEL:
            print(event.x, event.y)
            if event.x == 0 and event.y == 1:
                direction = pygame.Vector2(event.x, event.y).normalize()
                offset += direction * 10
            elif event.x == 0 and event.y == -1:
                direction = pygame.Vector2(event.x, event.y).normalize()
                offset += direction * 10
            elif event.x == -1 and event.y == 0:
                direction = pygame.Vector2(1, event.y).normalize()
                offset += direction * 10
            elif event.x == 1 and event.y == 0:
                direction = pygame.Vector2(-1, event.y).normalize()
                offset += direction * 10

    scene.render(offset)
    pygame.display.flip()

pygame.quit()

This seems to work much more smoothly, but I feel like this could be improved still: https://imgur.com/a/7WNNQIl

EDIT: Based on Matiiss's comment, using their answer and replacing event.x with -event.x seems to work like the above without if/elif:

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEWHEEL:
            direction = pygame.Vector2(-event.x, event.y).normalize()
            offset += direction * 10

    scene.render(offset)
    pygame.display.flip()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM