Оптимизация большого количества разрушаемых блоков на Python Arcade

Я пишу игру с разрушаемыми блоками которых будет почти 100к на карте. Они все - спрайты, хранятся в спрайт листе, и отрисовываются листом self.blocks_list.draw(pixelated=True)

Если сделать карту поменьше (примерно 20к блоков) фпс нормальный. Как можно это оптимизировать? Важно сохранить их коллизии чтобы нпс за экраном не падали сквозь блоки.

ps. использую self.blocks_list.use_spatial_hashing=True для оптимизации коллизий.

Дополняю: вот код загрузки блоков

class Block(arcade.Sprite):
    def __init__(self, x, y, id):
        super().__init__(f"resources/sprites/blocks/block.png")
        self.center_x = x
        self.center_y = y
        self.id = id

Ответы (1 шт):

Автор решения: Qwertiy
  1. Отрисовывать надо не всю карту, а только те объекты, которые находятся в видимой области.

  2. Проверка коллизий обычно требует O(n2) проверок - это можно оптимизировать.

    • В первую очередь разделить объекты на подвижные (пусть их будет m<=n) и неподвижные - в коллизии должен участвовать подвижный объект, асимптотика улучшается до O(n*m).
    • Если есть существенное количество подвижных объектов, то объекты можно кластреризировать в группы близкорасположенных, что позволит делать проверку только внутри кластера и, возможно, с соседними кластерами. Простейший способ - поделить по сетке с шагом в квадратный корень размера карты - движущийся объект может попадать не более чем в 4 квадрата сетки одновременно, что при равномерном распределении неподвижных объектов по карте сократит асимптотику до O(b2 * (n*m)1/4) ~ O(n1/4 * m).
  3. Я не знаю, на чём ты пишешь игру, но меня смущает вот эта строка:

    super().__init__(f"resources/sprites/blocks/block.png")
    

    У меня есть подозрение, что для каждого объекта она создаёт новый спрайт даже если картина передаётся одна и та же. Если это так, то программа весьма нерационально использует память (вплоть до файла подкачки, что может её очень существенно замедлять) и кэш процессора.

    Надо убедиться, что каждый файл загружается только один раз и используется единый спрайт для всех объектов. Это вот прям точно надо сделать.

  4. Если копать в эту же сторону, хотя блоки и разрушаемые, вряд ли ожидается, что все они одновременно будут находиться в повреждённом состоянии. Можно воспользоваться паттерном flyweight и до первого повреждения использовать один и тот же объект для одинаковых блоков. Вообще, можно по степени разрушения сделать пул объектов. Но, честно говоря, я не уверен, что это принесёт значительную пользу после выполнения пункта 3.

→ Ссылка