Zum Inhalt springen
DasPeTe Tutorials

Inventarsystem · Teil 2 von 2 der Serie

Einfache UI für das Inventarsystem

So erstellst du eine Benutzeroberfläche für das Inventar in Unity

Nachdem du das Inventarsystem fertiggestellt hast, ist es Zeit für eine Benutzeroberfläche (UI), die den Spielenden das Inventar anzeigt. In diesem Tutorial erstellen wir mit dem UI Toolkit von Unity eine einfache Inventar-UI.

Für die UI brauchen wir drei Dateien: eine UXML-Datei für das Layout, eine USS-Datei für das Styling und ein C#-Skript für die Logik.

Die UXML-Datei

Erstelle im Unity Editor einen neuen Ordner namens UI im Ordner Scripts deines Projekts. Erstelle darin eine neue UXML-Datei namens InventoryUi.

Öffne die Datei InventoryUi.uxml und füge Folgendes ein:

xml
<?xml version="1.0" encoding="utf-8"?>
<ui:UXML
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ui="UnityEngine.UIElements"
    xmlns:uie="UnityEditor.UIElements"
    xsi:noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd"
>
    <Style src="project://database/Assets/Scripts/UI/InventoryUi/InventoryUi.uss"/>
    <ui:VisualElement name="Container" class="Container">
        <ui:VisualElement name="CollectibleItems" class="CollectibleItems">
            <ui:VisualElement name="InventoryItem" class="InventoryItem">
                <ui:Label text="99" name="ItemQuantity" class="ItemQuantity"/>
            </ui:VisualElement>
        </ui:VisualElement>

        <ui:VisualElement name="BuildableItems" class="BuildableItems">
            <ui:VisualElement name="InventoryItem" class="InventoryItem">
                <ui:Label text="99" name="ItemQuantity" class="ItemQuantity"/>
            </ui:VisualElement>
        </ui:VisualElement>
    </ui:VisualElement>
</ui:UXML>

Wie du siehst, importieren wir ein Stylesheet namens InventoryUi.uss, das wir im nächsten Schritt erstellen. Das Layout besteht aus einem Container mit zwei Bereichen: einem für Collectibles und einem für Buildables. Jeder Bereich enthält ein Inventarelement mit einem Label, das die Menge des Gegenstands anzeigt. Diese Elemente sind nur Platzhalter, die zur Laufzeit ersetzt werden.

Die USS-Datei

Erstelle im Ordner UI eine neue USS-Datei namens InventoryUi.uss.

Öffne die Datei InventoryUi.uss und füge Folgendes ein:

css
.Container {
    bottom: 0;
    left: 0;
    right: 0;

    height: 50px;
    flex-direction: row;
    align-items: center;
    position: absolute;
}

.CollectibleItems {
    height: 50px;
    flex-direction: row;
    width: 50%;
    background-color: rgb(0, 0, 0);
    margin-right: 5px;
}

.BuildableItems {
    height: 50px;
    flex-direction: row;
    width: 50%;
    background-color: rgb(0, 0, 0);
}

.InventoryItem {
    width: 45px;
    height: 45px;
    margin-right: 5px;
    position: relative;
    background-size: contain;
}

.ItemQuantity {
    position: absolute;
    bottom: 1px;
    right: 1px;
    padding: 0px;
    margin: 0px;
    color: white;
    font-size: 12px;
    -unity-font-definition: resource("FlatSkin/Font/Roboto-Bold");
    -unity-text-align: upper-right;
    -unity-font-style: bold;
    -unity-text-outline-width: 1px;
    -unity-text-outline-color: rgb(0, 0, 0);
    justify-content: flex-end;
    align-self: flex-end;
    align-content: flex-end;
    align-items: flex-end;
    height: 12px;
    flex-direction: row;
    flex-grow: 1;
}

Du siehst, dass wir eine Leiste am unteren Rand mit zwei Bereichen bekommen. Jeder Bereich enthält Inventarelemente mit einem Mengen-Label in der unteren rechten Ecke.

Das Controller-Skript

Das UI-Controller-Skript enthält die Logik, die die UI aktualisiert, wenn Gegenstände zum Inventar hinzukommen oder daraus entfernt werden.

Erstelle im Ordner UI ein neues C#-Skript namens InventoryUi.

csharp
using UnityEngine;
using UnityEngine.UIElements;

public class InventoryUi : MonoBehaviour {
    public static InventoryUi Instance;

    public UIDocument uiDocument;

    // the UI element references for the main container
    // and the containers for the collectible- and buildable items
    VisualElement rootElement;
    VisualElement collectibleItemsContainer;
    VisualElement buildableItemsContainer;

    // the inventory references for the collectible- and buildable inventory
    Inventory<
        CollectibleInventoryItem,
        CollectibleItem,
        CollectibleItemType
    > collectibleInventory;

    Inventory<
        BuildableInventoryItem,
        BuildableItem,
        BuildableItemType
    > buildableInventory;

    private void Awake() {
        // set the singleton instance (very basic implementation)
        Instance = this;

        // initialize the UI references
        rootElement = uiDocument.rootVisualElement.Q<VisualElement>(className: "Container");
        collectibleItemsContainer = rootElement.Q<VisualElement>(name: "CollectibleItems");
        buildableItemsContainer = rootElement.Q<VisualElement>(name: "BuildableItems");
    }

    // set the collectible inventory reference
    // and subscribe to its events on withdraw and deposit items
    public void SetCollectibleInventory(
        Inventory<
            CollectibleInventoryItem,
            CollectibleItem,
            CollectibleItemType
        > _inventory
    ) {
        collectibleInventory = _inventory;

        collectibleInventory.OnItemDeposited += UpdateCollectibleItemsUi;
        collectibleInventory.OnItemWithdrawn += UpdateCollectibleItemsUi;

        collectibleItemsContainer.Clear();

        UpdateCollectibleItemsUi(null);
    }

    // set the buildable inventory reference
    // and subscribe to its events on withdraw and deposit items
    public void SetBuildableInventory(
        Inventory<
            BuildableInventoryItem,
            BuildableItem,
            BuildableItemType
        > _inventory
    ) {
        buildableInventory = _inventory;

        buildableInventory.OnItemDeposited += UpdateBuildableItemsUi;
        buildableInventory.OnItemWithdrawn += UpdateBuildableItemsUi;

        buildableItemsContainer.Clear();

        UpdateBuildableItemsUi(null);
    }

    void UpdateCollectibleItemsUi(CollectibleInventoryItem changedItem) {
        if(changedItem == null) {
            foreach(CollectibleInventoryItem item in collectibleInventory.items) {
                AddCollectibleInventoryElement(item);
            }

            return;
        }

        Label changedItemQuantityLabel = collectibleItemsContainer.Q<Label>(
            name: "ItemQuantity_" + changedItem.Type.ToString()
        );

        if(changedItemQuantityLabel != null) {
            int itemAmount = collectibleInventory.GetAmountOfItemType(changedItem.Type);

            if(itemAmount > 0) {
                changedItemQuantityLabel.text = collectibleInventory
                    .GetAmountOfItemType(changedItem.Type)
                    .ToString();
            } else {
                VisualElement itemElement = changedItemQuantityLabel.parent;
                collectibleItemsContainer.Remove(itemElement);
            }

            return;
        }

        AddCollectibleInventoryElement(changedItem);
    }

    void UpdateBuildableItemsUi(BuildableInventoryItem changedItem) {
        if(changedItem == null) {
            foreach(BuildableInventoryItem item in buildableInventory.items) {
                AddBuildableInventoryElement(item);
            }

            return;
        }

        Label changedItemQuantityLabel = buildableItemsContainer.Q<Label>(
            name: "ItemQuantity_" + changedItem.Type.ToString()
        );

        if(changedItemQuantityLabel != null) {
            int itemAmount = buildableInventory.GetAmountOfItemType(changedItem.Type);
            if(itemAmount > 0) {
                changedItemQuantityLabel.text = buildableInventory
                    .GetAmountOfItemType(changedItem.Type)
                    .ToString();
            } else {
                VisualElement itemElement = changedItemQuantityLabel.parent;
                buildableItemsContainer.Remove(itemElement);
            }
            return;
        }

        AddBuildableInventoryElement(changedItem);
    }

    void AddCollectibleInventoryElement(CollectibleInventoryItem item) {
        VisualElement itemElement = new VisualElement();
        itemElement.name = "InventoryItem";
        itemElement.AddToClassList("InventoryItem");

        itemElement.style.backgroundImage = new StyleBackground(item.Icon);

        Label itemQuantityLabel = new Label(
            collectibleInventory.GetAmountOfItemType(item.Type).ToString()
        );

        itemQuantityLabel.name = "ItemQuantity_" + item.Type.ToString();
        itemQuantityLabel.AddToClassList("ItemQuantity");

        itemElement.Add(itemQuantityLabel);

        collectibleItemsContainer.Add(itemElement);
    }

    void AddBuildableInventoryElement(BuildableInventoryItem item) {
        VisualElement itemElement = new VisualElement();
        itemElement.name = "InventoryItem";
        itemElement.AddToClassList("InventoryItem");

        itemElement.style.backgroundImage = new StyleBackground(item.Icon);

        Label itemQuantityLabel = new Label(
            buildableInventory.GetAmountOfItemType(item.Type).ToString()
        );

        itemQuantityLabel.name = "ItemQuantity_" + item.Type.ToString();
        itemQuantityLabel.AddToClassList("ItemQuantity");

        itemElement.Add(itemQuantityLabel);

        buildableItemsContainer.Add(itemElement);
    }
}

Die Methoden AddCollectibleInventoryElement und AddBuildableInventoryElement erstellen neue UI-Elemente für die jeweiligen Inventargegenstände und fügen sie dem passenden Container hinzu. Die Methoden UpdateCollectibleItemsUi und UpdateBuildableItemsUi aktualisieren die UI, wenn Gegenstände zum Inventar hinzukommen oder daraus entfernt werden.

Wir könnten auch generische Methoden hinzufügen, um doppelten Code zu vermeiden, aber der Übersichtlichkeit halber lassen wir sie hier getrennt.

Hänge zum Schluss das Skript InventoryUi an ein neues GameObject in deiner Szene und füge ihm eine UIDocument-Komponente hinzu. Weise in der UIDocument-Komponente die Datei InventoryUi.uxml dem Feld Source Asset zu.

Du solltest nun diese Struktur in deinem Unity-Projekt haben:

text
Assets/
├── Objects/
│   ├── Collectibles/
│   │   ├── Wood.asset
│   │   ├── Stone.asset
│   │   ├── Iron.asset
│   │   ├── Silver.asset
│   │   └── Gold.asset
│   └── Buildables/
│       └── Storage.asset
└── Scripts/
    ├── Inventory/
    │   ├── Inventory.cs
    │   └── IInventoryItem.cs
    ├── Items/
    │   ├── Collectibles/
    │   │   ├── CollectibleItem.cs
    │   │   ├── CollectibleItemType.cs
    │   │   └── CollectibleInventoryItem.cs
    │   └── Buildables/
    │       ├── BuildableItem.cs
    │       ├── BuildableItemType.cs
    │       ├── BuildableInventoryItem.cs
    │       └── Buildable.cs
    ├── Ui/
    │   └── InventoryUi/
    │       ├── InventoryUi.uss
    │       ├── InventoryUi.uxml
    │       └── InventoryUi.cs
    └── PlayerController.cs

Kommentare

Die Kommentare sind GitHub Discussions: Zum Schreiben mit GitHub anmelden.

Die Kommentare kommen über giscus von GitHub. Beim Laden gehen deine IP-Adresse an giscus.app und GitHub.