2

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?

2
  • 1
    I'd highly suggest that you use constants instead of plain integers for event types, use elif event.type == pygame.MOUSEWHEEL: otherwise it's confusing
    – Matiiss
    Commented Jan 29, 2023 at 3:51
  • ah, my bad. I didn't actually know that integer was a documented key (or in this case, button?). Also didn't know there was a MOUSEWHEEL support either, but I guess that makes sense. @Matiiss Commented Jan 29, 2023 at 8:35

2 Answers 2

1

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.

0

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://i.sstatic.net/ZPNOO.jpg

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()
3
  • You don't need all those if/elifs... if you need inverted x, just do direction = pygame.Vector2(-event.x, event.y).normalize() and what I showed you should be an exact solution since it handles diagonal movement too, your code handles either of up, down, left, right at a time
    – Matiiss
    Commented Jan 29, 2023 at 10:41
  • that's true, but I noticed that using what you did by itself, while it does work better diagonally, it somehow reverses the movement I'm doing in my original implementation (eg: I press using two fingers and move clockwise, but it moves in the opposite direction, etc). Otherwise, it's true yours work better aside from the "reversed" movement :) @Matiiss Commented Jan 29, 2023 at 10:48
  • ah, I think I got what you meant. By using -event.x using your solution, it does replace the need for all of those if/elif. I'll update the example above, Thank you Commented Jan 29, 2023 at 10:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.