Codieren eines einfachen Inventarsystems mit Drag-and-Drop der Benutzeroberfläche in Unity

Bei vielen Spielen können Spieler eine große Anzahl von Gegenständen sammeln und mit sich herumtragen (z. B. RTS-/MOBA-/RPG-Spiele, Action-Rollenspiele usw.). Hier kommt das Inventar ins Spiel.

Inventar ist eine Tabelle mit Elementen, die schnellen Zugriff auf Spielergegenstände und eine einfache Möglichkeit bietet, diese zu organisieren.

Diablo 3-Inventarsystem

In diesem Beitrag lernen wir, wie man ein einfaches Inventarsystem mit Artikelabholung und UI-Drag & Drop in Unity programmiert.

Schritt 1: Erstellen Sie die Skripte

Für dieses Tutorial sind drei Skripte erforderlich:

SC_CharacterController.cs

//You are free to use this script in Free or Commercial projects
//sharpcoderblog.com @2019

using UnityEngine;

[RequireComponent(typeof(CharacterController))]

public class SC_CharacterController : MonoBehaviour
{
    public float speed = 7.5f;
    public float jumpSpeed = 8.0f;
    public float gravity = 20.0f;
    public Camera playerCamera;
    public float lookSpeed = 2.0f;
    public float lookXLimit = 60.0f;

    CharacterController characterController;
    Vector3 moveDirection = Vector3.zero;
    Vector2 rotation = Vector2.zero;

    [HideInInspector]
    public bool canMove = true;

    void Start()
    {
        characterController = GetComponent<CharacterController>();
        rotation.y = transform.eulerAngles.y;
    }

    void Update()
    {
        if (characterController.isGrounded)
        {
            // We are grounded, so recalculate move direction based on axes
            Vector3 forward = transform.TransformDirection(Vector3.forward);
            Vector3 right = transform.TransformDirection(Vector3.right);
            float curSpeedX = speed * Input.GetAxis("Vertical");
            float curSpeedY = speed * Input.GetAxis("Horizontal");
            moveDirection = (forward * curSpeedX) + (right * curSpeedY);

            if (Input.GetButton("Jump"))
            {
                moveDirection.y = jumpSpeed;
            }
        }

        // Apply gravity. Gravity is multiplied by deltaTime twice (once here, and once below
        // when the moveDirection is multiplied by deltaTime). This is because gravity should be applied
        // as an acceleration (ms^-2)
        moveDirection.y -= gravity * Time.deltaTime;

        // Move the controller
        characterController.Move(moveDirection * Time.deltaTime);

        // Player and Camera rotation
        if (canMove)
        {
            rotation.y += Input.GetAxis("Mouse X") * lookSpeed;
            rotation.x += -Input.GetAxis("Mouse Y") * lookSpeed;
            rotation.x = Mathf.Clamp(rotation.x, -lookXLimit, lookXLimit);
            playerCamera.transform.localRotation = Quaternion.Euler(rotation.x, 0, 0);
            transform.eulerAngles = new Vector2(0, rotation.y);
        }
    }
}

SC_PickItem.cs

//You are free to use this script in Free or Commercial projects
//sharpcoderblog.com @2019

using UnityEngine;

public class SC_PickItem : MonoBehaviour
{
    public string itemName = "Some Item"; //Each item must have an unique name
    public Texture itemPreview;

    void Start()
    {
        //Change item tag to Respawn to detect when we look at it
        gameObject.tag = "Respawn";
    }

    public void PickItem()
    {
        Destroy(gameObject);
    }
}

SC_InventorySystem.cs

//You are free to use this script in Free or Commercial projects
//sharpcoderblog.com @2019

using UnityEngine;

public class SC_InventorySystem : MonoBehaviour
{
    public Texture crosshairTexture;
    public SC_CharacterController playerController;
    public SC_PickItem[] availableItems; //List with Prefabs of all the available items

    //Available items slots
    int[] itemSlots = new int[12];
    bool showInventory = false;
    float windowAnimation = 1;
    float animationTimer = 0;

    //UI Drag & Drop
    int hoveringOverIndex = -1;
    int itemIndexToDrag = -1;
    Vector2 dragOffset = Vector2.zero;

    //Item Pick up
    SC_PickItem detectedItem;
    int detectedItemIndex;

    // Start is called before the first frame update
    void Start()
    {
        Cursor.visible = false;
        Cursor.lockState = CursorLockMode.Locked;

        //Initialize Item Slots
        for (int i = 0; i < itemSlots.Length; i++)
        {
            itemSlots[i] = -1;
        }
    }

    // Update is called once per frame
    void Update()
    {
        //Show/Hide inventory
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            showInventory = !showInventory;
            animationTimer = 0;

            if (showInventory)
            {
                Cursor.visible = true;
                Cursor.lockState = CursorLockMode.None;
            }
            else
            {
                Cursor.visible = false;
                Cursor.lockState = CursorLockMode.Locked;
            }
        }

        if (animationTimer < 1)
        {
            animationTimer += Time.deltaTime;
        }

        if (showInventory)
        {
            windowAnimation = Mathf.Lerp(windowAnimation, 0, animationTimer);
            playerController.canMove = false;
        }
        else
        {
            windowAnimation = Mathf.Lerp(windowAnimation, 1f, animationTimer);
            playerController.canMove = true;
        }

        //Begin item drag
        if (Input.GetMouseButtonDown(0) && hoveringOverIndex > -1 && itemSlots[hoveringOverIndex] > -1)
        {
            itemIndexToDrag = hoveringOverIndex;
        }

        //Release dragged item
        if (Input.GetMouseButtonUp(0) && itemIndexToDrag > -1)
        {
            if (hoveringOverIndex < 0)
            {
                //Drop the item outside
                Instantiate(availableItems[itemSlots[itemIndexToDrag]], playerController.playerCamera.transform.position + (playerController.playerCamera.transform.forward), Quaternion.identity);
                itemSlots[itemIndexToDrag] = -1;
            }
            else
            {
                //Switch items between the selected slot and the one we are hovering on
                int itemIndexTmp = itemSlots[itemIndexToDrag];
                itemSlots[itemIndexToDrag] = itemSlots[hoveringOverIndex];
                itemSlots[hoveringOverIndex] = itemIndexTmp;

            }
            itemIndexToDrag = -1;
        }

        //Item pick up
        if (detectedItem && detectedItemIndex > -1)
        {
            if (Input.GetKeyDown(KeyCode.F))
            {
                //Add the item to inventory
                int slotToAddTo = -1;
                for (int i = 0; i < itemSlots.Length; i++)
                {
                    if (itemSlots[i] == -1)
                    {
                        slotToAddTo = i;
                        break;
                    }
                }
                if (slotToAddTo > -1)
                {
                    itemSlots[slotToAddTo] = detectedItemIndex;
                    detectedItem.PickItem();
                }
            }
        }
    }

    void FixedUpdate()
    {
        //Detect if the Player is looking at any item
        RaycastHit hit;
        Ray ray = playerController.playerCamera.ViewportPointToRay(new Vector3(0.5F, 0.5F, 0));

        if (Physics.Raycast(ray, out hit, 2.5f))
        {
            Transform objectHit = hit.transform;

            if (objectHit.CompareTag("Respawn"))
            {
                if ((detectedItem == null || detectedItem.transform != objectHit) && objectHit.GetComponent<SC_PickItem>() != null)
                {
                    SC_PickItem itemTmp = objectHit.GetComponent<SC_PickItem>();

                    //Check if item is in availableItemsList
                    for (int i = 0; i < availableItems.Length; i++)
                    {
                        if (availableItems[i].itemName == itemTmp.itemName)
                        {
                            detectedItem = itemTmp;
                            detectedItemIndex = i;
                        }
                    }
                }
            }
            else
            {
                detectedItem = null;
            }
        }
        else
        {
            detectedItem = null;
        }
    }

    void OnGUI()
    {
        //Inventory UI
        GUI.Label(new Rect(5, 5, 200, 25), "Press 'Tab' to open Inventory");

        //Inventory window
        if (windowAnimation < 1)
        {
            GUILayout.BeginArea(new Rect(10 - (430 * windowAnimation), Screen.height / 2 - 200, 302, 430), GUI.skin.GetStyle("box"));

            GUILayout.Label("Inventory", GUILayout.Height(25));

            GUILayout.BeginVertical();
            for (int i = 0; i < itemSlots.Length; i += 3)
            {
                GUILayout.BeginHorizontal();
                //Display 3 items in a row
                for (int a = 0; a < 3; a++)
                {
                    if (i + a < itemSlots.Length)
                    {
                        if (itemIndexToDrag == i + a || (itemIndexToDrag > -1 && hoveringOverIndex == i + a))
                        {
                            GUI.enabled = false;
                        }

                        if (itemSlots[i + a] > -1)
                        {
                            if (availableItems[itemSlots[i + a]].itemPreview)
                            {
                                GUILayout.Box(availableItems[itemSlots[i + a]].itemPreview, GUILayout.Width(95), GUILayout.Height(95));
                            }
                            else
                            {
                                GUILayout.Box(availableItems[itemSlots[i + a]].itemName, GUILayout.Width(95), GUILayout.Height(95));
                            }
                        }
                        else
                        {
                            //Empty slot
                            GUILayout.Box("", GUILayout.Width(95), GUILayout.Height(95));
                        }

                        //Detect if the mouse cursor is hovering over item
                        Rect lastRect = GUILayoutUtility.GetLastRect();
                        Vector2 eventMousePositon = Event.current.mousePosition;
                        if (Event.current.type == EventType.Repaint && lastRect.Contains(eventMousePositon))
                        {
                            hoveringOverIndex = i + a;
                            if (itemIndexToDrag < 0)
                            {
                                dragOffset = new Vector2(lastRect.x - eventMousePositon.x, lastRect.y - eventMousePositon.y);
                            }
                        }

                        GUI.enabled = true;
                    }
                }
                GUILayout.EndHorizontal();
            }
            GUILayout.EndVertical();

            if (Event.current.type == EventType.Repaint && !GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
            {
                hoveringOverIndex = -1;
            }

            GUILayout.EndArea();
        }

        //Item dragging
        if (itemIndexToDrag > -1)
        {
            if (availableItems[itemSlots[itemIndexToDrag]].itemPreview)
            {
                GUI.Box(new Rect(Input.mousePosition.x + dragOffset.x, Screen.height - Input.mousePosition.y + dragOffset.y, 95, 95), availableItems[itemSlots[itemIndexToDrag]].itemPreview);
            }
            else
            {
                GUI.Box(new Rect(Input.mousePosition.x + dragOffset.x, Screen.height - Input.mousePosition.y + dragOffset.y, 95, 95), availableItems[itemSlots[itemIndexToDrag]].itemName);
            }
        }

        //Display item name when hovering over it
        if (hoveringOverIndex > -1 && itemSlots[hoveringOverIndex] > -1 && itemIndexToDrag < 0)
        {
            GUI.Box(new Rect(Input.mousePosition.x, Screen.height - Input.mousePosition.y - 30, 100, 25), availableItems[itemSlots[hoveringOverIndex]].itemName);
        }

        if (!showInventory)
        {
            //Player crosshair
            GUI.color = detectedItem ? Color.green : Color.white;
            GUI.DrawTexture(new Rect(Screen.width / 2 - 4, Screen.height / 2 - 4, 8, 8), crosshairTexture);
            GUI.color = Color.white;

            //Pick up message
            if (detectedItem)
            {
                GUI.color = new Color(0, 0, 0, 0.84f);
                GUI.Label(new Rect(Screen.width / 2 - 75 + 1, Screen.height / 2 - 50 + 1, 150, 20), "Press 'F' to pick '" + detectedItem.itemName + "'");
                GUI.color = Color.green;
                GUI.Label(new Rect(Screen.width / 2 - 75, Screen.height / 2 - 50, 150, 20), "Press 'F' to pick '" + detectedItem.itemName + "'");
            }
        }
    }
}

Schritt 2: Richten Sie das Player- und Inventarsystem ein

Beginnen wir mit der Einrichtung unseres Players:

  • Erstellen Sie ein neues GameObject und nennen Sie es "Player"
  • Erstellen Sie eine neue Kapsel (GameObject -> 3D-Objekt -> Kapsel), entfernen Sie die Capsule Collider-Komponente, verschieben Sie dann die Kapsel in das "Player"-Objekt und ändern Sie schließlich ihre Position in (0, 1, 0).
  • Bewegen Sie die Hauptkamera in das Objekt "Player" und ändern Sie ihre Position in (0, 1,64, 0).

  • Hängen Sie das SC_CharacterController-Skript an das Objekt "Player" an (es fügt automatisch eine weitere Komponente namens „Character Controller“ hinzu und ändert den Mittelwert in (0, 1, 0)).
  • Weisen Sie die Hauptkamera einer "Player Camera"-Variablen bei SC_CharacterController zu

Jetzt richten wir die Gegenstände zum Aufnehmen ein – dabei handelt es sich um Fertigteile der Gegenstände, die im Spiel gepflückt werden können.

Für dieses Tutorial verwende ich einfache Formen (Würfel, Zylinder und Kugel), aber Sie können auch andere Modelle hinzufügen, möglicherweise einige Partikel usw.

  • Erstellen Sie ein neues GameObject und nennen Sie es "SimpleItem"
  • Erstellen Sie einen neuen Würfel (GameObject -> 3D-Objekt -> Würfel), verkleinern Sie ihn auf (0,4, 0,4, 0,4) und verschieben Sie ihn dann in "SimpleItem" GameObject
  • Wählen Sie "SimpleItem" aus und fügen Sie eine Rigidbody-Komponente und ein SC_PickItem-Skript hinzu

Sie werden feststellen, dass es in SC_PickItem zwei Variablen gibt:

Artikelname - this should be a unique name.
Artikelvorschau - a Texture that will be displayed in the Inventory UI, preferably you should assign the image that represents the item.

In meinem Fall ist der Artikelname "Cube" und die Artikelvorschau ist ein weißes Quadrat:

Wiederholen Sie die gleichen Schritte für die anderen 2 Elemente.

Für Zylinderartikel:

  • Duplizieren Sie ein "SimpleItem"-Objekt und benennen Sie es "SimpleItem 2"
  • Entfernen Sie den untergeordneten Würfel und erstellen Sie einen neuen Zylinder (GameObject -> 3D-Objekt -> Zylinder). Verschieben Sie es innerhalb von "SimpleItem 2" und skalieren Sie es auf (0,4, 0,4, 0,4).
  • Ändern Sie den Artikelnamen in SC_PickItem in "Cylinder" und die Artikelvorschau in ein Bild eines Zylinders

Für Kugelartikel:

  • Duplizieren Sie ein "SimpleItem"-Objekt und benennen Sie es "SimpleItem 3"
  • Entfernen Sie den untergeordneten Würfel und erstellen Sie eine neue Kugel (GameObject -> 3D-Objekt -> Kugel). Verschieben Sie es innerhalb von "SimpleItem 3" und skalieren Sie es auf (0,4, 0,4, 0,4).
  • Ändern Sie den Artikelnamen in SC_PickItem in "Sphere" und die Artikelvorschau in ein Bild einer Kugel

Speichern Sie nun jedes Element in Prefab:

Die Artikel sind nun fertig.

Der letzte Schritt besteht darin, das Inventarsystem einzurichten:

  • Hängen Sie SC_InventorySystem an das Objekt "Player" an
  • Weisen Sie eine Variable für die Fadenkreuztextur zu (Sie können das Bild unten verwenden oder hochwertige Fadenkreuztexturen von hier erhalten):

  • Weisen Sie SC_CharacterController der Variablen "Player Controller" in SC_InventorySystem zu
  • Weisen Sie für "Available Items" zuvor erstellte Element-Prefabs zu (Hinweis: Dies sollten Prefab-Instanzen aus der Projektansicht und keine Szenenobjekte sein):

Das Inventarsystem ist jetzt fertig, testen wir es:

Sharp Coder Videoplayer

Alles funktioniert wie erwartet!

Quelle
📁InventorySystem.unitypackage159.83 KB
Empfohlene Artikel
Erstellen eines Puzzlespiels in Unity
Erstellen eines von Pac-Man inspirierten Spiels in Unity
Erstellen eines Jagdsimulators in Unity
Erstellen eines Verkehrssimulators in Unity
Erstellen eines einfachen 2D-Aufzählungssystems in Unity
Tutorial zum Öffnen einer Tür mit einem Schlüssel in Unity
Unity implementiert Schrittgeräusche