ArXen42
@ArXen42

Как обеспечить строгий порядок подписки на события?

Разрабатываю игру на юнити. Логика довольно объемна, поэтому оказалось очень удобным использовать события (event'ы). И пока должны выполняться несвязанные друг с другом события - например начало хода и фиксация камеры на игроке, все хорошо: в соответствующих скриптах подписываемся на событие, все красиво и удобно.
А вот другая ситуация: игрок щелкает мышью по детали корабля, нужно сначала вызвать метод, узнающий, на какуюю деталь щелкнул игрок, и только после этого несколько скриптов должны показать информацию об этой детали. Естественно, если вызвать в обратном порядке, то вылетит ошибка. Проблема: подписка скриптами осуществляется в Awake() или OnEnable() - при загрузке сцены. И никогда не знаешь, чей OnEnable() будет вызван первым. Какие есть варианты решения проблемы? Я вижу такие:
1)Делать два разных события - одно на обработку клика, второе для остальных, вызываемое в конце первого. В некоторых случаях я так и делал.
2)Делать подписку всех методов в одном месте. Т.е. в первом скрипте кроме своего метода подписывать и остальные - получится уродливая простыня, ООП в ужасе. Я такое использовал только один раз и все пытаюсь уйти от этого совсем. Вот, полюбуйтесь:
spoiler
using System.Collections;
using UnityEngine;

public class EventController_script : MonoBehaviour       //Только методы, которые должны подписываться в строгом порядке
{
	public EventContainer_class events;
	public Trajectory_script trajectoryScript;
	public TrajectoryGUI_script trajectoryGUIScript;
	public PlayerMov_script playerMovScript;
	public DateGUI_script dateGUIScript;
	public CamMov_script camMovScript;

	void OnEnable()
	{
		events = GetComponent<EventContainer_class>();

		EventContainer_class.OnTrajectoryCall_event += events.CallOnTrajectoryCleanEvent;
		EventContainer_class.OnTrajectoryCall_event += trajectoryScript.CalculateTrajectory;
		EventContainer_class.OnTrajectoryCall_event += trajectoryGUIScript.DrawTrajectory;

		EventContainer_class.OnTrajectoryClean_event += trajectoryGUIScript.CleanTrajectory;

		EventContainer_class.OnTurnStart_event += WorldState_class.StartTurn;
		EventContainer_class.OnTurnStart_event += dateGUIScript.UpdateDateBar;
		EventContainer_class.OnTurnStart_event += events.CallOnTrajectoryCleanEvent;
		EventContainer_class.OnTurnStart_event += events.CallOnCamReturnEvent;

		EventContainer_class.OnTurnEnd_event += WorldState_class.StopTurn;

		EventContainer_class.OnLand_event += WorldState_class.StopTurn;
		EventContainer_class.OnLand_event += SceneSwitch_class.Land;

		EventContainer_class.OnJump_event += WorldState_class.StopTurn;
		EventContainer_class.OnJump_event += SceneSwitch_class.Jump;
	}

	void OnDisable()
	{
		events = GetComponent<EventContainer_class>();

		EventContainer_class.OnTrajectoryCall_event -= events.CallOnTrajectoryCleanEvent;
		EventContainer_class.OnTrajectoryCall_event -= trajectoryScript.CalculateTrajectory;
		EventContainer_class.OnTrajectoryCall_event -= trajectoryGUIScript.DrawTrajectory;

		EventContainer_class.OnTrajectoryClean_event -= trajectoryGUIScript.CleanTrajectory;

		EventContainer_class.OnTurnStart_event -= WorldState_class.StartTurn;
		EventContainer_class.OnTurnStart_event -= dateGUIScript.UpdateDateBar;
		EventContainer_class.OnTurnStart_event -= events.CallOnTrajectoryCleanEvent;
		EventContainer_class.OnTurnStart_event -= events.CallOnCamReturnEvent;

		EventContainer_class.OnTurnEnd_event -= WorldState_class.StopTurn;

		EventContainer_class.OnLand_event -= WorldState_class.StopTurn;
		EventContainer_class.OnLand_event -= SceneSwitch_class.Land;

		EventContainer_class.OnJump_event -= WorldState_class.StopTurn;
		EventContainer_class.OnJump_event -= SceneSwitch_class.Jump;
	}
}

И такой способ действует только для тех методов, которые должны быть подписаны один раз и существуют в единственном экземпляре. В общем, это решением не назвать.
3)Ваши варианты?

Еще у меня такой вопрос возник: где объявлять события? Пока у меня для этого отдельный класс-синглтон, в принципе устраивает, но интересно как в нормальных проектах такие вещи делаются.

P.S. поправка. OnEnable(), Awake() и прочие вызываются в порядке иерархии игровых объектов - метод родителя вызывается первым. Потестил - вроде действительно так. Но вот подписчики - они точно вызываются в порядке подписки? Хотя в какой-то статье читал, что да, но тем не менее. Да и как быть, если объекты не в иерархии друг друга или их иерархия заранее не известна?
  • Вопрос задан
  • 472 просмотра
Решения вопроса 1
@Espleth
Игрок кликает мышью. Посылается raycast или что-то типа того. Этот raycast ударяется о Collider. Берем GetComponent<ShipPart> (или как он там называется) у нашего Collider и вызываем наше событие с полученным ShipPart. Не пойму, в чем проблема?
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы