Горячая перезагрузка и редактор в игре
В этом руководстве настраивается цикл правки интерфейса игры: меняйте разметку страницы в текстовом редакторе или в
редакторе поверх запущенной игры и смотрите результат без перезапуска. Используется пакет IcyUI.Design — инструмент
разработки; в выпущенной игре его нет.
Вам понадобится игра, загружающая страницу из разметки (Первый экран). Страница Редактирование разметки и инструменты дизайна описывает, что это за инструменты; здесь они подключаются и показано, как с ними работать.
1. Подключайте пакет только в Debug-сборках
Добавьте пакет и сделайте ссылку условной, чтобы релизная сборка его не несла:
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="IcyUI.Design" Version="0.1.0-alpha.1" />
</ItemGroup>
<!-- The code that uses it lives in a DevTools folder, compiled in Debug only. -->
<ItemGroup Condition="'$(Configuration)' != 'Debug'">
<Compile Remove="DevTools\**" />
</ItemGroup>
Страницы загружаются из вывода сборки (bin/Debug/.../Menu.xml) — копии файла из вашего проекта. Правки в копии пропали
бы при следующей сборке, поэтому инструментам нужно знать, где лежат файлы вашего проекта.
2. Подключите инструменты
Поместите этот класс в папку DevTools. Он подключает сеанс дизайна, указывает ему ваши исходные файлы, следит за ними и
добавляет редактор:
/// <summary>The game's development tools: page editing in a text editor and in the game. Create it before the first screen loads.</summary>
public sealed class DevTools : IDisposable
{
private readonly IDisposable watch;
private readonly EditorOverlay overlay;
public DevTools(IIcyHost ui, string? sourceRoot = null)
{
// The session sees only the pages loaded after this line.
Design = DesignSession.Attach(ui.Configuration);
// Pages load from the build output; Save and the file watcher use your project's copy.
sourceRoot ??= DesignSession.FindSourceRoot("MyGame.csproj", "Assets/UI");
if (sourceRoot != null)
Design.UseSourceRoot(sourceRoot);
watch = Design.WatchSourceFiles(); // a file saved in your text editor shows in the game
overlay = EditorOverlay.Attach(ui.MainCanvas, Design); // F4 shows and hides the in-game editor
}
public DesignSession Design { get; }
public void Dispose()
{
overlay.Dispose();
watch.Dispose();
Design.Dispose();
}
}
- Подключайте до загрузки первой страницы. Сеанс отслеживает только страницы, загруженные после подключения.
UseSourceRoot— то, что заставляет работать сохранение и слежение.FindSourceRootподнимается от папки вывода игры к папке сMyGame.csprojи возвращаетAssets/UIвнутри неё — там лежит разметка. Если ничего не найдено, сохранение и слежение используют копию в выводе сборки, которую перезапишет следующая сборка.WatchSourceFilesвключается явно. Ни за чем не следят, пока вы его не вызовете.
3. Правьте в текстовом редакторе
При запущенной игре сохраните файл .xml в проекте. Через доли секунды живая страница меняется: Width кнопки, текст,
цвет, добавленный элемент. За кадром инструменты читают файл и применяют его к странице как одну правку.
- Это работает и для редакторов, которые сохраняют, записывая временный файл и переименовывая его.
- Несколько быстрых сохранений подряд применяются один раз.
- Файл, текст которого совпадает с тем, что уже есть в игре, игнорируется. Поэтому сохранение из редактора в игре (шаг 4) не перезагружает само себя.
Не всякая правка может изменить живую страницу. Правка внутри Style, ControlTemplate или элемента-свойства
(<Button.Content>) меняет документ, но не уже показанные элементы; документ сообщает NeedsReload. Загрузите страницу
заново, чтобы увидеть её. ReloadablePage делает это по F5 из последнего текста документа:
/// <summary>
/// A page that F5 loads again from its document's latest text. Most edits show on the live page at once; an edit inside a
/// style or a template doesn't, and this shows it.
/// </summary>
public sealed class ReloadablePage
{
private readonly IIcyHost ui;
private readonly DesignSession design;
private readonly string path;
private readonly object dataContext;
private UIElement page;
public ReloadablePage(IIcyHost ui, DesignSession design, string path, object dataContext)
{
this.ui = ui;
this.design = design;
this.path = path;
this.dataContext = dataContext;
page = ui.MainCanvas.LoadMarkup(path, dataContext);
ui.Configuration.Input.Events.RegisterCommand(new RelayCommand(Reload), new KeyGesture(Keys.F5));
}
public UIElement Page => page;
public void Reload()
{
// The document holds the latest text, from the file or from the editor. LoadMarkup(path) would read the build
// output's older copy.
DesignDocument document = design.FindDocument(page, out _) ?? throw new InvalidOperationException("The page isn't tracked.");
UIElement fresh = new MarkupLoader(ui.Configuration).Load(document.Text, path);
fresh.DataContext = dataContext;
ui.MainCanvas.Remove(page);
ui.MainCanvas.Add(fresh);
page = fresh;
}
}
Он строит страницу из текста документа, а не через LoadMarkup, потому что LoadMarkup прочитал бы более старую копию в
выводе сборки.
Теперь запустите игру с инструментами:
using Icy.Stride.Configuration;
using Icy.UI.Controls;
using Stride.Engine;
public class MenuStartup : StartupScript
{
#if DEBUG
private DevTools? tools;
#endif
public override void Start()
{
StrideIcyHost ui = Game.UseIcyUI();
var viewModel = new MenuViewModel();
#if DEBUG
tools = new DevTools(ui);
_ = new ReloadablePage(ui, tools.Design, "Menu.xml", viewModel);
#else
ui.MainCanvas.LoadMarkup("Menu.xml", viewModel);
#endif
}
#if DEBUG
public override void Cancel() => tools?.Dispose();
#endif
}
В Release-сборке ветка #else загружает страницу обычным способом, а инструменты не компилируются вовсе.
4. Правьте в игре
Нажмите F4, чтобы показать редактор поверх запущенной игры. Щёлкните элемент, чтобы выбрать его, меняйте свойства на панели справа, перетаскивайте его или добавляйте элементы из набора инструментов. Ctrl+Shift+E переключает режим правки (щелчок выбирает) и режим взаимодействия (интерфейс работает как в игре, так что можно открыть меню и править показанное в нём). Ctrl+Z и Ctrl+Y отменяют и возвращают.
Save на панели команд записывает текст документа в файл вашего проекта с сохранением форматирования и комментариев. Слежение за файлами видит запись, обнаруживает, что текст уже совпадает с документом, и ничего не делает.
5. Меню отладки вместо оверлея
Если оверлей мешает экрану, тот же редактор — это элемент управления. Поместите EditorWorkspace на страницу меню отладки вместе с сеансом дизайна из шага 2:
// The same editor as a page, for a debug menu.
public static void ShowEditorPage(Icy.UI.Controls.ContentControl debugPage, DesignSession design)
{
debugPage.Content = new EditorWorkspace { Design = design };
}
Canvas принимает один редактор за раз: пока показан оверлей, рабочая область ждёт, и наоборот.
Что вы построили
Запущенная игра, страницы которой следуют за вашим текстовым редактором, редактор поверх неё и F5 для правок, которые не могут изменить живую страницу, — и всё это только в Debug-сборках. В релизе от этого ничего нет.
Примечание
Править можно только страницы, загруженные из разметки после подключения сеанса. Элемент, созданный в коде, выбрать
нельзя, а правка стиля или шаблона требует перезагрузки по F5. NeedsReload остаётся установленным, если правка его
требовала, поэтому нажимайте F5 всякий раз, когда живая страница выглядит устаревшей.
Дальше
- Редактирование разметки и инструменты дизайна: части и команды правки подробно.
- Игровое меню: меню, на котором можно опробовать цикл.