Добавил ускоренный A* из библиотеки pathfinding - произволительность выросла, но несильно. Пока этот вариант закомментил, сейчас реализация BFS + walkable матрица, работает гораздо лучше, с неоптимальным рендером 100 объектов держит, без рендера 300.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
285
common.py
285
common.py
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
import random
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
@@ -12,6 +13,14 @@ import pygame_gui
|
|||||||
|
|
||||||
from classes import Rock
|
from classes import Rock
|
||||||
|
|
||||||
|
from pathfinding.core.grid import Grid
|
||||||
|
from pathfinding.finder.a_star import AStarFinder
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from heapq import heappush, heappop
|
||||||
|
from math import sqrt, inf
|
||||||
|
|
||||||
|
|
||||||
def path_exists(data, path):
|
def path_exists(data, path):
|
||||||
current = data
|
current = data
|
||||||
for key in path:
|
for key in path:
|
||||||
@@ -146,87 +155,233 @@ def path_exists(data, path):
|
|||||||
# print("Путь не найден (нет связи)")
|
# print("Путь не найден (нет связи)")
|
||||||
# return None
|
# return None
|
||||||
|
|
||||||
|
#def find_way(cells, start, goal):
|
||||||
|
# """Находит путь с диагональным движением, но БЕЗ прохода через углы"""
|
||||||
|
# rows = len(cells)
|
||||||
|
# if rows == 0:
|
||||||
|
# return None
|
||||||
|
# cols = len(cells[0])
|
||||||
|
#
|
||||||
|
# def is_passable(cell):
|
||||||
|
# return (cell.terrain_obj is None or not isinstance(cell.terrain_obj, Rock))
|
||||||
|
#
|
||||||
|
# s_row, s_col = start
|
||||||
|
# g_row, g_col = goal
|
||||||
|
# if not (0 <= s_row < rows and 0 <= s_col < cols and 0 <= g_row < rows and 0 <= g_col < cols):
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# start_cell = cells[s_row][s_col]
|
||||||
|
# goal_cell = cells[g_row][g_col]
|
||||||
|
# if not is_passable(start_cell) or not is_passable(goal_cell):
|
||||||
|
# print(f"Старт/гол непроходимы")
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# directions = [
|
||||||
|
# (-1, 0), (1, 0), (0, -1), (0, 1), # ортогональ (1.0)
|
||||||
|
# (-1, -1), (-1, 1), (1, -1), (1, 1) # диагональ (1.414)
|
||||||
|
# ]
|
||||||
|
#
|
||||||
|
# def can_move_diagonal(current_r, current_c, dr, dc):
|
||||||
|
# """Проверяет, можно ли двигаться по диагонали (НЕ через угол)"""
|
||||||
|
# nr, nc = current_r + dr, current_c + dc
|
||||||
|
#
|
||||||
|
# # Ортогональные соседи ДОЛЖНЫ быть проходимыми для диагонального хода
|
||||||
|
# ortho1_r, ortho1_c = current_r + dr, current_c # вертикальный сосед
|
||||||
|
# ortho2_r, ortho2_c = current_r, current_c + dc # горизонтальный сосед
|
||||||
|
#
|
||||||
|
# # Проверяем границы для ортососедей
|
||||||
|
# if not (0 <= ortho1_r < rows and 0 <= ortho1_c < cols):
|
||||||
|
# return False
|
||||||
|
# if not (0 <= ortho2_r < rows and 0 <= ortho2_c < cols):
|
||||||
|
# return False
|
||||||
|
#
|
||||||
|
# return (is_passable(cells[ortho1_r][ortho1_c]) and
|
||||||
|
# is_passable(cells[ortho2_r][ortho2_c]))
|
||||||
|
#
|
||||||
|
# open_set = []
|
||||||
|
# h = max(abs(s_row - g_row), abs(s_col - g_col))
|
||||||
|
# heappush(open_set, (h, 0, s_row, s_col))
|
||||||
|
#
|
||||||
|
# came_from = {}
|
||||||
|
# g_score = defaultdict(lambda: float('inf'))
|
||||||
|
# g_score[(s_row, s_col)] = 0
|
||||||
|
#
|
||||||
|
# while open_set:
|
||||||
|
# _, _, row, col = heappop(open_set)
|
||||||
|
# if (row, col) == (g_row, g_col):
|
||||||
|
# path = []
|
||||||
|
# current = (row, col)
|
||||||
|
# while current in came_from:
|
||||||
|
# path.append(current)
|
||||||
|
# current = came_from[current]
|
||||||
|
# path.append(start)
|
||||||
|
# return path[::-1]
|
||||||
|
#
|
||||||
|
# for dr, dc in directions:
|
||||||
|
# nr, nc = row + dr, col + dc
|
||||||
|
# if 0 <= nr < rows and 0 <= nc < cols:
|
||||||
|
# target_cell = cells[nr][nc]
|
||||||
|
#
|
||||||
|
# if (nr, nc) != start and target_cell.creature_obj is not None:
|
||||||
|
# continue
|
||||||
|
# if not is_passable(target_cell):
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# if abs(dr) + abs(dc) == 2:
|
||||||
|
# if not can_move_diagonal(row, col, dr, dc):
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# move_cost = 1.414 if abs(dr) + abs(dc) == 2 else 1.0
|
||||||
|
# tentative_g = g_score[(row, col)] + move_cost
|
||||||
|
# pos = (nr, nc)
|
||||||
|
#
|
||||||
|
# if tentative_g < g_score[pos]:
|
||||||
|
# came_from[pos] = (row, col)
|
||||||
|
# g_score[pos] = tentative_g
|
||||||
|
# h = max(abs(nr - g_row), abs(nc - g_col))
|
||||||
|
# f = tentative_g + h
|
||||||
|
# heappush(open_set, (f, tentative_g, nr, nc))
|
||||||
|
#
|
||||||
|
# print("Путь не найден")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
|
||||||
|
#def find_way(cells, start, goal):
|
||||||
|
# """A* pathfinding — только новая библиотека"""
|
||||||
|
# rows = len(cells)
|
||||||
|
# if rows == 0:
|
||||||
|
# print("Путь не найден: пустая карта")
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# cols = len(cells[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):
|
||||||
|
# print(f"Путь не найден: выход за границы карты {start} -> {goal}")
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# # ★ НАХОДИМ существо в start ★
|
||||||
|
# start_creature = cells[s_row][s_col].creature_obj
|
||||||
|
#
|
||||||
|
# # Матрица препятствий
|
||||||
|
# matrix = [[1 for _ in cells[row]] for row in range(rows)]
|
||||||
|
#
|
||||||
|
# for r in range(rows):
|
||||||
|
# for c in range(cols):
|
||||||
|
# cell_creature = cells[r][c].creature_obj
|
||||||
|
# if cell_creature and cell_creature != start_creature:
|
||||||
|
# matrix[r][c] = 0
|
||||||
|
#
|
||||||
|
# from pathfinding.core.grid import Grid
|
||||||
|
# from pathfinding.finder.a_star import AStarFinder
|
||||||
|
#
|
||||||
|
# grid = Grid(matrix=matrix)
|
||||||
|
# start_node = grid.node(s_row, s_col)
|
||||||
|
# end_node = grid.node(g_row, g_col)
|
||||||
|
#
|
||||||
|
# finder = AStarFinder()
|
||||||
|
# path_nodes, _ = finder.find_path(start_node, end_node, grid)
|
||||||
|
#
|
||||||
|
# if not path_nodes or len(path_nodes) <= 1:
|
||||||
|
# print(f"Путь не найден: {start} -> {goal}")
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# path = [(node.x, node.y) for node in path_nodes]
|
||||||
|
#
|
||||||
|
# return path
|
||||||
|
|
||||||
|
|
||||||
def find_way(cells, start, goal):
|
def find_way(cells, start, goal):
|
||||||
"""Находит путь с диагональным движением, но БЕЗ прохода через углы"""
|
"""★СУПЕРБЫСТРЫЙ BFS: массивы вместо set/deque★"""
|
||||||
rows = len(cells)
|
rows = len(cells)
|
||||||
if rows == 0:
|
if rows == 0:
|
||||||
|
print("Путь не найден: пустая карта")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cols = len(cells[0])
|
cols = len(cells[0])
|
||||||
|
|
||||||
def is_passable(cell):
|
|
||||||
return (cell.terrain_obj is None or not isinstance(cell.terrain_obj, Rock))
|
|
||||||
|
|
||||||
s_row, s_col = start
|
s_row, s_col = start
|
||||||
g_row, g_col = goal
|
g_row, g_col = goal
|
||||||
if not (0 <= s_row < rows and 0 <= s_col < cols and 0 <= g_row < rows and 0 <= g_col < cols):
|
|
||||||
|
if (s_row >= rows or s_col >= cols or
|
||||||
|
g_row >= rows or g_col >= cols):
|
||||||
|
print(f"Путь не найден: выход за границы {start} -> {goal}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
start_cell = cells[s_row][s_col]
|
# ★ МАТРИЦЫ вместо set (10x быстрее хэширования) ★
|
||||||
goal_cell = cells[g_row][g_col]
|
walkable = [[True] * cols for _ in range(rows)]
|
||||||
if not is_passable(start_cell) or not is_passable(goal_cell):
|
rocks_only = [[False] * cols for _ in range(rows)]
|
||||||
print(f"Старт/гол непроходимы")
|
start_creature = cells[s_row][s_col].creature_obj
|
||||||
return None
|
|
||||||
|
|
||||||
directions = [
|
for r in range(rows):
|
||||||
(-1, 0), (1, 0), (0, -1), (0, 1), # ортогональ (1.0)
|
for c in range(cols):
|
||||||
(-1, -1), (-1, 1), (1, -1), (1, 1) # диагональ (1.414)
|
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
|
||||||
|
|
||||||
def can_move_diagonal(current_r, current_c, dr, dc):
|
# ★ ВЫЧИСЛЯЕМЫЕ МАССИВЫ ★
|
||||||
"""Проверяет, можно ли двигаться по диагонали (НЕ через угол)"""
|
visited = [[False] * cols for _ in range(rows)]
|
||||||
nr, nc = current_r + dr, current_c + dc
|
parent = [[None] * cols for _ in range(rows)]
|
||||||
|
|
||||||
|
# ★ БЫСТРАЯ ОЧЕРЕДЬ: индекс вместо deque ★
|
||||||
|
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
|
||||||
|
|
||||||
|
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:
|
||||||
ortho1_r, ortho1_c = current_r + dr, current_c # вертикальный сосед
|
|
||||||
ortho2_r, ortho2_c = current_r, current_c + dc # горизонтальный сосед
|
|
||||||
|
|
||||||
# Проверяем границы для ортососедей
|
|
||||||
if not (0 <= ortho1_r < rows and 0 <= ortho1_c < cols):
|
|
||||||
return False
|
|
||||||
if not (0 <= ortho2_r < rows and 0 <= ortho2_c < cols):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return (is_passable(cells[ortho1_r][ortho1_c]) and
|
|
||||||
is_passable(cells[ortho2_r][ortho2_c]))
|
|
||||||
|
|
||||||
open_set = []
|
|
||||||
h = max(abs(s_row - g_row), abs(s_col - g_col))
|
|
||||||
heappush(open_set, (h, 0, s_row, s_col))
|
|
||||||
|
|
||||||
came_from = {}
|
|
||||||
g_score = defaultdict(lambda: float('inf'))
|
|
||||||
g_score[(s_row, s_col)] = 0
|
|
||||||
|
|
||||||
while open_set:
|
|
||||||
_, _, row, col = heappop(open_set)
|
|
||||||
if (row, col) == (g_row, g_col):
|
|
||||||
path = []
|
path = []
|
||||||
current = (row, col)
|
cr, cc = g_row, g_col
|
||||||
while current in came_from:
|
while True:
|
||||||
path.append(current)
|
path.append((cr, cc))
|
||||||
current = came_from[current]
|
if parent[cr][cc] is None:
|
||||||
path.append(start)
|
break
|
||||||
|
pr, pc = parent[cr][cc]
|
||||||
|
cr, cc = pr, pc
|
||||||
return path[::-1]
|
return path[::-1]
|
||||||
|
|
||||||
for dr, dc in directions:
|
for dr, dc in directions:
|
||||||
nr, nc = row + dr, col + dc
|
nr, nc = r + dr, c + dc
|
||||||
if 0 <= nr < rows and 0 <= nc < cols and is_passable(cells[nr][nc]):
|
|
||||||
|
if (0 <= nr < rows and 0 <= nc < cols and
|
||||||
|
walkable[nr][nc] and not visited[nr][nc]):
|
||||||
|
|
||||||
# ★ ПРОВЕРКА ДИАГОНАЛИ ★
|
# ★ ПРОВЕРКА ДИАГОНАЛИ ★
|
||||||
if abs(dr) + abs(dc) == 2: # диагональное движение
|
if dr * dc == 0 or can_move_diagonal(r, c, nr, nc, rocks_only, rows, cols):
|
||||||
if not can_move_diagonal(row, col, dr, dc):
|
visited[nr][nc] = True
|
||||||
continue # пропускаем запрещённую диагональ
|
parent[nr][nc] = (r, c)
|
||||||
|
|
||||||
move_cost = 1.414 if abs(dr) + abs(dc) == 2 else 1.0
|
# ★ БЫСТРОЕ добавление в очередь ★
|
||||||
tentative_g = g_score[(row, col)] + move_cost
|
queue[queue_size] = [nr, nc]
|
||||||
pos = (nr, nc)
|
queue_size += 1
|
||||||
|
|
||||||
if tentative_g < g_score[pos]:
|
|
||||||
came_from[pos] = (row, col)
|
|
||||||
g_score[pos] = tentative_g
|
|
||||||
h = max(abs(nr - g_row), abs(nc - g_col))
|
|
||||||
f = tentative_g + h
|
|
||||||
heappush(open_set, (f, tentative_g, nr, nc))
|
|
||||||
|
|
||||||
print("Путь не найден")
|
print(f"Путь не найден: {start} -> {goal}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def can_move_diagonal(r, c, nr, nc, rocks_only, rows, cols):
|
||||||
|
"""Запрет среза только около rock"""
|
||||||
|
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 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
|
||||||
@@ -926,7 +926,11 @@
|
|||||||
"sprite_name": "grass_small"
|
"sprite_name": "grass_small"
|
||||||
},
|
},
|
||||||
"item_obj": {},
|
"item_obj": {},
|
||||||
"creature_obj": {}
|
"creature_obj": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "2",
|
||||||
|
"sprite_name": "elf_watching"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"terrain_obj": {
|
"terrain_obj": {
|
||||||
|
|||||||
72
eb_engine.py
72
eb_engine.py
@@ -1,4 +1,5 @@
|
|||||||
from common import os, json, uuid, deepcopy, dataclass, field
|
from common import os, json, uuid, deepcopy, random
|
||||||
|
from common import dataclass, field
|
||||||
from common import pygame, pygame_gui
|
from common import pygame, pygame_gui
|
||||||
import eb_objects
|
import eb_objects
|
||||||
import eb_terrain_objects
|
import eb_terrain_objects
|
||||||
@@ -68,6 +69,9 @@ class Map:
|
|||||||
def move_obj(self, type, start, goal):
|
def move_obj(self, type, start, goal):
|
||||||
"""Перемещает объект типа 'terrain_obj', 'item_obj' или 'creature_obj'
|
"""Перемещает объект типа 'terrain_obj', 'item_obj' или 'creature_obj'
|
||||||
из клетки start=(row, col) в goal=(row, col)"""
|
из клетки start=(row, col) в goal=(row, col)"""
|
||||||
|
if goal is None:
|
||||||
|
return False
|
||||||
|
|
||||||
s_y, s_x = start
|
s_y, s_x = start
|
||||||
d_y, d_x = goal
|
d_y, d_x = goal
|
||||||
|
|
||||||
@@ -79,8 +83,9 @@ class Map:
|
|||||||
source_cell = self.cells[s_y][s_x]
|
source_cell = self.cells[s_y][s_x]
|
||||||
dest_cell = self.cells[d_y][d_x]
|
dest_cell = self.cells[d_y][d_x]
|
||||||
obj = getattr(source_cell, type)
|
obj = getattr(source_cell, type)
|
||||||
|
check = getattr(dest_cell, type)
|
||||||
|
|
||||||
if obj is None:
|
if obj is None or check is not None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
setattr(source_cell, type, None)
|
setattr(source_cell, type, None)
|
||||||
@@ -287,6 +292,11 @@ class Engine:
|
|||||||
gc.collect()
|
gc.collect()
|
||||||
mem_before = process.memory_info().rss / 1024
|
mem_before = process.memory_info().rss / 1024
|
||||||
|
|
||||||
|
|
||||||
|
NUM_ELVES = 1000
|
||||||
|
elf_count = 0
|
||||||
|
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
time_delta = clock.tick(60)/1000.0
|
time_delta = clock.tick(60)/1000.0
|
||||||
#pygame.event.clear()
|
#pygame.event.clear()
|
||||||
@@ -301,6 +311,19 @@ class Engine:
|
|||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
running = False
|
running = False
|
||||||
|
|
||||||
|
if event.type == pygame.MOUSEWHEEL:
|
||||||
|
scroll_y = event.y
|
||||||
|
|
||||||
|
if scroll_y > 0:
|
||||||
|
easy_map.scale += self.scale_step * 5
|
||||||
|
self.spr_scale += self.scale_step * 5
|
||||||
|
self.scale_sprites()
|
||||||
|
elif scroll_y < 0 and easy_map.scale >= self.scale_step:
|
||||||
|
easy_map.scale -= self.scale_step * 5
|
||||||
|
self.spr_scale -= self.scale_step * 5
|
||||||
|
self.scale_sprites()
|
||||||
|
|
||||||
|
|
||||||
if event.type == pygame_gui.UI_TEXT_ENTRY_FINISHED and event.ui_element == input_entry:
|
if event.type == pygame_gui.UI_TEXT_ENTRY_FINISHED and event.ui_element == input_entry:
|
||||||
user_text = input_entry.get_text()
|
user_text = input_entry.get_text()
|
||||||
exec(user_text)
|
exec(user_text)
|
||||||
@@ -349,15 +372,44 @@ class Engine:
|
|||||||
if keys[pygame.K_d]:
|
if keys[pygame.K_d]:
|
||||||
easy_map.cam_x -= self.camera_step
|
easy_map.cam_x -= self.camera_step
|
||||||
|
|
||||||
if keys[pygame.K_q]:
|
#if keys[pygame.K_q]:
|
||||||
easy_map.scale += self.scale_step
|
# easy_map.scale += self.scale_step
|
||||||
self.spr_scale += self.scale_step
|
# self.spr_scale += self.scale_step
|
||||||
self.scale_sprites()
|
# self.scale_sprites()
|
||||||
if keys[pygame.K_e] and easy_map.scale >= self.scale_step:
|
#if keys[pygame.K_e] and easy_map.scale >= self.scale_step:
|
||||||
easy_map.scale -= self.scale_step
|
# easy_map.scale -= self.scale_step
|
||||||
self.spr_scale -= self.scale_step
|
# self.spr_scale -= self.scale_step
|
||||||
self.scale_sprites()
|
# self.scale_sprites()
|
||||||
|
|
||||||
|
|
||||||
|
if keys[pygame.K_SPACE] and elf_count < NUM_ELVES:
|
||||||
|
# Находим свободные клетки
|
||||||
|
# ★ МАССОВЫЙ СПАВН 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:
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
||||||
mouse_pos = pygame.mouse.get_pos()
|
mouse_pos = pygame.mouse.get_pos()
|
||||||
console_rect = console_window.get_abs_rect()
|
console_rect = console_window.get_abs_rect()
|
||||||
|
|||||||
@@ -38,13 +38,28 @@ class Creature(Object):
|
|||||||
|
|
||||||
move_progress: float = 0.0 # 0.0 = старт клетки, 1.0 = конец клетки
|
move_progress: float = 0.0 # 0.0 = старт клетки, 1.0 = конец клетки
|
||||||
current_target: tuple = None # (row, col) следующая клетка
|
current_target: tuple = None # (row, col) следующая клетка
|
||||||
|
final_goal: tuple = None
|
||||||
move_speed: float = 0.02 # пикселей/кадр (настройте)
|
move_speed: float = 0.02 # пикселей/кадр (настройте)
|
||||||
render_offset: tuple = (0.0, 0.0)
|
render_offset: tuple = (0.0, 0.0)
|
||||||
start_pos: tuple = None # (row, col) начальная позиция сегмента пути
|
start_pos: tuple = None # (row, col) начальная позиция сегмента пути
|
||||||
|
|
||||||
|
replan_counter: int = 0
|
||||||
|
REPLAN_INTERVAL: int = 300
|
||||||
|
|
||||||
|
|
||||||
|
def replan(self, cells, pos):
|
||||||
|
from common import find_way
|
||||||
|
path = find_way(cells, pos, self.final_goal)
|
||||||
|
if path and len(path) > 1:
|
||||||
|
self.waypoints = path[1:]
|
||||||
|
self.current_target = self.waypoints[0]
|
||||||
|
else:
|
||||||
|
self.waypoints.clear()
|
||||||
|
self.current_target = None
|
||||||
|
|
||||||
def move(self, cells, start, goal):
|
def move(self, cells, start, goal):
|
||||||
from common import find_way
|
from common import find_way
|
||||||
|
self.final_goal = goal
|
||||||
self.start_pos = start
|
self.start_pos = start
|
||||||
path = find_way(cells, start, goal)
|
path = find_way(cells, start, goal)
|
||||||
if path and len(path) > 1:
|
if path and len(path) > 1:
|
||||||
@@ -59,6 +74,24 @@ class Creature(Object):
|
|||||||
if self.current_target is None or not self.waypoints:
|
if self.current_target is None or not self.waypoints:
|
||||||
self.render_offset = (0.0, 0.0)
|
self.render_offset = (0.0, 0.0)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.replan_counter += 1
|
||||||
|
if self.replan_counter >= self.REPLAN_INTERVAL:
|
||||||
|
self.replan_counter = 0
|
||||||
|
self.replan(map_obj.cells, self.start_pos)
|
||||||
|
|
||||||
|
|
||||||
|
if self.current_target is None: return
|
||||||
|
target_row, target_col = self.current_target
|
||||||
|
if (target_row in map_obj.cells and
|
||||||
|
target_col < len(map_obj.cells[target_row])):
|
||||||
|
target_cell = map_obj.cells[target_row][target_col]
|
||||||
|
if target_cell.creature_obj is not None:
|
||||||
|
self.current_target = None
|
||||||
|
self.waypoints.clear()
|
||||||
|
self.render_offset = (0.0, 0.0)
|
||||||
|
self.replan(map_obj.cells, self.start_pos)
|
||||||
|
return
|
||||||
|
|
||||||
self.move_progress += self.move_speed * time_delta * 60
|
self.move_progress += self.move_speed * time_delta * 60
|
||||||
self.move_progress = min(self.move_progress, 1.0)
|
self.move_progress = min(self.move_progress, 1.0)
|
||||||
@@ -66,7 +99,7 @@ class Creature(Object):
|
|||||||
if self.move_progress >= 1.0:
|
if self.move_progress >= 1.0:
|
||||||
map_obj.move_obj('creature_obj', self.start_pos, self.current_target)
|
map_obj.move_obj('creature_obj', self.start_pos, self.current_target)
|
||||||
|
|
||||||
self.waypoints.pop(0)
|
if self.waypoints: self.waypoints.pop(0)
|
||||||
if self.waypoints:
|
if self.waypoints:
|
||||||
self.start_pos = self.current_target # Новая клетка как старт
|
self.start_pos = self.current_target # Новая клетка как старт
|
||||||
self.current_target = self.waypoints[0]
|
self.current_target = self.waypoints[0]
|
||||||
@@ -79,7 +112,10 @@ class Creature(Object):
|
|||||||
|
|
||||||
# ★ ТОЛЬКО интерполяция offset ★
|
# ★ ТОЛЬКО интерполяция offset ★
|
||||||
start_row, start_col = self.start_pos #or (0, 0)
|
start_row, start_col = self.start_pos #or (0, 0)
|
||||||
target_row, target_col = self.current_target
|
if self.current_target is None:
|
||||||
|
self.render_offset = (0.0, 0.0)
|
||||||
|
return
|
||||||
|
target_row, target_col = self.current_target
|
||||||
offset_x = (target_col - start_col) * cell_size * self.move_progress
|
offset_x = (target_col - start_col) * cell_size * self.move_progress
|
||||||
offset_y = (target_row - start_row) * cell_size * self.move_progress
|
offset_y = (target_row - start_row) * cell_size * self.move_progress
|
||||||
self.render_offset = (offset_x, offset_y)
|
self.render_offset = (offset_x, offset_y)
|
||||||
|
|||||||
12
main.py
12
main.py
@@ -1,8 +1,18 @@
|
|||||||
import eb_engine
|
import eb_engine
|
||||||
|
#import os
|
||||||
|
#os.environ['PYTHONJIT'] = '1'
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
e = eb_engine.Engine()
|
e = eb_engine.Engine()
|
||||||
e.main_loop()
|
e.main_loop()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
# todo:
|
||||||
|
# прокрутка баг консоль и карта
|
||||||
|
# ОПТИМИЗАЦИИ
|
||||||
|
# очередь задач и задача рандомного патруля
|
||||||
|
# устроить краш тест поиску пути, запустив много объектов на маленьком поле, успел заметить баги
|
||||||
|
# добавить функцию движения за каким-то объектом
|
||||||
|
# сделать, чтобы в случае отменненого движения не телепортировался назад, а плавно
|
||||||
|
# приступаем к логике
|
||||||
@@ -50,8 +50,7 @@ scale_image() вызывается 150×150=22,500 раз в секунду пр
|
|||||||
# проверить у ллм на ошибки - РЕГУЛЯРНАЯ АКТИВНОСТЬ:
|
# проверить у ллм на ошибки - РЕГУЛЯРНАЯ АКТИВНОСТЬ:
|
||||||
# - deepcopy +
|
# - deepcopy +
|
||||||
# - общие +
|
# - общие +
|
||||||
# !!! ДОБАВИТЬ ПРОКРУТКУ И МАСШТАБ КАРТЫ ДЛЯ МЫШИ !!!
|
#
|
||||||
#
|
|
||||||
# ДОДЕЛАТЬ move для Creature - хранить pos в объекте ???
|
# ДОДЕЛАТЬ move для Creature - хранить pos в объекте ???
|
||||||
#
|
#
|
||||||
# ПРОВЕРИТЬ МЕНЯЕТСЯ ЛИ ПЕРЕДАННЫЙ В ОБЪЕКТ cells и если да,
|
# ПРОВЕРИТЬ МЕНЯЕТСЯ ЛИ ПЕРЕДАННЫЙ В ОБЪЕКТ cells и если да,
|
||||||
|
|||||||
Reference in New Issue
Block a user