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

This commit is contained in:
shiva404
2026-03-06 04:00:43 +03:00
parent fa189a4c3b
commit b1548ea182
6 changed files with 529 additions and 50 deletions

226
common.py
View File

@@ -26,6 +26,12 @@ def path_exists(data, path):
return False
return True
def find_way(cells, start, goal, walkable, rocks_only):
result = bfs_quick(cells, start, goal, walkable, rocks_only)
return result
'''
#def find_way(cells, start, goal):
# """Находит путь от start=(row, col) к goal=(row, col). row=y (строка), col=x (столбец)"""
# rows = len(cells)
@@ -289,8 +295,9 @@ def path_exists(data, path):
#
# return path
'''
def find_way(cells, start, goal):
def bfs_quick(cells, start, goal, walkable, rocks_only):
"""★СУПЕРБЫСТРЫЙ BFS: массивы вместо set/deque★"""
rows = len(cells)
if rows == 0:
@@ -303,22 +310,22 @@ def find_way(cells, start, goal):
if (s_row >= rows or s_col >= cols or
g_row >= rows or g_col >= cols):
print(f"Путь не найден: выход за границы {start} -> {goal}")
#print(f"Путь не найден: выход за границы {start} -> {goal}")
return None
# ★ МАТРИЦЫ вместо set (10x быстрее хэширования) ★
walkable = [[True] * cols for _ in range(rows)]
rocks_only = [[False] * cols for _ in range(rows)]
start_creature = cells[s_row][s_col].creature_obj
for r in range(rows):
for c in range(cols):
cell = cells[r][c]
if (cell.creature_obj and cell.creature_obj != start_creature) or \
(cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small"):
walkable[r][c] = False
if cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small":
rocks_only[r][c] = True
## ★ МАТРИЦЫ вместо set (10x быстрее хэширования) ★
#walkable = [[True] * cols for _ in range(rows)]
#rocks_only = [[False] * cols for _ in range(rows)]
#start_creature = cells[s_row][s_col].creature_obj
#
#for r in range(rows):
# for c in range(cols):
# cell = cells[r][c]
# if (cell.creature_obj and cell.creature_obj != start_creature) or \
# (cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small"):
# walkable[r][c] = False
# if cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small":
# rocks_only[r][c] = True
# ★ ВЫЧИСЛЯЕМЫЕ МАССИВЫ ★
visited = [[False] * cols for _ in range(rows)]
@@ -380,4 +387,191 @@ def can_move_diagonal(r, c, nr, nc, rocks_only, rows, cols):
check1_ok = (0 <= r1 < rows and 0 <= c1 < cols and not rocks_only[r1][c1])
check2_ok = (0 <= r2 < rows and 0 <= c2 < cols and not rocks_only[r2][c2])
return check1_ok and check2_ok
return check1_ok and check2_ok
'''
#def bfs_quick_d(obstacle_matrix, start, goal):
# rows = len(obstacle_matrix)
# if rows == 0:
# print("❌ DEBUG: ПУСТАЯ МАТРИЦА")
# return None
#
# cols = len(obstacle_matrix[0])
# s_row, s_col = start
# g_row, g_col = goal
#
# print(f"🔍 DEBUG: start={start}, goal={goal}, размер={rows}x{cols}")
#
# if (s_row >= rows or s_col >= cols or
# g_row >= rows or g_col >= cols):
# print(f"❌ DEBUG: ВЫХОД ЗА ГРАНИЦЫ: start({s_row},{s_col}) goal({g_row},{g_col})")
# return None
#
# print(f"✅ DEBUG: Границы OK. obstacle[start]={obstacle_matrix[s_row][s_col]}, obstacle[goal]={obstacle_matrix[g_row][g_col]}")
#
# visited = [[False] * cols for _ in range(rows)]
# parent = [[None] * cols for _ in range(rows)]
#
# queue_size = 0
# queue = [[0, 0] for _ in range(rows * cols)]
# queue[0] = [s_row, s_col]
# queue_size = 1
# front = 0
#
# visited[s_row][s_col] = True
# print(f"✅ DEBUG: Старт добавлен в очередь. queue_size={queue_size}")
#
# directions = [(-1,0), (1,0), (0,-1), (0,1), (-1,-1), (-1,1), (1,-1), (1,1)]
#
# DEBUG_COUNTER = 0
#
# while front < queue_size:
# r, c = queue[front]
# front += 1
# DEBUG_COUNTER += 1
#
# print(f"🔄 DEBUG[{DEBUG_COUNTER}]: обрабатываем ({r},{c}), очередь={front}/{queue_size}")
#
# if r == g_row and c == g_col:
# print(f"✅ DEBUG: НАЙДЕН ЦЕЛЬ! Путь: ({r},{c})")
# path = []
# cr, cc = g_row, g_col
# while True:
# path.append((cr, cc))
# if parent[cr][cc] is None:
# break
# pr, pc = parent[cr][cc]
# cr, cc = pr, pc
# return path[::-1]
#
# for dr, dc in directions:
# nr, nc = r + dr, c + dc
#
# print(f" 📍 Проверяем соседа ({nr},{nc}): граница={0<=nr<rows and 0<=nc<cols}, "
# f"visited={visited[nr][nc]}, obstacle={obstacle_matrix[nr][nc]}")
#
# if (0 <= nr < rows and 0 <= nc < cols and
# not visited[nr][nc] and
# not obstacle_matrix[nr][nc]):
#
# diagonal_ok = True
# if dr * dc != 0:
# diagonal_ok = can_move_diagonal(r, c, nr, nc, obstacle_matrix)
# print(f" ↘️ Диагональ: {diagonal_ok}")
#
# if diagonal_ok:
# visited[nr][nc] = True
# parent[nr][nc] = (r, c)
# queue[queue_size] = [nr, nc]
# queue_size += 1
# print(f" ✅ Добавили ({nr},{nc}) в очередь. queue_size={queue_size}")
# else:
# print(f" ❌ Диагональ заблокирована!")
# else:
# print(f" ❌ Сосед отклонен!")
#
# print(f"❌ DEBUG: ОЧЕРЕДЬ ОПУСТЕЛА! Обработано {DEBUG_COUNTER} узлов")
# print(f" Последняя клетка в очереди: {queue[front-1] if front > 0 else 'ПУСТО'}")
# print(f" Цель ({g_row},{g_col}) помечена как visited? {visited[g_row][g_col]}")
# return None
#
#
#def bfs_quick(obstacle_matrix, start, goal):
# """★СУПЕРБЫСТРЫЙ BFS 8-направлений★
# obstacle_matrix - ТОЛЬКО камни
# """
# rows = len(obstacle_matrix)
# if rows == 0:
# return None
#
# cols = len(obstacle_matrix[0])
# s_row, s_col = start
# g_row, g_col = goal
#
# if (s_row >= rows or s_col >= cols or
# g_row >= rows or g_col >= cols):
# return None
#
# # ★ МАТРИЦЫ состояния ★
# visited = [[False] * cols for _ in range(rows)]
# parent = [[None] * cols for _ in range(rows)]
#
# # ★ БЫСТРАЯ ОЧЕРЕДЬ ★
# queue_size = 0
# queue = [[0, 0] for _ in range(rows * cols)]
# queue[0] = [s_row, s_col]
# queue_size = 1
# front = 0
#
# visited[s_row][s_col] = True
#
# # ★ 8 НАПРАВЛЕНИЙ ★
# directions = [(-1,0), (1,0), (0,-1), (0,1), # Кардинальные (всегда)
# (-1,-1), (-1,1), (1,-1), (1,1)] # Диагональные
#
# while front < queue_size:
# r, c = queue[front]
# front += 1
#
# if r == g_row and c == g_col:
# # ★ Реконструкция пути ★
# path = []
# cr, cc = g_row, g_col
# while True:
# path.append((cr, cc))
# if parent[cr][cc] is None:
# break
# pr, pc = parent[cr][cc]
# cr, cc = pr, pc
# return path[::-1]
#
# for dr, dc in directions:
# nr, nc = r + dr, c + dc
#
# if not (0 <= nr < rows and 0 <= nc < cols):
# continue
# if visited[nr][nc] or obstacle_matrix[nr][nc]:
# continue
#
# diagonal_ok = True
# if dr * dc != 0:
# diagonal_ok = can_move_diagonal(r, c, nr, nc, obstacle_matrix)
# if diagonal_ok:
# visited[nr][nc] = True
# parent[nr][nc] = (r, c)
# queue[queue_size] = [nr, nc]
# queue_size += 1
#
# return None
'''
#def can_move_diagonal(r, c, nr, nc, obstacle_matrix):
# """Диагональ БЛОКИРУЕТСЯ только камнями по углам"""
# rows, cols = len(obstacle_matrix), len(obstacle_matrix[0])
# dr = nr - r
# dc = nc - c
#
# r1, c1 = r + dr, c # Вертикальная соседка
# r2, c2 = r, c + dc # Горизонтальная соседка
#
# check1_ok = (0 <= r1 < rows and 0 <= c1 < cols and not obstacle_matrix[r1][c1])
# check2_ok = (0 <= r2 < rows and 0 <= c2 < cols and not obstacle_matrix[r2][c2])
#
# return check1_ok and check2_ok
#def can_move_diagonal(r, c, nr, nc, obstacle_matrix):
# """Проверка диагонали с границами"""
# rows, cols = len(obstacle_matrix), len(obstacle_matrix[0])
# dr = nr - r
# dc = nc - c
#
# # ★ ПРОВЕРКА ГРАНИЦ ПОПЕРЕДУ ★
# r1, c1 = r + dr, c # вертикальная
# r2, c2 = r, c + dc # горизонтальная
#
# # Если УЖЕ за границей - False
# if not (0 <= r1 < rows and 0 <= c1 < cols):
# return False
# if not (0 <= r2 < rows and 0 <= c2 < cols):
# return False
#
# return not obstacle_matrix[r1][c1] and not obstacle_matrix[r2][c2]