Unity'de UI Sürükle ve Bırak ile Basit Bir Envanter Sistemini Kodlama

Çoğu oyun, oyuncuların çok sayıda öğeyi toplamasına ve taşımasına olanak tanır (örn. RTS/MOBA/RPG oyunları, Aksiyon Rol Yapma oyunları vb.), Envanterin devreye girdiği yer burasıdır.

Envanter, oyuncu öğelerine hızlı erişim ve bunları düzenlemenin basit bir yolunu sağlayan bir öğeler tablosudur.

Diablo 3 Envanter Sistemi

Bu yazıda, Unity'te Öğe Alma ve Kullanıcı Arayüzü Sürükle ve Bırak ile basit bir Envanter Sistemini programlamayı öğreneceğiz.

1. Adım: Komut Dosyalarını Oluşturun

Bu eğitim 3 komut dosyası gerektirir:

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 + "'");
            }
        }
    }
}

2. Adım: Oynatıcıyı ve Envanter Sistemini Kurun

Player'ımızı kurarak başlayalım:

  • Yeni bir GameObject oluşturun ve onu çağırın "Player"
  • Yeni bir Kapsül oluşturun (GameObject -> 3D Nesne -> Kapsül), Kapsül Çarpıştırıcısı bileşenini kaldırın, ardından Kapsülü "Player" Nesnesinin içine taşıyın ve son olarak konumunu (0, 1, 0) olarak değiştirin
  • Ana Kamerayı "Player" Nesnesinin içine taşıyın ve konumunu (0, 1,64, 0) olarak değiştirin

  • SC_CharacterController betiğini "Player" Object'e ekleyin (Karakter Denetleyicisi adı verilen başka bir bileşeni otomatik olarak ekleyecektir, merkez değerini (0, 1, 0) olarak değiştirecektir))
  • Ana Kamerayı SC_CharacterController'da bir "Player Camera" değişkenine atayın

Şimdi Pick Up öğelerini ayarlayalım - bunlar oyunda seçilebilecek öğelerin Prefabrik yapıları olacaktır.

Bu eğitim için basit şekiller (Küp, Silindir ve Küre) kullanacağım ancak siz farklı modeller, muhtemelen bazı parçacıklar vb. ekleyebilirsiniz.

  • Yeni bir GameObject oluşturun ve onu çağırın "SimpleItem"
  • Yeni bir Küp oluşturun (GameObject -> 3D Object -> Cube), onu (0.4, 0.4, 0.4) değerine kadar ölçeklendirin ve ardından onu "SimpleItem" GameObject'in içine taşıyın
  • "SimpleItem"'ü seçin ve bir Rigidbody bileşeni ve bir SC_PickItem komut dosyası ekleyin

SC_PickItem'de 2 değişken olduğunu fark edeceksiniz:

Öğe adı - this should be a unique name.
Öğe Önizlemesi - a Texture that will be displayed in the Inventory UI, preferably you should assign the image that represents the item.

Benim durumumda Öğe Adı "Cube" ve Öğe Önizlemesi beyaz bir karedir:

Diğer 2 öğe için de aynı adımları tekrarlayın.

Silindir öğesi için:

  • Bir "SimpleItem" Nesnesini çoğaltın ve adlandırın "SimpleItem 2"
  • Alt Küpü kaldırın ve yeni bir Silindir oluşturun (GameObject -> 3D Nesne -> Silindir). "SimpleItem 2" içine taşıyın ve (0,4, 0,4, 0,4) değerine ölçeklendirin.
  • SC_PickItem'deki Öğe Adını "Cylinder" olarak ve Öğe Önizlemesini bir silindir görüntüsü olarak değiştirin

Küre Öğesi için:

  • Bir "SimpleItem" Nesnesini çoğaltın ve adlandırın "SimpleItem 3"
  • Alt Küpü kaldırın ve yeni bir Küre oluşturun (GameObject -> 3D Object -> Küre). "SimpleItem 3" içine taşıyın ve (0,4, 0,4, 0,4) değerine ölçeklendirin.
  • SC_PickItem'deki Öğe Adını "Sphere" olarak ve Öğe Önizlemesini bir küre görüntüsüyle değiştirin

Şimdi her öğeyi Prefabrik'e kaydedin:

Öğeler artık hazır.

Son adım Envanter Sistemini kurmaktır:

  • SC_InventorySystem'i "Player" Nesnesine ekleyin
  • Bir Artı İşareti Dokusu değişkeni atayın (Aşağıdaki resmi kullanabilir veya buradan adresinden yüksek kaliteli artı işareti dokuları alabilirsiniz):

  • SC_CharacterController'ı SC_InventorySystem'deki "Player Controller" değişkenine atayın
  • "Available Items" için önceden oluşturulmuş öğe Prefab'lerini atayın (Not: Bu, Sahne nesneleri değil, Proje görünümündeki Prefab örnekleri olmalıdır):

Envanter sistemimiz artık hazır, test edelim:

Sharp Coder Video oynatıcı

Her şey beklendiği gibi çalışıyor!

Kaynak
📁InventorySystem.unitypackage159.83 KB
Önerilen Makaleler
Unity'de Pac-Man'den Esinlenen Bir Oyun Yaratmak
Envanter ve Eşya Üretim Sisteminin Unity İçinde Yapılması
Unity'de Envantersiz Alma ve Bırakma Sistemi
Unity'de Basit 2D Madde İşareti Sistemi Oluşturma
Birlik İçinde Anahtarla Kapı Açma Eğitimi
Unity Code'da JSON ile Dahili Çalışma Şekli
Unity'de Bullet Time Etkisi Yaratmak