@dimadjims595

Как исправить отображение объектов в pygame?

Хочу создать приложение интерактивной карты для игры War Thunder. Данные для карты получаю с локалхоста двумя json файлами: 1) координаты объектов (x и y), 2) Свойства карты: параметры карты (grid_steps, grid_zero, map_max, map_min.) и изображение карты в формате img. Ранее удалось реализовать отображение карты через matplotlib, однако из-за необходимости обновлять карту минимум раз в 5 секунд (объекты движутся) пришлось перейти на pygame (изображение получалось рваным, фризы каждые 5 секунд). Получилось импортировать и открывать карту, но вот с объектами и сеткой квадратов какая-то беда. Квадраты слишком больших размеров и выходят за пределы изображения карты, а все объекты почему-то скапливаются и отображаются в левом верхнем углу, хотя они явно должны быть где-то ниже и не налеплены один на другого. Мои попытки "исправить положение" привели меня к тому что я удалил сетку квадратов и попробовал сделать так чтобы координаты объектов считались от grid_zero. Но тогда объекты и вовсе пропали из поля зрения. Я попробовал разобраться почему так и прикрутил matplotlib для того чтобы посмотреть куда смещаются объекты, но оказалось что они и так уже находятся на карте, но я их при этом не вижу. Собственно я немного запутался и не понимаю как грамотно нужно реализовать работу с получаемыми данными. Внятного описания того что за что отвечает у меня нет, но приблизительно я понял так, что grid_steps - это масштаб одного квадрата координатной сетки, grid_zero - начало отсчёта, map_max и map_min - оси по Х и Y. Я подозреваю что то-ли у меня изображение карты перекрывает объекты, то ли я неправильно задал параметры для карты. Ниже привожу код на pygame:
map_info = MapInformation()
        map_info.grid_steps = list(map(float, map_info_data.get('grid_steps', [])))
        map_info.grid_zero = list(map(float, map_info_data.get('grid_zero', [])))
        map_info.map_max = list(map(float, map_info_data.get('map_max', [])))
        map_info.map_min = list(map(float, map_info_data.get('map_min', [])))
        map_info.valid = map_info_data.get('valid', False)
        
        # Create MapObjects and store their positions
        map_objects = []
        for obj_data in map_obj_data:
            map_object = MapObject(
                type=obj_data.get('type', ''),
                color=obj_data.get('color', ''),
                colors=obj_data.get('color[]', []),
                blink=obj_data.get('blink', 0),
                icon=obj_data.get('icon', ''),
                icon_bg=obj_data.get('icon_bg', ''),
                x=float(obj_data.get('x', 0.0)),
                y=float(obj_data.get('y', 0.0)),
                dx=float(obj_data.get('dx', 0.0)),
                dy=float(obj_data.get('dy', 0.0)),
                sx=float(obj_data.get('sx', 0.0)),
                sy=float(obj_data.get('sy', 0.0)),
                ex=float(obj_data.get('ex', 0.0)),
                ey=float(obj_data.get('ey', 0.0))
            )
            map_objects.append(map_object)
        
        return map_info, map_img, map_objects
    except requests.RequestException as e:
        print(f"Error loading data: {e}")
        return None, None, None

def to_absolute(x, y, map_info):
    map_min_x = map_info.map_min[0]
    map_min_y = map_info.map_min[1]
    map_max_x = map_info.map_max[0]
    map_max_y = map_info.map_max[1]
    
    grid_zero_x = map_info.grid_zero[0]
    grid_zero_y = map_info.grid_zero[1]
    
    abs_x = grid_zero_x + (map_min_x + x * (map_max_x - map_min_x) - map_min_x)
    abs_y = grid_zero_y + (map_min_y + (1 - y) * (map_max_y - map_min_y) - map_min_y)
    
    return abs_x, abs_y

def save_positions(map_objects):
    # Save positions to a file or data structure for later plotting
    positions = {
        'x': [],
        'y': [],
        'sx': [],
        'sy': [],
        'ex': [],
        'ey': []
    }
    
    for obj in map_objects:
        positions['x'].append(obj.x)
        positions['y'].append(obj.y)
        positions['sx'].append(obj.sx)
        positions['sy'].append(obj.sy)
        positions['ex'].append(obj.ex)
        positions['ey'].append(obj.ey)
    
    return positions

def draw_objects(screen, map_info, map_objects, offset, zoom):
    for obj in map_objects:
        obj_type = obj.type
        color = tuple(obj.colors) if obj.colors else tuple(obj.color)
        if obj_type == "airfield":
            sx, sy = to_absolute(obj.sx, obj.sy, map_info)
            ex, ey = to_absolute(obj.ex, obj.ey, map_info)
            sx = (sx - map_info.map_min[0]) * zoom + offset[0]
            sy = (sy - map_info.map_min[1]) * zoom + offset[1]
            ex = (ex - map_info.map_min[0]) * zoom + offset[0]
            ey = (ey - map_info.map_min[1]) * zoom + offset[1]
            pygame.draw.rect(screen, color, pygame.Rect(sx, sy, ex - sx, ey - sy))
        elif obj_type == "aircraft":
            x, y = to_absolute(obj.x, obj.y, map_info)
            x = (x - map_info.map_min[0]) * zoom + offset[0]
            y = (y - map_info.map_min[1]) * zoom + offset[1]
            dx = obj.dx
            dy = obj.dy
            if obj.icon == "Player":
                draw_aircraft(screen, x, y, dx, dy, (0, 255, 0), zoom)
            else:
                draw_aircraft(screen, x, y, dx, dy, color, zoom)

def draw_aircraft(screen, x, y, dx, dy, color, zoom):
    size = 10 * zoom
    points = [
        (x + dx * size, y + dy * size),
        (x - dy * size / 2, y + dx * size / 2),
        (x + dy * size / 2, y - dx * size / 2),
    ]
    pygame.draw.polygon(screen, color, points)

def plot_positions(map_info, positions, map_objects):
    fig, ax = plt.subplots()
    
    # Plot map area as green square
    map_min_x, map_min_y = map_info.map_min
    map_max_x, map_max_y = map_info.map_max
    map_width = map_max_x - map_min_x
    map_height = map_max_y - map_min_y
    map_rect = plt.Rectangle((map_min_x, map_min_y), map_width, map_height, edgecolor='green', facecolor='none')
    ax.add_patch(map_rect)
    
    # Plot objects with labels
    for obj in map_objects:
        x_label = f"{obj.type} ({obj.icon})" if obj.icon else obj.type
        if obj.type == "airfield":
            sx, sy = obj.sx, obj.sy
            ex, ey = obj.ex, obj.ey
            ax.scatter([sx, ex], [sy, ey], color='red', label=x_label)
        elif obj.type == "aircraft":
            x, y = obj.x, obj.y
            ax.scatter(x, y, color='red', label=x_label)
    
    ax.set_xlim(map_min_x, map_max_x)
    ax.set_ylim(map_min_y, map_max_y)
    ax.set_aspect('equal', adjustable='box')
    ax.set_xlabel('Map X Coordinate')
    ax.set_ylabel('Map Y Coordinate')
    ax.set_title('Object Positions on Map')
    ax.legend()
    
    plt.show()

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Map Viewer')

map_info, map_img, map_objects = load_data()

offset = [0, 0]
zoom = 1.0
zoom_step = 0.1
dragging = False
drag_start = (0, 0)

running = True
last_update = time.time()

def update_data():
    global map_info, map_img, map_objects, last_update
    while running:
        if time.time() - last_update >= 5:
            map_info, map_img, map_objects = load_data()
            last_update = time.time()

update_thread = threading.Thread(target=update_data)
update_thread.start()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_PLUS or event.key == pygame.K_EQUALS:
                zoom += zoom_step
            elif event.key == pygame.K_MINUS:
                zoom = max(0.1, zoom - zoom_step)
            elif event.key == pygame.K_p and pygame.key.get_mods() & pygame.KMOD_CTRL:
                # Save and plot positions on Ctrl + P
                positions = save_positions(map_objects)
                plot_positions(map_info, positions, map_objects)
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                dragging = True
                drag_start = event.pos
        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                dragging = False
        elif event.type == pygame.MOUSEMOTION:
            if dragging:
                mouse_x, mouse_y = event.pos
                offset[0] += mouse_x - drag_start[0]
                offset[1] += mouse_y - drag_start[1]
                drag_start = event.pos

    if map_info and map_img and map_objects:
        screen.fill((0, 0, 0))
        screen.blit(pygame.transform.scale(map_img, (int(map_img.get_width() * zoom), int(map_img.get_height() * zoom))), offset)
        draw_objects(screen, map_info, map_objects, offset, zoom)
        pygame.display.flip()
    else:
        print("Waiting for data...")

pygame.quit()
running = False
update_thread.join()


Вот так выглядит вывод на matplotlib (pygame версия):
6692952aa9bb1062393678.png

А вот как должен выглядеть (версия с matplotlib):
6692955ce49de682140613.png
  • Вопрос задан
  • 99 просмотров
Пригласить эксперта
Ответы на вопрос 1
@dimadjims595 Автор вопроса
Если у кого-то есть альтернативные варианты создания карты с интерактивным интерфейсом - буду рад выслушать
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы