Merge branch 'world-loading-scene' into main

Add a loading screen (WorldLoadingScene) between world setup/load and WorldScene so
the blocking world generation shows a "Generating world" indicator instead of a
blank fade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-06-14 23:02:19 +03:00
co-authored by Claude Opus 4.8
5 changed files with 67 additions and 2 deletions
+1
View File
@@ -92,6 +92,7 @@
"menu.settings": "Settings", "menu.settings": "Settings",
"menu.credits": "Credits", "menu.credits": "Credits",
"menu.quit": "Quit", "menu.quit": "Quit",
"loading.world": "Generating world",
"newworld.title": "New World", "newworld.title": "New World",
"newworld.name": "Name", "newworld.name": "Name",
"newworld.defaultname": "New World", "newworld.defaultname": "New World",
+1
View File
@@ -92,6 +92,7 @@
"menu.settings": "Настройки", "menu.settings": "Настройки",
"menu.credits": "Авторы", "menu.credits": "Авторы",
"menu.quit": "Выход", "menu.quit": "Выход",
"loading.world": "Создание мира",
"newworld.title": "Новый мир", "newworld.title": "Новый мир",
"newworld.name": "Название", "newworld.name": "Название",
"newworld.defaultname": "Новый мир", "newworld.defaultname": "Новый мир",
+1 -1
View File
@@ -73,7 +73,7 @@ public sealed class LoadGameScene : Scene
if (!Context.Scenes.IsTransitioning) if (!Context.Scenes.IsTransitioning)
{ {
Context.Scenes.Switch( Context.Scenes.Switch(
new WorldScene(save.ToConfig(), save), new WorldLoadingScene(save.ToConfig(), save),
Transitions.Fade(0.6f) Transitions.Fade(0.6f)
); );
} }
+1 -1
View File
@@ -154,7 +154,7 @@ public sealed class NewWorldScene : Scene
Seed = seed, Seed = seed,
SmoothPasses = _smoothing, SmoothPasses = _smoothing,
}; };
Context.Scenes.Switch(new WorldScene(config), Transitions.Fade(0.6f)); Context.Scenes.Switch(new WorldLoadingScene(config), Transitions.Fade(0.6f));
} }
private void Back() private void Back()
+63
View File
@@ -0,0 +1,63 @@
using LittleSim.App;
using LittleSim.Content;
using LittleSim.UI;
using MrGameEng.Core;
using MrGameEng.UI;
using Myra.Graphics2D.UI;
namespace LittleSim.Scenes;
/// <summary>
/// Экран загрузки между окном создания/выбора мира и игровой сценой. Генерация мира
/// (<see cref="WorldScene.OnLoad"/>: рельеф, атласы, спавн) идёт синхронно и блокирует кадр; раньше под
/// ней был лишь сплошной фейд без индикатора. Эта сцена рисует надпись «Создание мира…», даёт ей
/// прорисоваться, затем переключается на <see cref="WorldScene"/> БЕЗ перехода — её последний кадр
/// (надпись) и остаётся на экране, пока идёт генерация, давая игроку понятную обратную связь.
/// </summary>
public sealed class WorldLoadingScene : Scene
{
private readonly WorldConfig _config;
private readonly WorldSave? _save;
private GameSettings _settings = new();
private Label _label = null!;
private int _frames;
/// <summary>Новый мир (<paramref name="save"/> = null) или загрузка сохранения.</summary>
public WorldLoadingScene(WorldConfig config, WorldSave? save = null)
{
_config = config;
_save = save;
}
protected override void OnLoad()
{
_settings = GameSettingsStore.Load();
var desktop = this.UseScaledUI(); // ставит MyraEnvironment.Game до создания виджетов
_label = new Label
{
TextColor = Ui.Accent,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
};
desktop.Root = Ui.Screen(_label);
UpdateSystems.Add(new CallbackSystem(Tick));
}
private void Tick()
{
var content = Context.Services.Get<GameContent>();
var word = content.Languages.Get("loading.world");
var dots = new string('.', (int)(Context.Clock.UnscaledTotalTime * 2) % 4);
_label.Text = word + dots;
// Дождаться, пока надпись точно прорисована (переход въезда завершён + пара кадров), затем
// мгновенно переключиться: блокирующий WorldScene.OnLoad оставит на экране кадр с надписью.
_frames++;
if (Context.Scenes.IsTransitioning || _frames < 2)
{
return;
}
Context.Scenes.Switch(new WorldScene(_config, _save));
}
}