Оптимизация кода pygame

Хочу создать игру на типе 'Песочница' и столкнулся с мелким FPS Перепробывал уже все способы Pypy, numba не работает Как мне лучше оптимизировать этот код ?>


import pygame as pg
from random import choice, randint
from screeninfo import get_monitors as gm
from math import hypot

#######################################
# >>MONITOR SETTING<<
monitor = 0
resoltion = gm()[monitor].width-100, gm()[monitor].height-100
FPS = 60

# >>COLOR SETTINGS<<
SKY = 77, 143, 172
SAND = 252, 221, 118
WATER = 0, 149, 182
EARTH = 162, 101, 62
GRASS = 93, 161, 48
WOOD = 139, 90, 43
BETON = 104, 108, 94
WHITE = 255, 255, 255
BLACK = 0, 0, 0
FIRE1 = 255, 88, 34
FIRE2 = 240, 127, 19
FIRE3 = 253, 207, 88

# >>OBJECTS SETTINGS<<
AIRTEMP = 25

VERSION = '1.0'

#######################################

class Button():
    def __init__(self, color, x,y,width,height, text=''):
        self.color = color
        self.ogcol = color
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text

    def draw(self,win,outline=None):
        if outline:
            pg.draw.rect(win, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
            
        pg.draw.rect(win, self.color, (self.x,self.y,self.width,self.height),0)
        
        if self.text != '':
            font = pg.font.SysFont('Consolas', 24)
            text = font.render(self.text, 1, (0,0,0))
            win.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))

    def isOver(self, pos):
        global STATE
        if pos[0] > self.x and pos[0] < self.x + self.width:
            if pos[1] > self.y and pos[1] < self.y + self.height:
                self.color = (128,128,128)
            else:
                self.color = self.ogcol
        else:
            self.color = self.ogcol
        keys = pg.key.get_pressed()
        if(keys[pg.K_LSHIFT]):
            if pos[0] > self.x and pos[0] < self.x + self.width:
                if pos[1] > self.y and pos[1] < self.y + self.height:
                    return True


objects = []
spawn = True
play = True
ID = 'EMPTY'
hei = 50
NUMS = 10
mglife = ['BETON', 'WATER', 'NONE', 'DIRT', 'WOOD']
onstop = ['DIRT']

def find_collision(obj):
    objs = objects.copy()
    objs.remove(obj)
    for _obj in objs:
        if(_obj['type'] == 'BLOCK'):
            if obj['pos'][0] > _obj['pos'][0] and obj['pos'][0] < _obj['pos'][0] + _obj['pos'][2]:
                if obj['pos'][1] > _obj['pos'][1] and obj['pos'][1] < _obj['pos'][1] + _obj['pos'][3]:
                    return True
        elif(_obj['type'] == 'PARTICLE'):
            if(_obj['pos'] == obj['pos']):
                return True
def update():
    global objects
    global play
    global ID
    if(play):
        if(spawn[0]):
            for _ in range(NUMS):
                pos = pg.mouse.get_pos()
                posp = [randint(pos[0]-(hei/2), pos[0]+(hei/2)), randint(pos[1]-(hei/2), pos[1]+(hei/2))]
                posb = (int(pos[0]-(hei/2)), int(pos[1]-(hei/2)), hei, hei)
                OBJECTS = {
                    'FIRE': {
                        'type': 'PARTICLE',
                        'colors': [FIRE1, FIRE2, FIRE3],
                        'life': randint(30, 50),
                        'pos': posp,
                        'gravity': -1,
                        'temp': 250,
                        'NAME': 'FIRE'
                    },
                    'EMPTY': {
                        'type': 'BLOCK',
                        'color': BLACK,
                        'life': 0,
                        'pos': posb,
                        'gravity': 0,
                        'temp': AIRTEMP,
                        'NAME': 'NONE'
                    },
                    'BETON': {
                        'type': 'BLOCK',
                        'color': BETON,
                        'life': 0,
                        'pos': posb,
                        'gravity': 0,
                        'temp': AIRTEMP,
                        'NAME': 'BETON'
                    },
                    'WATER': {
                        'type': 'PARTICLE',
                        'colors': [WATER],
                        'life': -1,
                        'pos': posp,
                        'gravity': 4,
                        'temp': AIRTEMP,
                        'NAME': 'WATER',
                    },
                    'DIRT': {
                        'type': 'PARTICLE',
                        'colors': [EARTH],
                        'life': -1,
                        'pos': posp,
                        'gravity': 5,
                        'temp': AIRTEMP,
                        'NAME': 'DIRT',
                    },
                    'WOOD': {
                        'type': 'BLOCK',
                        'color': WOOD,
                        'life': -1,
                        'pos': posb,
                        'gravity': 0,
                        'temp': AIRTEMP,
                        'NAME': 'WOOD',
                    }
                }
                objects.append(OBJECTS[ID])
        for obj in objects:
            if(obj['type'] == 'PARTICLE'):
                collide = False
                onflight = find_collision(obj)
                obj['pos'][1] += obj['gravity']
                obj['life'] -= 1
                if(not obj['NAME'] in onstop) and (not onflight):
                    obj['pos'][0] += randint(0, 1); obj['pos'][0] -= randint(0, 1)
                if(obj['life'] <= 0) and (not obj['NAME'] in mglife):
                    objects.remove(obj)
                    continue
                if(obj['pos'][1] >= resoltion[1]):
                    obj['pos'][1] -= obj['gravity']
                    collide = True
                
                if(find_collision(obj)):
                    obj['pos'][1] -= obj['gravity']
                    collide = True
                
                if(collide):
                    st = []
                    obj['pos'][0] -= 2
                    if(not find_collision(obj)):
                        st.append(obj)
                    obj['pos'][0] += 4
                    if(not find_collision(obj)):
                        st.append(obj)
                    obj['pos'][0] -= 2
                    if(len(st) != 0):
                        obj = choice(st)
                
                if(obj['pos'][0] < 0):
                    obj['pos'][0] = obj['gravity']
                elif(obj['pos'][0] > resoltion[0]):
                    obj['pos'][0] = resoltion[0]-obj['gravity']
def draw():
    global scr
    for obj in objects:
        if(obj['type'] == 'PARTICLE'):
            pg.draw.circle(scr, choice(obj['colors']), obj['pos'], 1)
        elif(obj['type'] == 'BLOCK'):
            pg.draw.rect(scr, obj['color'], obj['pos'])

pg.init()
scr = pg.display.set_mode(resoltion)
clock = pg.time.Clock()

scr.fill((BLACK))
pg.display.flip()
pg.display.set_caption('SANDBOX BETA v' + VERSION)

btns = []

albjs = ['FIRE', 'EMPTY', 'BETON', 'WATER']
firebtn = Button(FIRE1, 0, resoltion[1]-100, 100, 50, 'FIRE')
waterbtn = Button(WATER, 0, resoltion[1]-50, 100, 50, 'WATER')
dirtbtn = Button(EARTH, 100, resoltion[1]-100, 100, 50, 'DIRT')
betonbtn = Button(BETON, 100, resoltion[1]-50, 100, 50, 'BETON')
erasebtn = Button(WHITE, 200, resoltion[1]-100, 100, 50, 'ERASE')
woodbtn = Button(WOOD, 200, resoltion[1]-50, 100, 50, 'WOOD')

while True:
    [exit() for event in pg.event.get() if event.type == pg.QUIT]
    keys = pg.key.get_pressed()
    if(keys[pg.K_LCTRL]):
        spawn = (True, 0)
    else:
        spawn = (False, 0)
    if(keys[pg.K_SPACE]):
        play = not play
    if(keys[pg.K_c]):
        objects = []
    
    if(firebtn.isOver(pg.mouse.get_pos())):
        ID = 'FIRE'
    if(waterbtn.isOver(pg.mouse.get_pos())):
        ID = 'WATER'
    if(dirtbtn.isOver(pg.mouse.get_pos())):
        ID = 'DIRT'
    if(betonbtn.isOver(pg.mouse.get_pos())):
        ID = 'BETON'
    if(erasebtn.isOver(pg.mouse.get_pos())):
        ID = 'EMPTY'
    if(woodbtn.isOver(pg.mouse.get_pos())):
        ID = 'WOOD'
    update()
    draw()

    scr.blit(pg.font.SysFont("Arial", 20).render(str(int(clock.get_fps())), 0, pg.Color('white')), (0, 0))
    
    firebtn.draw(scr)
    waterbtn.draw(scr)
    dirtbtn.draw(scr)
    betonbtn.draw(scr)
    erasebtn.draw(scr)
    woodbtn.draw(scr)
    
    pg.display.update()
    scr.fill((BLACK))
    clock.tick(FPS)

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