Попробовал самую очевидную оптимизацию рендера, теперь отрисовываются только те клетки, которые в камере. Карта корраптится при движении камеры, надо исправить, зато масштаб работает и прирост производительности очень хороший, теперь до 200 объектов обрабатывается при стабильных 60 ФПС. Промежуточный коммит, хочу посмотреть сколько можно ещё выжать кадров на 500 объектах, потом нужно починить движение камеры. Также написал простую функцию спавна эльфов в главном цикле для создания нагрузки. Эльфы создаются в углах карты и по центру каждые сто тактов цикла. Они начинают перемещаться по карте в случайные точки, создавая относительно равномерную нагрузку поиска пути.

This commit is contained in:
shiva404
2026-03-05 22:00:32 +03:00
parent 45f2c71cb8
commit fa189a4c3b
4 changed files with 123 additions and 50 deletions

View File

@@ -119,13 +119,79 @@ class Map:
if cell.creature_obj:
cell.creature_obj.update(time_delta, self.cell_size, self) # self!
# def draw_map(self, screen, current_frame, grid=True):
# terrain_list = []
# creature_list = []
#
# # ★ 1 ПАСС: собираем terrain и creatures ★
# for j in range(len(self.cells)):
# for i, cell in enumerate(self.cells[j]):
# base_x = i * self.cell_size + self.cam_x
# base_y = j * self.cell_size + self.cam_y
#
# # Terrain данные
# terrain_dd = {
# "x": int(base_x * self.scale), "y": int(base_y * self.scale),
# "w": int(self.cell_size * self.scale - self.cell_dist),
# "h": int(self.cell_size * self.scale - self.cell_dist),
# "spr_up": current_frame % self.sprites_refresh,
# "sprites": self.sprites, "scale": self.scale, "screen": screen
# }
# terrain_list.append((cell.terrain_obj, terrain_dd))
#
# # Creature данные (если есть)
# if cell.creature_obj:
# offset_x, offset_y = cell.creature_obj.render_offset
# creature_dd = terrain_dd.copy()
# creature_dd["x"] = int((base_x + offset_x) * self.scale)
# creature_dd["y"] = int((base_y + offset_y) * self.scale)
# creature_list.append((cell.creature_obj, creature_dd))
#
# # ★ 2 ПАСС: рисуем terrain ★
# for obj, dd in terrain_list:
# obj.draw(dd)
#
# # ★ 3 ПАСС: рисуем ВСЕХ creatures ПОСЛЕ terrain ★
# for obj, dd in creature_list:
# obj.draw(dd)
#
# # ★ 4 ПАСС: grid поверх всего ★
# for j in range(len(self.cells)):
# for i, cell in enumerate(self.cells[j]):
# base_x = i * self.cell_size + self.cam_x
# base_y = j * self.cell_size + self.cam_y
# grid_rect = pygame.Rect(
# int(base_x * self.scale), int(base_y * self.scale),
# int(self.cell_size * self.scale - self.cell_dist),
# int(self.cell_size * self.scale - self.cell_dist)
# )
# color = self.target_color if cell.is_target else self.color
# pygame.draw.rect(screen, color, grid_rect, self.bord)
def draw_map(self, screen, current_frame, grid=True):
screen_rect = screen.get_rect()
terrain_list = []
creature_list = []
# ★ 1 ПАСС: собираем terrain и creatures ★
for j in range(len(self.cells)):
for i, cell in enumerate(self.cells[j]):
# Вычисляем видимую область в координатах карты (аналогично get_cell_at_mouse)
left_map = (self.cam_x * self.scale) / self.scale / self.cell_size
top_map = (self.cam_y * self.scale) / self.scale / self.cell_size
right_map = ((self.cam_x * self.scale + screen_rect.width) / self.scale / self.cell_size)
bottom_map = ((self.cam_y * self.scale + screen_rect.height) / self.scale / self.cell_size)
min_row = max(0, int(top_map - 1)) # -1 для буфера
max_row = min(len(self.cells), int(bottom_map + 2)) # +2 для буфера
min_col = max(0, int(left_map - 1))
max_col = min(len(self.cells[0]) if self.cells and self.cells[0] else 0, int(right_map + 2))
# ★ 1 ПАСС: собираем только видимые terrain и creatures ★
for j in range(min_row, max_row):
if j not in self.cells: continue
row_cells = self.cells[j]
for i in range(min_col, min(max_col, len(row_cells))):
cell = row_cells[i]
base_x = i * self.cell_size + self.cam_x
base_y = j * self.cell_size + self.cam_y
@@ -155,15 +221,18 @@ class Map:
for obj, dd in creature_list:
obj.draw(dd)
# ★ 4 ПАСС: grid поверх всего
for j in range(len(self.cells)):
for i, cell in enumerate(self.cells[j]):
# ★ 4 ПАСС: grid только для видимых ячеек
for j in range(min_row, max_row):
if j not in self.cells: continue
row_cells = self.cells[j]
for i in range(min_col, min(max_col, len(row_cells))):
cell = row_cells[i]
base_x = i * self.cell_size + self.cam_x
base_y = j * self.cell_size + self.cam_y
grid_rect = pygame.Rect(
int(base_x * self.scale), int(base_y * self.scale),
int(self.cell_size * self.scale - self.cell_dist),
int(self.cell_size * self.scale - self.cell_dist)
int(self.cell_size * self.scale - self.cell_dist) # ИСПРАВЛЕНО
)
color = self.target_color if cell.is_target else self.color
pygame.draw.rect(screen, color, grid_rect, self.bord)
@@ -171,6 +240,7 @@ class Map:
@dataclass
class Engine:
sprites: dict = field(default_factory = dict)
@@ -301,13 +371,20 @@ class Engine:
space_pressed = False
spawn = False
def spawn_elf():
elf = eb_objects.Creature(id=f"elf_{random.randint(1000,9999)}", name="Elf", sprite_name="elf_watching", grid_pos = (0, 0))
r_move_short = eb_objects.Action(sprite_name="elf_watching", func = partial(elf.patrol, easy_map.cells, 3), duration = 0.01)
r_move_long = eb_objects.Action(sprite_name="elf_watching", func = partial(elf.move_rand, easy_map.cells, 0, 99), duration = 0.01)
elf.tasks.append([r_move_short, r_move_short, r_move_short, r_move_short, r_move_short, r_move_long])
easy_map.cells[0][0].creature_obj = elf
def spawn_elves():
spawn_positions = [(0,0), (0,99), (99,0), (99,99), (50,50)]
for pos in spawn_positions:
row, col = pos
if (row < len(easy_map.cells) and
col < len(easy_map.cells[row]) and
easy_map.cells[row][col].creature_obj is None):
elf = eb_objects.Creature(id=f"elf_{random.randint(1000,9999)}", name="Elf", sprite_name="elf_watching", grid_pos = pos)
r_move_short = eb_objects.Action(sprite_name="elf_watching", func=partial(elf.patrol, easy_map.cells, 3), duration=0.01)
r_move_long = eb_objects.Action(sprite_name="elf_watching", func=partial(elf.move_rand, easy_map.cells, 0, 99), duration=0.01)
elf.tasks.append([r_move_short]*5 + [r_move_long])
easy_map.cells[row][col].creature_obj = elf
while running:
time_delta = clock.tick(60)/1000.0
@@ -318,8 +395,8 @@ class Engine:
print(f"Leak: {mem_after - mem_before:.1f} KB per 1000 frames")
if global_counter % 100 == 0 and spawn == True:
spawn_elf()
elf_count += 1
spawn_elves()
elf_count += 5
# poll for events
# pygame.QUIT event means the user clicked X to close your window
@@ -410,31 +487,6 @@ class Engine:
self.spr_scale -= self.scale_step
self.scale_sprites()
# # Находим свободные клетки
# # ★ МАССОВЫЙ СПАВН 50 эльфов ★
# free_cells = []
# for j in range(len(easy_map.cells)): # ★ ЛИМИТ строк ★
# for i in range(len(easy_map.cells[j])): # ★ ЛИМИТ колонок ★
# cell = easy_map.cells[j][i]
# if (cell.creature_obj is None and
# cell.terrain_obj and
# cell.terrain_obj.sprite_name == "grass_small"):
# free_cells.append((j, i))
#
# spawn_count = min(50, len(free_cells)) # ★ Не больше свободных клеток ★
# for _ in range(spawn_count):
# row, col = random.choice(free_cells)
# elf = eb_objects.Creature(id=f"elf_{random.randint(1000,9999)}", name="Elf", sprite_name="elf_watching")
# easy_map.cells[row][col].creature_obj = elf
#
# # ★ БЕЗОПАСНЫЙ выбор цели ★
# possible_targets = [c for c in free_cells if c != (row, col)]
# if possible_targets:
# target_cell = random.choice(possible_targets)
# elf.move(easy_map.cells, (row, col), target_cell)
#
# free_cells.remove((row, col)) # Убираем занятые клетки
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
@@ -453,7 +505,6 @@ class Engine:
row, col = active_cell
easy_map.cells[row][col].is_target = True
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
mouse_pos = pygame.mouse.get_pos()
console_rect = console_window.get_abs_rect()