Search code examples
pythonscrollpygameaspect-ratiotile

Issue with Incorrect Tile Numbers When Scaling Window in Pygame


Could you identify a bug in my code and explain why, when I use int((mouse_x + scroll_x) // (tw / MAX_COL)), int((mouse_y // th / MAX_ROW)) and hover over some tile, it consistently prints incorrect tile numbers every time I scale my window? For instance, when referring to tile (19, 0), the printed number is incorrect, it displays (21, 0), and the offset increases or descreases with a larger window scale. It also occurs only if I scroll my window firts. My code:

import pygame as pg
import json

SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT
WIDTH = 640
HEIGHT = 360
TILE_SIZE = 32
MAX_COL = 20
MAX_ROW = 10

pg.init()

# 16:9 R
screen = pg.display.set_mode((WIDTH, HEIGHT), pg.RESIZABLE) # 0, 0
s = pg.Surface([WIDTH, HEIGHT])

pg.display.set_caption("Level editor")

scroll_x = 0

move_front = False
move_back = False

img = pg.image.load("rock.png") # .get_rect().colliderect() -> bool

world_data = []
for col in range(MAX_COL):
    world_data.append([-1 for i in range(MAX_ROW)])

def draw_world():
    for x, col in enumerate(world_data):
        for y, row in enumerate(col):
            if row >= 0:
                s.blit(img, (x * TILE_SIZE - scroll_x, y * TILE_SIZE))
    
    with open("data.json", "w") as file:
        json.dump(world_data, file)


def draw_grid():

    s.fill((255, 255, 255))

    for v in range(MAX_COL + 1):
        # vertical / col
        pg.draw.line(s, (0, 0, 0), (TILE_SIZE * v - scroll_x, 0), (TILE_SIZE * v - scroll_x, HEIGHT))
        # horizontal / row
    for h in range(MAX_ROW + 1):
        pg.draw.line(s, (0, 0, 0), (0 - scroll_x, TILE_SIZE * h), (MAX_COL * TILE_SIZE - scroll_x, TILE_SIZE * h))

def draw_bg():
    global tw, th
    sw = screen.get_width()
    sh = screen.get_height()
    tw = sw
    th = tw / ASPECT
    if th > sh:
        th = sh
        tw = th * ASPECT
    pad_x = sw / 2 - tw / 2
    pad_y = sh / 2 - th / 2

    f = pg.transform.scale(s, (tw ,th))
    screen.blit(f, (pad_x, pad_y))

while True:

    draw_grid()
    draw_world()
    draw_bg()

    if move_front: # move_right
        scroll_x += 1
    elif move_back:
        scroll_x -= 1

    mouse_x, mouse_y = pg.mouse.get_pos()

    grid_x = screen.get_width() / 2 - tw / 2
    grid_y = screen.get_height() / 2 - th / 2
    mouse_x -= grid_x
    mouse_y -= grid_y

    print(int((mouse_x + scroll_x) // (tw / MAX_COL)), int((mouse_y // th / MAX_ROW)))

    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_d:
                move_front = True
            elif event.key == pg.K_a:
                move_back = True
        if event.type == pg.KEYUP:
            if event.key == pg.K_d:
                move_front = False
            elif event.key == pg.K_a:
                move_back = False
        if event.type == pg.MOUSEBUTTONDOWN:
            if mouse_x >= 0 and mouse_y >= 0:
                if mouse_x < tw and mouse_y < th:
                    pass

    pg.display.flip()

enter image description here enter image description here enter image description here


Solution

  • The problem is that you scale scroll_x when calculating the column. However, the grid is scrolled before it is scaled, so the tile size used when drawing the grid must be used to calculate the scroll offset:

    print(int((mouse_x + scroll_x) // (tw / MAX_COL)), int((mouse_y // th / MAX_ROW)))

    print(int(scroll_x / TILE_SIZE + mouse_x / (tw / MAX_COL)), int((mouse_y // (th / MAX_ROW))))