Решение, которое предоставляет @Everts, довольно хорошее, но, поскольку вы используете Unity, я бы порекомендовал несколько настроек, чтобы лучше работать с редактором. Вместо общего event
я рекомендую использовать UnityEvent
из пространства имен UnityEngine.Events
. Я бы также посоветовал против статики из-за того, как единство идет о сериализации их между сценами. Есть несколько странных крайних случаев, в которые вы можете попасть, если вы не знакомы с тем, как Unity управляет их сериализацией. Если вам просто нужно отправить сообщение другому объекту в той же сцене, я бы порекомендовал игровой менеджер. Вы можете безопасно выполнить GameObject.Find()
in onvalidate()
и связать свои переменные, чтобы избежать снижения производительности во время выполнения поиска. Если эти данные нужно перенести в другую сцену для этого сообщения, вместо этого используйте ScriptableObject
. Это будет выглядеть примерно так:
Поместите этот компонент в "Game Manager" сцены GameObject
public class CountingPassthrough : MonoBehaviour
{
public CountdownTimer countdownTimer;
}
поместите этот компонент в "Таймер" сцены GameObject
public class CountdownTimer : MonoBehaviour
{
public float startingTime = 10f;
public UnityEvent timedOut = new UnityEvent();
private void OnValidate()
{
if(FindObjectOfType<CountingPassthrough>().gameObject.scene == gameObject.scene && FindObjectOfType<CountingPassthrough>() != new UnityEngine.SceneManagement.Scene())
FindObjectOfType<CountingPassthrough>().countdownTimer = this;
}
private void Start()
{
StartCoroutine(TimerCoroutine());
}
// Coroutine is called once per frame
private IEnumerator TimerCoroutine()
{
float currentTime = 0f;
while (currentTime != 0)
{
currentTime = Mathf.Max(0, currentTime - Time.deltaTime);
yield return null;//wait for next frame
}
timedOut.Invoke();
}
}
Поместите этот компонент на GameObject
, который вы хотите использовать таймер
public class user : MonoBehaviour
{
[SerializeField, HideInInspector]
private CountingPassthrough timerObject;
private void OnValidate()
{
if(FindObjectOfType<CountingPassthrough>().gameObject.scene == gameObject.scene && FindObjectOfType<CountingPassthrough>() != new UnityEngine.SceneManagement.Scene())
timerObject = FindObjectOfType<CountingPassthrough>();
}
private void OnEnable()
{
timerObject.countdownTimer.timedOut.AddListener(DoSomething);
}
private void OnDisable()
{
timerObject.countdownTimer.timedOut.RemoveListener(DoSomething);
}
private void DoSomething()
{
//do stuff here...
}
}
Этот рабочий процесс также удобен для префабов, потому что вы можете заключить find()
в onvalidate()
с if(FindObjectOfType<CountingPassthrough>().gameObject.scene == gameObject.scene)
, чтобы предотвратить захват неверного ресурса из других загруженных сцен. И опять же, если вам это нужно для переноса данных между сценами, тогда имейте CountingPassthrough
наследование от ScriptableObject
вместо MonoBehaviour
, создайте новый ScriptableObject
в папке вашего проекта где-нибудь и игнорируйте этот дополнительный параметр if, чтобы ограничить совпадение сцен , Затем просто убедитесь, что вы используете функцию, чтобы найти ее, которая включает активы, если вы используете межсценовый подход ScriptableObject
.
РЕДАКТИРОВАТЬ: Забытые вложенные префабы edgecase в версиях Unity 2018+. Вам нужно добавить это в аккаунт: && FindObjectOfType<CountingPassthrough>() != new UnityEngine.SceneManagement.Scene()
Я обновил фрагмент кода выше. Извините за это.