/
Vibek
/
First_Game
Обзор
Документация
Войти
/
Vibek
/
First_Game
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
controllers/separation_system.py
106 строк
3 KB
Vibek
Оптимизация, иснхронизация хитбоксов
21 июн 2026, 17:09
21 июн 2026, 17:09
bbdda9b
Код
Авторство
О чём код?
""" Контроллер выталкивания врагов друг из друга и от игрока. Broad-phase: SpatialHashGrid.query_nearby; narrow-phase: круговое push-apart. """ from __future__ import annotations import math from typing import Any from pygame.math import Vector2 from core.config import Config from controllers.algorithms.spatial_hash import SpatialHashGrid from models.enemies.enemy_base import EnemyBase, EnemyState from models.player import Player class EnemySeparationSystem: """Разводит перекрывающихся врагов после chase-движения.""" @staticmethod def apply( enemies: list[EnemyBase], spatial_grid: SpatialHashGrid, player: Player | None, dt: float, ) -> None: margin = int(Config.ENEMY_SEPARATION_RADIUS) for _ in range(Config.ENEMY_SEPARATION_ITERATIONS): for enemy in enemies: if not EnemySeparationSystem._can_be_pushed(enemy): continue push = EnemySeparationSystem._compute_push( enemy, spatial_grid, player, margin, ) if push.length_squared() == 0: continue max_push = enemy.speed * dt * 2.0 if max_push > 0 and push.length() > max_push: push.scale_to_length(max_push) enemy.pos += push enemy._finalize_movement() @staticmethod def _can_be_pushed(enemy: EnemyBase) -> bool: return ( enemy.is_alive and enemy.state == EnemyState.CHASE and not enemy.is_stunned ) @staticmethod def _is_obstacle(other: Any, player: Player | None) -> bool: if isinstance(other, EnemyBase): return other.is_alive if player is not None and other is player: return player.is_alive return False @staticmethod def _collision_radius(entity: Any) -> float: hitbox = getattr(entity, "hitbox", None) if hitbox is None: return 16.0 return max(hitbox.width, hitbox.height) * 0.5 @staticmethod def _compute_push( enemy: EnemyBase, spatial_grid: SpatialHashGrid, player: Player | None, margin: int, ) -> Vector2: push = Vector2(0, 0) enemy_radius = EnemySeparationSystem._collision_radius(enemy) enemy_center = enemy.hitbox_center for other in spatial_grid.query_nearby(enemy, margin=margin): if other is enemy: continue if not EnemySeparationSystem._is_obstacle(other, player): continue other_center = other.hitbox_center other_radius = EnemySeparationSystem._collision_radius(other) delta = enemy_center - other_center dist_sq = delta.length_squared() min_dist = enemy_radius + other_radius if dist_sq < 1e-6: angle_deg = (id(enemy) ^ id(other)) % 360 delta = Vector2(1, 0).rotate(angle_deg) dist = 0.0 else: dist = math.sqrt(dist_sq) if dist >= min_dist: continue overlap = (min_dist - dist) * Config.ENEMY_SEPARATION_STRENGTH push += delta.normalize() * overlap return push