Unity: Почему не отображается плавающий текст урона/лечения?
Здравствуйте!
Я создаю 2D игру на Unity для мобильных устройств (в пиксель-арте), и пытаюсь реализовать всплывающий текст урона и лечения — например, при получении урона появляется “-1”, при восстановлении “+1”, при полном исцелении — “Full”.
Использую TextMeshProUGUI внутри префаба, который появляется через Canvas. Позицию текста определяю через Camera.main.WorldToScreenPoint(...), чтобы он появлялся над персонажем.
Но вот в чём проблема:
1. Текст появляется на сцене, но не отображается в игре.
2. Если посмотреть в режиме Scene — текст улетает на координаты вроде X = 8000+.
3. Иногда вообще появляется без цвета или шрифта.
4. Canvas существует, создан отдельно (в Screen Space - Overlay).
5. Префаб корректно назначен и спавнится при получении урона.
Я уже пробовал:
• Использовать World Space / Overlay
• Привязку к пустому textSpawnPoint на голове персонажа
• Задержку и Destroy через скрипт
• Разные камеры и Canvas’ы
Вопрос:
Что может быть причиной такой ошибки? Как правильно отобразить TextMeshProUGUI, чтобы он появлялся строго над игроком в нужной точке экрана?
Буду рад любой помощи. С меня плюсик!
Ответ от LLM O4-mini: Привет! Часто такая «уплывающая» цифра урона — это просто неверное позиционирование UI-элемента: ты инстанцируешь TextMeshProUGUI-префаб без привязки к Canvas и тупо пишешь в world-координатах значение экранных пикселей. В результате RectTransform берет эти «пиксели» как мировые юниты и уходит на несколько тысяч.
Как это обычно делается правильно:
1) Один единственный Canvas в режиме Screen Space – Overlay (или Screen Space – Camera)
2) Пре-фаб всплывающего текста имеет TextMeshProUGUI + RectTransform
3) При спавне ты сразу делаешь его дочерним к Canvas (с false-сбросом локальных трансформ), а позицию задаёшь уже в UI-координатах
Ниже пример кода (C#) для Screen Space – Overlay:
using UnityEngine;
using TMPro;
public class FloatingTextSpawner : MonoBehaviour
{
[SerializeField] private RectTransform canvasRect; // Ссылка на RectTransform Canvas
[SerializeField] private GameObject textPrefab; // Префаб с TextMeshProUGUI
public void Spawn(Vector3 worldPosition, string message)
{
// 1) Конвертируем world→screen
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(Camera.main, worldPosition);
// 2) Создаём UI-объект сразу под Canvas
GameObject go = Instantiate(textPrefab, canvasRect);
go.transform.localScale = Vector3.one; // сброс масштабов
// 3) Переводим screenPoint в локальные координаты Canvas
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvasRect, screenPoint, null, out Vector2 localPoint);
// 4) Устанавливаем на местоположение
RectTransform rt = go.GetComponent<RectTransform>();
rt.anchoredPosition = localPoint;
// 5) Пишем текст
TextMeshProUGUI tmp = go.GetComponent<TextMeshProUGUI>();
tmp.text = message;
// 6) (Опционально) старт анимации и уничтожение
Destroy(go, 1.0f);
}
}
Разбор по пунктам:
- Canvas в режиме Screen Space – Overlay не требует камеры в `ScreenPointToLocalPointInRectangle` (ставим `null`).
- `Instantiate(textPrefab, canvasRect)` сразу делает префаб дочерним по-умолчанию и сбрасывает мировую позицию → ты не получишь этих 8000+ юнитов.
- `anchoredPosition` — правильный способ сместить элемент внутри Canvas.
- Убедись, что `textPrefab` имеет `RectTransform` и компонент `TextMeshProUGUI`, и что шрифт (Font Asset) действительно включён в билд.
Если ты используешь Screen Space – Camera:
- В Canvas ставишь Render Mode = Screen Space – Camera и в поле «Render Camera» указываешь свою камеру.
- При вызове `ScreenPointToLocalPointInRectangle` вместо `null` передаёшь эту камеру.
Ещё пара советов:
• Проверь CanvasScaler: на каком разрешении ты тестишь и какое в билде (от этого тоже зависят UI-координаты).
• Убедись, что у префаба локальные Scale = (1,1,1), Rotation = (0,0,0).
• Если текст «без шрифта» — вероятно, Font Asset не попал в билд. Принудительно засвети его в Resources или в настройках Addressables/AssetBundle.
Вариант «в мире» (World Space Canvas):
- Меняешь Canvas на World Space, прикрепляешь его к персонажу (или в корень сцены),
- Тогда при инстансе просто пишешь `go.transform.position = worldPosition + Vector3.up * offset;`
- Но, IMHO, проще иметь один Overlay-Canvas и рисовать цифры через конвертацию world→screen.
Надеюсь, это поможет. Если что-то остальное не работает, напиши, посмотрим лог ошибок или конкретные настройки Canvas/камер. Удачи! 🚀