Mail.ruПочтаМой МирОдноклассникиВКонтактеИгрыЗнакомстваНовостиКалендарьОблакоЗаметкиВсе проекты

Timer в c# unity сошёл с ума...

igor mavrin Ученик (188), открыт 4 недели назад
У меня есть простенький проект, в котором обязательно должен быть таймер, принцип работы таймера:
1. TMP label - туда записывается время
  1. DateTime - дата, которая будет записана в TMP label
  2. Сам таймер
  3. Моя цель сделать так, чтобы каждую секунду в DateTime добавлялся один час и это было отражено на label
  4. Код программы:
 using System.Collections;
using System.Collections.Generic;
using System;
using System.Timers;
using UnityEngine;
using Unity.UI;
using TMPro;

public class TimeDisplayer : MonoBehaviour
{
public DateTime worldTime = new DateTime(2025, 1, 1, 1, 0, 0);
public TMP_Text timeDisplayer;
private Timer timer = new Timer();
private void InitializeTimer()
{
timer.Interval = 1000;
timer.Enabled = true;
timer.Elapsed += new ElapsedEventHandler(addHour);
}

void Start()
{
InitializeTimer();
}

public void addHour(object sender, ElapsedEventArgs e)
{
worldTime = worldTime.AddHours(1);
timeDisplayer.text = worldTime.ToShortTimeString() + " " + worldTime.ToLongDateString();
print("Смена часа");
}
}
Проблема заключается в том, что текст label меняется в инспекторе, но не на экране, сообщение "смена часа" выводится ПОСЛЕ ЗАВЕРШЕНИЯ РАБОТЫ ПРИЛОЖЕНИЯ (это я вообще не представляю как)
4 ответа
Арнольд Рванула Профи (672) 4 недели назад
Возможно какие то событие перекрывает метод
Марк Молчанов Знаток (270) 4 недели назад
вместо таймеров из стандартной библиотеки лучше использовать корутины. Они завершают работу после уничтожения monobehavior'а или gameobject
S.H.I. Оракул (71328) 4 недели назад
 using System.Collections; 
using UnityEngine;
using TMPro;
using System;

public class TimeDisplayer : MonoBehaviour
{
public DateTime worldTime = new DateTime(2025, 1, 1, 1, 0, 0);
public TMP_Text timeDisplayer;

void Start()
{
StartCoroutine(AddHourCoroutine());
}

IEnumerator AddHourCoroutine()
{
while (true)
{
yield return new WaitForSeconds(1f); // Ждём 1 секунду
AddHour();
}
}

void AddHour()
{
worldTime = worldTime.AddHours(1);
// Обновляем текст в основном потоке
timeDisplayer.text = $"{worldTime:HH:mm} {worldTime:dd MMMM yyyy}";
Debug.Log("Смена часа");
}
}
dmilor Мастер (2443) 4 недели назад
Проблема в вашем коде заключается в использовании System.Timers.Timer и то, как он взаимодействует с Unity. Таймер System.Timers.Timer работает на отдельном потоке, который не синхронизирован с главным потоком Unity (главным игровым потоком, где происходят все изменения UI и логики в Unity). Unity, однако, требует, чтобы вы обновляли UI только из главного потока. В результате происходит следующее:

Вы обновляете timeDisplayer.text из другого потока, что Unity просто игнорирует или не отображает (зависит от версии Unity, но в вашем случае изменения не видны).
Debug.Log() (или print()) также некорректно используется из другого потока, поэтому сообщения логируются после завершения приложения (потому что поток может завершаться только при завершении приложения).
Решение проблемы — использовать что-то, поддерживающее основной поток Unity. Рекомендуется заменить System.Timers.Timer на Coroutine или использовать InvokeRepeating().
 using System; 
using UnityEngine;
using TMPro;

public class TimeDisplayer : MonoBehaviour
{
public DateTime worldTime = new DateTime(2025, 1, 1, 1, 0, 0);
public TMP_Text timeDisplayer;

void Start()
{
// Запускаем корутину
StartCoroutine(UpdateWorldTime());
}

private System.Collections.IEnumerator UpdateWorldTime()
{
while (true) // Бесконечно продолжаем обновлять таймер
{
// Добавляем 1 час
worldTime = worldTime.AddHours(1);

// Обновляем текст метки
timeDisplayer.text = worldTime.ToShortTimeString() + " " + worldTime.ToLongDateString();

// Выводим в консоль
Debug.Log("Смена часа");

// Ждем 1 секунду
yield return new WaitForSeconds(1f);
}
}
}
Другой подход — использовать встроенный метод InvokeRepeating.
 using System; 
using UnityEngine;
using TMPro;

public class TimeDisplayer : MonoBehaviour
{
public DateTime worldTime = new DateTime(2025, 1, 1, 1, 0, 0);
public TMP_Text timeDisplayer;

void Start()
{
// Запускаем метод "addHour" каждую секунду
InvokeRepeating("AddHour", 0f, 1f);
}

private void AddHour()
{
// Добавляем 1 час
worldTime = worldTime.AddHours(1);

// Обновляем текст метки
timeDisplayer.text = worldTime.ToShortTimeString() + " " + worldTime.ToLongDateString();

// Выводим в консоль
Debug.Log("Смена часа");
}
}
Ваш изначальный подход использовал неподходящий таймер для работы в Unity. Unity не поддерживает обновление UI и вызовы таких методов, как Debug.Log(), из других потоков. Использование либо корутин, либо InvokeRepeating позволяет работать с таймером в основном игровом потоке, правильно обновляя UI.
Похожие вопросы