Всем привет.
Ситуация следующая:
Есть модуль создания волн вражеских кораблей.
Корабли состоят в отряде(ширенга, шахматный, клин и т.д.), отряды в формации(фаланга, шахматная, клин и т.д.), этакий Bridge. Формация содержится в классе EnemyWave
, который является MonoBehaviour
и отвечает за различные параметры(тип кораблей, количество кораблей, отрядов и прочее). Класс EnemyWaveController
отвечает за волны кораблей(генерит их, меняет тип, например сразу только легкие корабли, потом средние и т.д.).
Корабли при уничтожении активируют событие EnemyDieEvent
, Отряд же подписывается на него и при активации события смотрит есть ли еще живые корабли, если нет то генерит событие SquadDieEvent
, аналогично формация по этому событию смотрит есть ли живые отряды и если их нет, то активирует событие FormationDieEvent
, дальше EnemyWave
при получении события создает новую формацию и активирует событие CanGenerateNewWaveEvent
, генерация формации, отрядов и кораблей происходит в классе EnemyWaveController
(при старте, а потом по событию CanGenerateNewWaveEvent
)
Проблема в том, что при уничтожении первого же корабля происходит вся цепочка событий и спавнится куча новых кораблей(при уничтожении 3го корабля unity у меня зависает от спавна такого количества кораблей). Где косяк не пойму, прошу помогите пожалуйста).
Скрин до уничтожения первого корабля:
После:
Классы:
namespace Core.EnemyWaves
{
public class EnemyWavesController : MonoBehaviour
{
[SerializeField]
private EnemyWave[] enemyWaves;
[SerializeField]
private float delay;
private EnemyWave _currentWave;
private int _enemyWaveIndex;
private void Awake()
{
_enemyWaveIndex = 0;
enemyWaves[_enemyWaveIndex].gameObject.SetActive(true);
for (int i = 1; i < enemyWaves.Length; i++)
{
enemyWaves[i].gameObject.SetActive(false);
}
_currentWave = enemyWaves[_enemyWaveIndex];
ManagerProvider.EventManager.CanGenerateNewWaveEvent.Subscribe(OnWaveDestroyed);
}
private void Start()
{
_currentWave.GenerateWave();
}
private void OnDestroy()
{
ManagerProvider.EventManager.CanGenerateNewWaveEvent.Unsubscribe(OnWaveDestroyed);
}
private void OnWaveChanged()
{
_enemyWaveIndex++;
if (_enemyWaveIndex < enemyWaves.Length - 1)
{
enemyWaves[_enemyWaveIndex - 1].gameObject.SetActive(false);
enemyWaves[_enemyWaveIndex].gameObject.SetActive(true);
_currentWave = enemyWaves[_enemyWaveIndex];
}
}
private void OnWaveDestroyed()
{
StartCoroutine(GenerateNewWave());
}
private IEnumerator GenerateNewWave()
{
yield return new WaitForSeconds(delay);
_currentWave.GenerateWave();
}
}
}
namespace Core.EnemyWaves
{
public class EnemyWave : MonoBehaviour
{
[SerializeField]
private GameObject[] enemyShipPrefabs;
[SerializeField]
private Vector2 startPosition;
[SerializeField]
private float squadOffset;
[SerializeField]
private float shipOffset;
[SerializeField]
private int countShipsInSquad;
[SerializeField]
private int countSqudsInFormation;
[SerializeField]
private int _countKindsOfFormation = 4;
private BaseFormation _formation;
public void GenerateWave()
{
_formation.GenerateFormation();
}
private void Awake()
{
_formation = CreateRandomFormation();
}
private void OnEnable()
{
ManagerProvider.EventManager.FormationDieEvent.Subscribe(OnFormationDie);
}
private void OnDisable()
{
ManagerProvider.EventManager.FormationDieEvent.Unsubscribe(OnFormationDie);
}
private void OnFormationDie()
{
_formation.Dispose();
_formation = CreateRandomFormation();
ManagerProvider.EventManager.CanGenerateNewWaveEvent.OnEvent();
}
private BaseFormation CreateRandomFormation()
{
int rand = Random.Range(0, _countKindsOfFormation);
switch (rand)
{
case 0:
return new FalangeFormation(this.startPosition, this.squadOffset, this.shipOffset,
this.countShipsInSquad, this.countSqudsInFormation, this.enemyShipPrefabs);
case 1:
return new WedgeFormation(this.startPosition, this.squadOffset, this.shipOffset,
this.countShipsInSquad, this.countSqudsInFormation, this.enemyShipPrefabs);
case 2:
return new TriangleFormation(this.startPosition, this.squadOffset, this.shipOffset,
this.countShipsInSquad, this.countSqudsInFormation, this.enemyShipPrefabs);
case 3:
return new ChessFormation(this.startPosition, this.squadOffset, this.shipOffset,
this.countShipsInSquad, this.countSqudsInFormation, this.enemyShipPrefabs);
default:
goto case 0;
}
}
}
}
namespace Core.EnemyWaves.Formations
{
public abstract class BaseFormation
{
private List<BaseSquad> _squads;
protected Vector2 startPosition;
protected float shipOffset;
protected int countShipsInSquad;
protected GameObject[] enemyShipPrefabs;
protected float SquadOffset { get; set; }
protected int CountSquads { get; private set; }
public BaseFormation
(Vector2 startPosition, float squadOffest, float shipOffset,
int countShipsInSquad, int countSquads, GameObject[] enemyShipPrefabs)
{
_squads = new List<BaseSquad>();
this.startPosition = startPosition;
this.shipOffset = shipOffset;
this.countShipsInSquad = countShipsInSquad;
SquadOffset = squadOffest;
CountSquads = countSquads;
if (enemyShipPrefabs == null || enemyShipPrefabs.Length == 0)
{
Debug.LogError("Enemy ship prefabs must be not null or length more then 0");
}
this.enemyShipPrefabs = enemyShipPrefabs;
ManagerProvider.EventManager.SquadDieEvent.Subscribe(OnSquadDie);
_squads.AddRange(CreateSquads());
}
public void GenerateFormation()
{
foreach (BaseSquad squad in _squads)
{
squad.GenerateSquad();
}
Debug.Log("Count squads in formation=" + _squads.Count);
}
public void Dispose()
{
ManagerProvider.EventManager.SquadDieEvent.Unsubscribe(OnSquadDie);
}
protected abstract BaseSquad[] CreateSquads();
private void OnSquadDie(BaseSquad squad)
{
_squads.Remove(squad);
squad.Dispose();
Debug.Log("Left Squads in formation=" + _squads.Count);
if (_squads.Count == 0)
{
ManagerProvider.EventManager.FormationDieEvent.OnEvent();
}
}
}
}
namespace Core.EnemyWaves.Formations
{
public class FalangeFormation : BaseFormation
{
public FalangeFormation(Vector2 startPosition, float squadOffest, float shipOffset,
int countShipsInSquad, int countSquads, GameObject[] enemyShipPrefabs)
: base(startPosition, squadOffest, shipOffset,
countShipsInSquad, countSquads, enemyShipPrefabs)
{
}
protected override BaseSquad[] CreateSquads()
{
BaseSquad[] result = new BaseSquad[CountSquads];
for (int i = 0; i < CountSquads; i++)
{
int rand = Random.Range(0, base.enemyShipPrefabs.Length);
result[i] = new SquadLine
(base.enemyShipPrefabs[rand], base.startPosition, base.shipOffset, base.countShipsInSquad);
base.startPosition.y -= base.SquadOffset;
}
return result;
}
}
}
namespace Core.EnemyWaves.Squads
{
public abstract class BaseSquad
{
protected List<GameObject> enemyShips;
protected GameObject enemyShipPrefab;
protected Vector2 startPosition;
protected float shipOffset;
protected int countShips;
public BaseSquad
(GameObject enemyShipPrefab, Vector2 startPosition, float shipOffset, int countShips)
{
enemyShips = new List<GameObject>();
this.enemyShipPrefab = enemyShipPrefab;
this.startPosition = new Vector2(startPosition.x, startPosition.y);
this.shipOffset = shipOffset;
this.countShips = countShips;
ManagerProvider.EventManager.EnemyDieEvent.Subscribe(OnEnemyShipDie);
}
public abstract void GenerateSquad();
public void Dispose()
{
ManagerProvider.EventManager.EnemyDieEvent.Unsubscribe(OnEnemyShipDie);
}
private void OnEnemyShipDie(GameObject enemyShip)
{
this.enemyShips.Remove(enemyShip);
Debug.Log("Left ships in squad=" + this.enemyShips.Count);
if (this.enemyShips.Count == 0)
{
ManagerProvider.EventManager.SquadDieEvent.OnEvent(this);
}
}
}
}
namespace Core.EnemyWaves.Squads
{
public class SquadLine : BaseSquad
{
public SquadLine
(GameObject enemyShipPrefab, Vector2 startPosition, float shipOffset, int countShips)
: base(enemyShipPrefab, startPosition, shipOffset, countShips)
{
}
public override void GenerateSquad()
{
for (int i = 0; i < base.countShips; i++)
{
GameObject enemyShipInstance = LeanPool.Spawn
(base.enemyShipPrefab, base.startPosition, base.enemyShipPrefab.transform.rotation);
base.enemyShips.Add(enemyShipInstance);
base.startPosition.x += base.shipOffset;
}
Debug.Log("Count ships in squad=" + base.enemyShips.Count);
}
}
}
namespace Core.Events
{
public interface IEvent
{
void OnEvent();
void Subscribe(Action handler);
void Unsubscribe(Action handler);
}
public interface IEvent<T>
{
void OnEvent(T type);
void Subscribe(Action<T> handler);
void Unsubscribe(Action<T> handler);
}
}
namespace Core.Events
{
public class SquadDieEvent : IEvent<BaseSquad>
{
private event Action<BaseSquad> _squadDie;
public void OnEvent(BaseSquad squad)
{
_squadDie?.Invoke(squad);
}
public void Subscribe(Action<BaseSquad> handler)
{
_squadDie += handler;
}
public void Unsubscribe(Action<BaseSquad> handler)
{
_squadDie -= handler;
}
}
}
namespace Core.Managers
{
[RequireComponent(typeof(EventManager))]
[RequireComponent(typeof(PlayerManager))]
[RequireComponent(typeof(ScoreManager))]
[RequireComponent(typeof(AudioManager))]
[RequireComponent(typeof(AnimationManager))]
[RequireComponent(typeof(LevelManager))]
public class ManagerProvider : MonoBehaviour
{
private List<AbstractManager> _managers;
public static EventManager EventManager { get; private set; }
public static PlayerManager PlayerManager { get; private set; }
public static ScoreManager ScoreManager { get; private set; }
public static AudioManager AudioManager { get; private set; }
public static AnimationManager AnimationManager { get; private set; }
public static LevelManager LevelManager { get; private set; }
private void Awake()
{
Initialize();
DontDestroyOnLoad(this.gameObject);
LevelManager.LoadLevel(Levels.MainMenu);
}
private void OnDestroy()
{
foreach (AbstractManager manager in _managers)
{
manager.Finalization();
}
}
private void Initialize()
{
EventManager = GetComponent<EventManager>();
ScoreManager = GetComponent<ScoreManager>();
AudioManager = GetComponent<AudioManager>();
AnimationManager = GetComponent<AnimationManager>();
PlayerManager = GetComponent<PlayerManager>();
LevelManager = GetComponent<LevelManager>();
_managers = new List<AbstractManager>
{
EventManager,
ScoreManager,
AudioManager,
AnimationManager,
PlayerManager,
LevelManager
};
foreach (AbstractManager manager in _managers)
{
manager.Initialization();
}
}
}
}
namespace Core.Managers
{
public abstract class AbstractManager : MonoBehaviour
{
public abstract void Initialization();
public abstract void Finalization();
}
}
namespace Core.Managers
{
public class EventManager : AbstractManager
{
public EnemyDieEvent EnemyDieEvent { get; private set; }
public SquadDieEvent SquadDieEvent { get; private set; }
public FormationDieEvent FormationDieEvent { get; private set; }
public CanGenerateNewWaveEvent CanGenerateNewWaveEvent { get; private set; }
public PlayerDieEvent PlayerDieEvent { get; private set; }
public HeavyScoreEvent HeavyScoreEvent { get; private set; }
public override void Initialization()
{
EnemyDieEvent = new EnemyDieEvent();
SquadDieEvent = new SquadDieEvent();
FormationDieEvent = new FormationDieEvent();
CanGenerateNewWaveEvent = new CanGenerateNewWaveEvent();
PlayerDieEvent = new PlayerDieEvent();
HeavyScoreEvent = new HeavyScoreEvent();
}
public override void Finalization()
{
}
}
}
namespace Core
{
public abstract class BaseDie : MonoBehaviour
{
[SerializeField]
private int health;
[SerializeField]
protected GameObject explosionPrefab;
[SerializeField]
protected AudioClip explosionAudio;
protected int CurrentHealth { get; set; }
public virtual void TakeDamage(int damage)
{
CurrentHealth -= damage;
}
public void OnSpawn()
{
CurrentHealth = health;
}
}
}
namespace Core
{
[RequireComponent(typeof(ItemsDroper))]
public class EnemyDie : BaseDie
{
[SerializeField]
private long scoreCost;
private ItemsDroper _itemDropper;
private void Awake()
{
_itemDropper = GetComponent<ItemsDroper>();
}
public override void TakeDamage(int damage)
{
base.TakeDamage(damage);
if (CurrentHealth <= 0)
{
Transform selfTransform = this.transform;
ManagerProvider.AnimationManager.PlayAnimatedObject
(base.explosionPrefab, selfTransform.position, selfTransform.rotation);
ManagerProvider.AudioManager.PlayClip(base.explosionAudio);
_itemDropper.DropRandomItem(selfTransform.position, selfTransform.rotation);
ManagerProvider.ScoreManager.Score += this.scoreCost;
ManagerProvider.EventManager.EnemyDieEvent.OnEvent(this.gameObject);
LeanPool.Despawn(this.gameObject);
}
}
}
}
Если что-то упустил, пишите добавлю.
Пример
Проблема решена.
Дело было в том, что объектов волн было несколько(с разными параметрами). Они все подписывались на событие и так как на момент его срабатывания, у волн были пустые списки с кораблями(по тому, что генерация была отдельно от создания объектов(формации, отрядов)), из-за этого они спавнили новые корабли, хоть и сами объекты волн были деактивированы.
Решил таким способом:
генерацию сделал в момент создания формации, ответственным за генерацию сделал EnemyWave(раньше был EnemyWaveController), и в событиях добавил проверку на активность объекта.
Виртуальный выделенный сервер (VDS) становится отличным выбором
У меня есть иерархия типов произвольной глубины, которая в конце реализует IPlugin<T> или IPlugin<T1, T2> интерфейс, который находится в другойdll
пытаюсь сделать авторизацию на ларавел с двумя ролямиНе понимаю как проверить права пользователя
я создал таблицы users, post и psusers - пользователь, post - почты, ps - хранятся id users и post