Гайд по GauntletUI (создание кнопок и интерфейса)
Оригинальная статья - https://forums.taleworlds.com/index.php?threads/epic-gauntletui-tutorial-custom-buttons-and-uis.419682/
Этот гайд научит вас множеству вещей, включая:
- Как добавить кнопку в MapBar;
- Как взаимодействовать с объектами INavigationHandler для плавного нажатия и всплывающих экранов с использованием объектов GameState;
- Как создавать собственные экраны (Screens) и модели просмотра (ViewModel).
Подоплека
Есть гораздо более простые методы продвижения экранов. Однако работа с игровыми объектами так, как задумано создателями, гарантирует, что ваш мод может масштабироваться без ошибок. Более того, как только вы освоите это, вы сможете полностью управлять объектами GauntletUI!
Необходимые знания
Прежде чем мы начнем, важно, чтобы вы знали, как делать эти вещи:
- Установить .NET (C#) в Rider или в Visual Studio;
- Создайте базовые префаб-объекты (Prefab-Objects) (шаблоны (Templates) будут предоставлены);
- Как установить NuGet-Packages в соответствующей Интегрированной среде разработки (Integrated Development Environment - IDE);
- Установка SubModules.
Если вам нужна помощь в любой из этих областей, воспользуйтесь этими ссылками:
- (C#Environment) https://docs.bannerlordmodding.com/_tutorials/basic-csharp-mod.html
- (SubModule) https://docs.bannerlordmodding.com/_xmldocs/submodule.html#important
- (Prefabs) https://docs.bannerlordmodding.com/_gauntlet/movie.html
- (Folder Structure) https://docs.bannerlordmodding.com/_intro/folderstructure.html#folder-descriptions-file-examples
Установка
1. Создайте базовый мод - SubModule (при необходимости обратитесь к гайду) и назовите его ExampleUIMod
2. Установите UIExtenderLib, следуя инструкциям на сайте.
Часть 1: Добавление кнопки в MapBar.
MapBar - это элемент интерфейса, который вы видите все время. Это панель с кнопками "Инвентарь", "Клан", "Королевство" и другие. Так что это отличное место, чтобы легко добавить свою собственную кнопку.
Прежде чем мы начнем, рекомендуется использовать мой шаблон ExampleButton. Создайте в этом месте файл с названием ExampleButton.xml. Вам потребуется создать этот каталог:
YourModule/GUI/PrefabExtensions/
Код шаблона (Template Code):
Теперь скопируйте этот код шаблона и вставьте его в файл XML:
<Widget UpdateChildrenStates="true" WidthSizePolicy="Fixed"
SuggestedHeight="!LeftBar.Button6.Height" VerticalAlignment="Bottom"
HorizontalAlignment="Right" HeightSizePolicy="Fixed"
SuggestedWidth="!LeftBar.Button6.Width">
<Children>
<ButtonIconOffsetWidget UpdateChildrenStates="true"
WidthSizePolicy="StretchToParent" HeightSizePolicy="StretchToParent" Id="w"
PressedXOffset="!MapBar.ButtonIcon.XOffset"
PressedYOffset="!MapBar.ButtonIcon.YOffset" HoveredCursorState="RightClickLink"
Brush="MapBar.Left.Button6" DoNotPassEventsToChildren="true"
Command.Click="ExecuteOpenScene" ButtonIcon="ClanIcon">
<Children>
<Widget WidthSizePolicy="Fixed" SuggestedHeight="!LeftBar.Icon7.Height"
VerticalAlignment="Center" HorizontalAlignment="Center" HeightSizePolicy="Fixed"
SuggestedWidth="!LeftBar.Icon7.Width" Id="ClanIcon" Brush="MapBar.Left.Icon7"/>
</Children>
</ButtonIconOffsetWidget>
</Children>
</Widget>
Настройка префаб-патча (Prefab Patch):
1. В вашем C# Bannerlord Mod Project создайте папку с названием MapBar. Она будет содержать все элементы, связанные с его переопределением.
2. Убедитесь, что вы установили UIExtenderLib, создали новый файл Класса и назовите его PrefabExtension внутри папки, используя следующий код:
using UIExtenderLib.Interface;
[UIExtenderLib.Interface.PrefabExtension("MapBar",
"/Prefab/Window/Widget/Children/Widget/Children/ListPanel/Children/Widget")]
public class PrefabExtension : PrefabExtensionInsertAsSiblingPatch
{
public override string Name => "ExampleButton";
public override InsertType Type => InsertType.Append;
}
3. Позвольте мне объяснить, что происходит:
- XML файл MapBar - это файл, который нужно переопределить.
- Местоположение XPath (ниже показано, где будет выполняться операция исправления (patching operation). [Подробнее о XPath здесь])
"/Prefab/Window/Widget/Children/Widget/Children/ListPanel/Children/Widget"
- Унаследованный класс PrefabExtensionInsertAsSiblingPatch определяет, какой тип исправления будет выполняться в указанном XPath.
- Name - это название XML файла, который патчер добавит/заменит.
- InsertType - это тип вставки, которую выполняет патчер.
- Чтобы узнать больше о доступных вам вариантах, обратитесь к документации здесь.
Настройка ViewModel
Теперь, когда мы добавили кнопку с помощью патчера, нам нужно реализовать пользовательское действие, которое будет выполняться при нажатии на нее. Для этого мы также используем UIExtenderLib.
4. Создайте файл класса с названием CustomMapVM и унаследуйте класс BaseViewModelMixin<MapNavigationVM>.
5. Теперь скопируйте этот код, чтобы продолжить.
[ViewModelMixin]
public class CustomMapVM : BaseViewModelMixin<MapNavigationVM>
{
public CustomMapVM(MapNavigationVM vm) : base(vm)
{ }
// Add Your MapBar Button Methods Here
[DataSourceMethod]
public void ExecuteOpenScene()
{ }
}
Этот код вполне приемлем, если все, что вы хотите сделать, это выполнить чит-код или загрузить сцену (бесполезный плагин), но я уверен, что вы хотите, чтобы он был связан с другим пользовательским интерфейсом (UI).
Часть 2: Настройка элементов навигации.
Чтобы ваша кнопка открывала новую сцену, важно реализовать элементы навигации (Navigation Elements), которые использует игра, чтобы это происходило плавно и с возможностью масштабирования. Прежде чем мы начнем, я дам вам краткий обзор для того, как MapNavigationVM загружает экраны (Screens):
1. GameStateManager выполняет команду, которая перемещает объект GameState в Queue (очередь) из GameStates.
2. Затем задания удаляются из очереди (Dequeued) и выполняются действия, связанные с JobTypes.
3. В случае новой сцены это в конечном итоге приводит к срабатыванию метода CreateScreen(), вызываемого GameStateScreenManager Open, который устанавливает экран, связанный с GameState, в качестве объекта IGameStateListener.
4. После этого метод OnInitialize() вызывается компонентом экрана IGameStateListener.
5. Затем вызывается метод экрана OnActivate() и загружается фильм (movie) (другое название графического интерфейса пользователя GUI, который вы видите на экране).
Хотя кажется, что шагов много, на самом деле нам нужно всего несколько вещей, чтобы это сработало! Начнем с создания CustomStateHandler.
Создание CustomStateHandler:
1. Очень просто! Просто создайте новый файл интерфейса под названием CustomStateHandler в папке NavigationElements и скопируйте следующий код. Вот и всё!
public interface CustomStateHandler
{
}
Создание собственного GameState:
1. Создайте класс в папке NavigationElements под названием CustomState.
2. Скопируйте код дословно.
public class CustomState : GameState
{
private CustomStateHandler _handler;
public override bool IsMenuState
{
get { return true; }
}
public CustomStateHandler Handler
{
get { return this._handler; }
set { this._handler = value; }
}
}
Создание CustomNavigationHandler:
1. Создайте новый файл интерфейса в папке NavigationElements под названием CustomNavigationHandler и унаследуйте INavigationHandler. Вставьте любой метод, который вы хотите выполнять Scene Opening events (события открытия сцены) следующим образом:
public interface CustomNavigationHandler : INavigationHandler
{
void ExampleOpenScene();
}
Расширение MapNavigationHandler:
1. Теперь мы хотим расширить обработчик MapNavigation, MapNavigationHandler и ваш CustomNavigationHandler.
public class CustomNavigation : MapNavigationHandler,
CustomNavigationHandler
{
}
2. Теперь создайте частный атрибут, назовите его _game и установите его с помощью конструктора.
private Game _game;
public CustomNavigation()
{
this._game = Game.Current;
}
3. Наконец, вы хотите соединить свои методы, указанные в CustomNavigationHandler, с методами PushState. Важно отметить, что CustomState зависит от имени созданного вами класса GameState.
void CustomNavigationHandler.ExampleOpenScene(){
this._game.GameStateManager.PushState(this._game.GameStateManager.CreateState<Cu
stomState>(), 0);
}
Теперь, когда у нас есть навигация, все, что нам нужно сделать, это настроить наш новый UIElement!
Часть 3: Настройка ваших Screen и View Model.
Создание собственного ViewModel:
1. Создайте папку под названием UIElements. Она будет содержать все ваши экраны и модели просмотра.
2. Теперь создайте новый классовый файл и назовите его CustomUI. Он будет содержать как ваш Экран, так и ViewModel.
3. В этом файле создайте класс ViewModel и создайте метод закрытия экрана:
public class CustomVM : ViewModel {
[DataSourceMethod]
private void CloseCustomScreen(){
Game.Current.GameStateManager.PopState(0);
}
}
4. На этом пока все.
Создание собственного экрана:
1. А теперь то, чего вы все ждали: экраны! В том же файле создайте новый класс CustomScreen, который унаследует ScreenBase и IGameStateListener.
public class CustomScreen : ScreenBase, IGameStateListener{}
2. Затем мы свяжем наш CustomState с этим файлом, выполнив следующие действия.
[GameStateScreen(typeof(CustomState))]
public class CustomScreen : ScreenBase, IGameStateListener {}
3. Теперь мы приступим к настройке наших объектов GauntletLayer, DataSource и GameState. Напоминание: это будет внутри класса (скобки {}).
private GauntletLayer _gauntletLayer;
private readonly CustomState _customState
private CustomVM _dataSOurce;
4. Дальше мы настроим наш CustomState с помощью конструктора.
public CustomScreen(CustomState customState){
this._customState = customState;
this._customState.Listener = (IGameStateListener) this;
}
5. Теперь мы настроим методы, необходимые для IGameStateListener.
void IGameStateListener.OnActivate(){}
void IGameStateListener.OnDeactivate(){}
void IGameStateListener.OnInitialize(){}
void IGameStateListener.OnFinalize(){}
6. Настроим метод OnActivate(). Вам нужно заменить «YourPrefabXML» на выбранный вами префаб. Если вы просто хотите посмотреть, работает ли это, вы можете использовать «ClanScreen».
base.OnActivate();
this._gauntletLayer = new GauntletLayer(1,"GauntletLayer");
this._gauntletLayer.InputRestrictions.SetInputRestrictions(true,InputUsageMask.A
ll);
this._gauntletLayer.Input.RegisterHotKeyCategory(HotKeyManager.GetCategory("Gene
ricCampaignPanelsGameKeyCategory"));
this._gauntletLayer.IsFocusLayer = true;
ScreenManager.TrySetFocus((ScreenLayer) this._gauntletLayer);
this.AddLayer((ScreenLayer) this._gauntletLayer);
this._dataSource = new CustomVM();
this._gauntletLayer.LoadMovie("YourPrefabXML", this._dataSource);
7. Теперь настроим OnDeactivate()
this.OnDeactivate();
this.RemoveLayer((ScreenLayer) this._gauntletLayer);
this._gauntletLayer.IsFocusLayer = false;
ScreenManager.TryLoseFocus((ScreenLayer) this._gauntletLayer);
8. OnInitialize() можно оставить пустым. Теперь давайте настроим OnFinalize()
this._dataSource = (CustomVM) null;
this._gauntletLayer = (GauntletLayer) null;
Отлично! Теперь мы можем это проверить!
Часть 4: Настройка и запуск SubModule
1. Перейдите в свой файл Main.cs. Здесь вы установите свой SubModule, унаследовав MBSubModuleBase().
2. Настройте UIExtenderLib следующим образом:
public class SubModule : MBSubModuleBase
{
private UIExtender _extender;
protected override void OnSubModuleLoad()
{
base.OnSubModuleLoad();
_extender = new UIExtender("ExampleUIMod");
_extender.Register();
}
protected override void OnBeforeInitialModuleScreenSetAsRoot()
{
base.OnBeforeInitialModuleScreenSetAsRoot();
_extender.Verify();
}
3. Создайте свой .dll-файл и запустите!
По всем вопросам обращайтесь к автору гайда в Discord: Panopticon#3014.
Комментариев 3
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.