Моддинг:GUI

From Vintage Story Wiki
This page is a translated version of the page Modding:GUIs and the translation is 100% complete.

This page was last verified for Vintage Story version 1.15.

Other languages:

Создание хороших графических пользовательских интерфейсов — непростая задача, иногда даже самые крупные компании допускают ошибки.

В Vintage Story в настоящее время вам нужно сформулировать свои графические интерфейсы в коде, что требует острого пространственного мышления. Надеюсь, когда-нибудь у нас появится конструктор графического интерфейса типа «укажи и щелкни», который снимет много работы. До тех пор, вот некоторые из предметов первой необходимости.

Protip:

 Используйте привязку клавиши отладки для «циклических режимов контура диалогового окна» при создании графического интерфейса. Он нарисует контуры вокруг границ вашего графического интерфейса, что значительно упростит понимание того, как ваш код переводится в визуальный графический интерфейс.

Начиная

Вы должны инкапсулировать каждый диалог GUI в свой собственный класс, хотя диалог может состоять из нескольких окон - например, диалоговое окно ванильного персонажа имеет два - одно для носимых игроков и одно для статистики игрока. Есть пара базовых классов, которые немного облегчат вам жизнь.

Графический интерфейс объекта блокировки

Чтобы создать графический интерфейс, привязанный к блочному объекту, наследуйте от GuiDialogBlockEntity. Затем в коде вашего блока вы можете создать и открыть этот графический интерфейс, например. при взаимодействии с игроком (пример: сущность блока жернова, master/Gui/GuiDialogBlockEntityQuern.cs Диалог блока жернова)

HUD

Чтобы создать элемент графического интерфейса, с которым нельзя взаимодействовать, наследуйте от HudElement.

Графический интерфейс общего назначения

Для любого другого использования наследуйте от класса общего назначения GuiDialog, от которого также наследуются HudElement и GuiDialogBlockEntity. Вы можете переопределить ToggleKeyCombinationCode на что-то вроде «yourAweseomeHotkeyCode» и использовать capi.Input.RegisterHotKey + capi.Input.SetHotKeyHandler, чтобы получить собственную клавишу клавиатуры. сопоставляется с открытием/закрытием вашего графического интерфейса (пример: World Map)

Основы графического интерфейса

В общем, вы можете создать свою собственную систему графического интерфейса, если хотите, просто переопределив OnRenderGUI и визуализируя все, что вам нравится. Также существует множество переопределяемых методов для обработки ввода с клавиатуры и мыши, см. также класс GuiDialog на Github.

Если вы хотите использовать ванильную систему графического интерфейса, ее концепция представляет собой просто плоский или иерархический список элементов графического интерфейса, размещенных в определенных позициях. Позиция определяется экземпляром ElementBounds. Рассмотрим подробнее его свойства:

Границы Элемента

  • FixedX/FixedY: абсолютное положение, в котором должен быть размещен элемент.
  • FixedWidth/FixedHeight: абсолютная ширина и высота элемента.
  • FixedPaddingX/FixedPaddingY: абсолютное внутреннее заполнение элемента.
  • FixedMarginX/FixedMarginY: абсолютное внешнее заполнение элемента
  • ParentBounds: родительские границы, в которых находится этот элемент.
  • ChildBounds: дочерние границы, которые содержит этот элемент.
  • Alignment: Выравнивание элемента. Если установлено значение None, используется фиксированная позиция X/Y. Для любого другого значения значения FixedX/Y игнорируются. Например, когда вы использовали RightTop, элемент всегда будет находиться в правом верхнем углу своих родительских границ. Если вы используете RightFixed, элемент будет выровнен по правому краю, но его Y-позиция определяется FixedY
  • HorizontalSizing/VerticalSizing: используемый метод изменения размера может быть либо Fixed (по умолчанию), Percentual, либо FitToChildren

GuiComposer

Это компонент, который создает и управляет вашими элементами графического интерфейса для вас. Вы можете создать композитор через клиентский API: capi.Gui.CreateCompo(dialogName, bounds). Вы должны предоставить ему уникальный идентификатор и общие границы диалога. Когда у вас есть экземпляр GUIComposer, вы можете последовательно добавлять элементы. Вот небольшой пример:

private void SetupDialog()
{
    // Auto-sized dialog at the center of the screen
    ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);

    // Just a simple 300x300 pixel box
    ElementBounds textBounds = ElementBounds.Fixed(0, 0, 300, 300);
        
    SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)
        .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), textBounds)
        .Compose()
    ;
}

Some explanations:

  • ElementStdBounds: Contains a bunch of often used element bounds configurations. See also ElementStdBounds on Github.
  • ElementBounds.Fixed(0, 0, 300, 300): Create a new bounds instance with fixedX/Y at 0/0 and a fixed widt/height of 300/300 pixels.
  • CairoFont.WhiteDetailText(): Create a new font configuration based on a often used size and color, in this case white with font size 14
  • SingleComposer: This property is defined in the GuiDialog class. Its a getter/setter to the Composers dictionary. It contains all the "windows" of this dialog which are then rendered / handled by this GuiDialog instance

Fancying it up

This is of course the absolute minimum example that will only show some text. Let's add a title bar and a dialog background:

private void SetupDialog()
{
    // Auto-sized dialog at the center of the screen
    ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);

    // Just a simple 300x100 pixel box with 40 pixels top spacing for the title bar
    ElementBounds textBounds = ElementBounds.Fixed(0, 40, 300, 100);

    // Background boundaries. Again, just make it fit it's child elements, then add the text as a child element
    ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding);
    bgBounds.BothSizing = ElementSizing.FitToChildren;
    bgBounds.WithChildren(textBounds);

    SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)
        .AddShadedDialogBG(bgBounds)
        .AddDialogTitleBar("Heck yeah!", OnTitleBarCloseClicked)
        .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), textBounds)
        .Compose()
    ;
}

private void OnTitleBarCloseClicked()
{
    TryClose();
}

This covers some of the most basic parts. There is a grand amount of various pre-built UI elements that each come with their own argument list that are in dire need of Documentation. Here's an overview of some of the more commonly used ones

Dialog / Graphics

  • .AddShadedDialogBG: Draws a pretty background and dialog border
  • .AddDialogTitleBar: Draws a title bar with a close button and a button to move around the dialog
  • .AddInset: Adds a darkened section with a inset border around it

Text

  • .AddStaticText: Add a static snippet of text
  • .AddDynamicText: Add a snippet of text that can be set to other texts without the need to redraw the whole dialog
  • .AddRichtext: Same as .AddDynamicText but allows use of VTML - a minimalist version of HTML code
  • .AddHoverText: When the mouse cursor moves over the element boundaries, will show supplied text as a tooltip

UI Control/Input

  • .AddButton: Adds a clickable button
  • .AddDropDown: Adds a drop down element
  • .AddHorizontalTabs: Adds horizontally aligned tabs, like the ingame chat window has
  • .AddVerticalScrollbar: Adds a vertical scrollbar
  • .AddTextInput: Adds an single line editable text field
  • .AddNumberInput: Adds an editable text field built for entering numbers
  • .AddTextArea: Adds multiple line editable text field

Tables/Grids/Inventory

Other

  • .AddIf/.EndIf: Can be used to conditionally add certain elements (but you can also just split up your creation code for more fine grained control)
  • .BeginClip/.EndClip: Used in combination with a scrollbar to cut away oversized content, such as in the creative inventory
  • .AddStaticCustomDraw: Lets you define your own drawing code to be added to the GUI

Examples

A simple standard dialog, triggered by the keyboard key 'U'

public class GuiDialogAnnoyingText : GuiDialog
{
    public override string ToggleKeyCombinationCode => "annoyingtextgui";

    public GuiDialogAnnoyingText(ICoreClientAPI capi) : base(capi)
    {
        SetupDialog();
    }

    private void SetupDialog()
    {
        // Auto-sized dialog at the center of the screen
        ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);

        // Just a simple 300x300 pixel box
        ElementBounds textBounds = ElementBounds.Fixed(0, 40, 300, 100);

        // Background boundaries. Again, just make it fit it's child elements, then add the text as a child element
        ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding);
        bgBounds.BothSizing = ElementSizing.FitToChildren;
        bgBounds.WithChildren(textBounds);

        // Lastly, create the dialog
        SingleComposer = capi.Gui.CreateCompo("myAwesomeDialog", dialogBounds)
            .AddShadedDialogBG(bgBounds)
            .AddDialogTitleBar("Heck yeah!", OnTitleBarCloseClicked)
            .AddStaticText("This is a piece of text at the center of your screen - Enjoy!", CairoFont.WhiteDetailText(), textBounds)
            .Compose()
        ;
    }

    private void OnTitleBarCloseClicked()
    {
        TryClose();
    }
}

public class AnnoyingTextSystem : ModSystem
{
    ICoreClientAPI capi;
    GuiDialog dialog;

    public override bool ShouldLoad(EnumAppSide forSide)
    {
        return forSide == EnumAppSide.Client;
    }
        
    public override void StartClientSide(ICoreClientAPI api)
    {
        base.StartClientSide(api);

        dialog = new GuiDialogAnnoyingText(api);

        capi = api;
        capi.Input.RegisterHotKey("annoyingtextgui", "Annoys you with annoyingly centered text", GlKeys.U, HotkeyType.GUIOrOtherControls);
        capi.Input.SetHotKeyHandler("annoyingtextgui", ToggleGui);
    }

    private bool ToggleGui(KeyCombination comb)
    {
        if (dialog.IsOpened()) dialog.TryClose();
        else dialog.TryOpen();

        return true;
    }
}


TBD

More to come here in the future.


Modding
Green Items require C# coding
Basics

Getting Started | Mod Types | Simple Examples | Theme Pack

Asset System | Textures | Items | Recipes | Blocks | Entities | Model Creator | Animation Basics | VTML & Icons | Mod Packaging & Release | Modinfo | Debugging

Advanced

JSON Patching | Advanced JSON Items | The Remapper | Server-Client Considerations | Compatibility with other mods

Setting Up Your Development Environment (General - Windows - Linux)

Advanced Blocks | Advanced Items | Item-Block Interactions | Block Behavior | Block Entities | Particle Effects | World Access | Inventory Handling | Chat Commands | GUIs | Server-Client Networking | Monkey patching (Harmony)

Data Management

Savegame Data Storage | ModConfig File | Chunk Data Storage | Tree Attribute

Worldgen

WorldGen Concepts | Terrain | Ores | Trees | WorldGen API

Rendering

Shaders and Renderers

Property Overview

Item | Entity | Block | Block Behaviors | Block Classes | Block Entities | Block Entity Behaviors

Workflows & Infrastructure

Modding Efficiency Tips | Mod-engine compatibility | Mod Extensibility | Load Order

Additional Resources

List of server commands | List of client commands | Client startup parameters | Creative Starter Guide | ServerBlockTicking | Bot System | WorldEdit | Cinematic Camera

Example Mods | API Docs | GitHub Repository