Счёт, интерфейс и повторный запуск
CanvasLayer с якорями, локализованные подписи и однократное начисление очков за попадание сверху.
Иерархия
Создайте Prefabs/Hud.rprefab с корнем CanvasLayer по имени Hud, дочерним Control по имени Root и его детьми ScoreLabel, MessageLabel, StartButton, InstructionsLabel. Типы — Label, Label, Button, Label.
У Hud оставьте Layer = 1. У Root — Full Rect и Mouse Filter = Ignore. У подписей также Ignore, у кнопки стандартный Stop. Якоря определяют точку привязки, смещения задают прямоугольник относительно неё:
| Объект | Якоря Left, Top, Right, Bottom | Смещения Left, Top, Right, Bottom |
|---|---|---|
| ScoreLabel | 0, 0, 0, 0 | 24, 16, 280, 52 |
| MessageLabel | 0.5, 0.5, 0.5, 0.5 | −300, −90, 300, −30 |
| StartButton | 0.5, 0.5, 0.5, 0.5 | −100, 20, 100, 68 |
| InstructionsLabel | 0, 1, 1, 1 | 0, −48, 0, −16 |
Для MessageLabel и InstructionsLabel выберите выравнивание текста по центру. ScoreLabel и MessageLabel используют размер шрифта 24. При окне 960 × 640 кнопка занимает (380, 340)…(580, 388).
Код интерфейса
Прикрепите к Hud полный Scripts/SquashHud.cs:
Полный SquashHud.cs
using Reckfell;
public partial class SquashHud : CanvasLayer
{
[Signal] public delegate void StartRequestedEventHandler();
private Label _score = null!;
private Label _message = null!;
private Label _instructions = null!;
private Button _button = null!;
public override void _EnterTree()
{
_score = GetEntity<Label>("Root/ScoreLabel");
_message = GetEntity<Label>("Root/MessageLabel");
_instructions = GetEntity<Label>("Root/InstructionsLabel");
_button = GetEntity<Button>("Root/StartButton");
_button.Pressed += OnPressed;
_instructions.Text = L("WASD / стрелки · Space — прыжок", "WASD / arrows · Space to jump");
}
public override void _ExitTree()
{
if (ReckfellObject.IsInstanceValid(_button))
_button.Pressed -= OnPressed;
}
public void SetScore(int score) => _score.Text = L("Счёт: ", "Score: ") + score;
public void ShowReady() => ShowMessage(L("Прыгайте на противников сверху", "Jump onto hazards from above"), L("Начать", "Start"));
public void ShowGameOver() => ShowMessage(L("Игра окончена", "Game over"), L("Ещё раз", "Restart"));
public void ShowPlaying()
{
_message.Hide();
_button.Hide();
}
public void ShowSetupError()
{
ShowMessage(L("Проверьте Prefabs/Mob.rprefab", "Check Prefabs/Mob.rprefab"), "");
_button.Disabled = true;
}
private void ShowMessage(string message, string buttonText)
{
_message.Text = message;
_message.Show();
_button.Text = buttonText;
_button.Show();
}
private void OnPressed() => EmitSignal(SignalName.StartRequested);
private static string L(string ru, string en) => TranslationServer.GetLocale().StartsWith("ru") ? ru : en;
}Русские подписи выбираются, если TranslationServer.GetLocale() начинается с ru; иначе используются английские. Здесь язык определяется при создании и обновлении текста. Отдельного игрового меню смены языка в прототипе нет.
Если видимая кнопка не нажимается, проверьте её Mouse Filter = Stop и декоративные Control поверх неё: им нужен Ignore. Корневой Control с Ignore не отключает ввод дочерней кнопки.
Кто меняет счёт
SquashGame подписывается на Squashed каждого созданного противника. Успешное событие при активном раунде увеличивает Score на 1. Цепочка обновления: Squashed → SquashGame.Score → Hud.SetScore(). HUD только отображает это значение. Таймер появления не начисляет очки за время.
StartRequested — запрос начала от кнопки. Контроллер сбрасывает счёт и персонажа, удаляет старых противников и скрывает кнопку. После поражения финальное значение остаётся видно; повторный запуск начинает с 0. Это состояние в памяти, без записи рекорда на диск.
Кнопка соединяется в коде один раз; второе подключение через редактор не нужно. Проверяйте несколько повторных раундов: одно попадание сверху должно давать одно очко и после перезапуска. Полный контроллер.