यूआई ड्रैग एंड ड्रॉप इन यूनिटी के साथ एक सरल इन्वेंटरी सिस्टम को कोडिंग
कई गेम खिलाड़ियों को बड़ी संख्या में आइटम इकट्ठा करने और ले जाने की अनुमति देते हैं (उदाहरण के लिए आरटीएस/एमओबीए/आरपीजी गेम, एक्शन रोल-प्लेइंग गेम इत्यादि), यहीं से इन्वेंटरी काम आती है।
इन्वेंटरी तत्वों की एक तालिका है जो खिलाड़ी वस्तुओं तक त्वरित पहुंच और उन्हें व्यवस्थित करने का एक सरल तरीका प्रदान करती है।
इस पोस्ट में, हम सीखेंगे कि आइटम पिक अप और यूआई ड्रैग एंड ड्रॉप के साथ Unity में एक सरल इन्वेंटरी सिस्टम को कैसे प्रोग्राम किया जाए।
चरण 1: स्क्रिप्ट बनाएं
इस ट्यूटोरियल के लिए 3 स्क्रिप्ट की आवश्यकता है:
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: प्लेयर और इन्वेंटरी सिस्टम सेट करें
आइए अपना प्लेयर सेट करके शुरुआत करें:
- एक नया गेमऑब्जेक्ट बनाएं और उसे कॉल करें "Player"
- एक नया कैप्सूल बनाएं (गेमऑब्जेक्ट -> 3डी ऑब्जेक्ट -> कैप्सूल) कैप्सूल कोलाइडर घटक को हटा दें, फिर कैप्सूल को "Player" ऑब्जेक्ट के अंदर ले जाएं और अंत में इसकी स्थिति को (0, 1, 0) में बदलें।
- मुख्य कैमरे को "Player" ऑब्जेक्ट के अंदर ले जाएं और उसकी स्थिति को (0, 1.64, 0) में बदलें
- SC_CharacterController स्क्रिप्ट को "Player" ऑब्जेक्ट में संलग्न करें (यह स्वचालित रूप से कैरेक्टर कंट्रोलर नामक एक अन्य घटक जोड़ देगा, इसके केंद्र मान को (0, 1, 0) में बदल देगा)
- SC_CharacterController पर मुख्य कैमरे को "Player Camera" वेरिएबल पर असाइन करें
अब पिक अप आइटम सेटअप करें - ये उन आइटम के प्रीफ़ैब होंगे जिन्हें गेम में चुना जा सकता है।
इस ट्यूटोरियल के लिए, मैं सरल आकृतियों (घन, सिलेंडर और क्षेत्र) का उपयोग करूंगा, लेकिन आप अलग-अलग मॉडल, संभवतः कुछ कण आदि जोड़ सकते हैं।
- एक नया गेमऑब्जेक्ट बनाएं और उसे कॉल करें "SimpleItem"
- एक नया क्यूब बनाएं (गेमऑब्जेक्ट -> 3डी ऑब्जेक्ट -> क्यूब), इसे नीचे स्केल करें (0.4, 0.4, 0.4) फिर इसे "SimpleItem" गेमऑब्जेक्ट के अंदर ले जाएं
- "SimpleItem" चुनें और एक Rigidbody घटक और एक SC_PickItem स्क्रिप्ट जोड़ें
आप देखेंगे कि SC_PickItem में 2 वेरिएबल हैं:
आइटम नाम - this should be a unique name.आइटम पूर्वावलोकन - a Texture that will be displayed in the Inventory UI, preferably you should assign the image that represents the item.
मेरे मामले में आइटम का नाम "Cube" है और आइटम पूर्वावलोकन एक सफेद वर्ग है:
अन्य 2 वस्तुओं के लिए भी यही चरण दोहराएँ।
सिलेंडर आइटम के लिए:
- "SimpleItem" ऑब्जेक्ट की डुप्लिकेट बनाएं और उसे नाम दें "SimpleItem 2"
- चाइल्ड क्यूब निकालें और एक नया सिलेंडर बनाएं (गेमऑब्जेक्ट -> 3डी ऑब्जेक्ट -> सिलेंडर)। इसे "SimpleItem 2" के अंदर ले जाएं और इसे (0.4, 0.4, 0.4) पर स्केल करें।
- SC_PickItem में आइटम नाम को "Cylinder" में बदलें और आइटम पूर्वावलोकन को सिलेंडर की छवि में बदलें
क्षेत्र मद के लिए:
- "SimpleItem" ऑब्जेक्ट की डुप्लिकेट बनाएं और उसे नाम दें "SimpleItem 3"
- चाइल्ड क्यूब को हटाएं और एक नया क्षेत्र बनाएं (गेमऑब्जेक्ट -> 3डी ऑब्जेक्ट -> क्षेत्र)। इसे "SimpleItem 3" के अंदर ले जाएं और इसे (0.4, 0.4, 0.4) पर स्केल करें।
- SC_PickItem में आइटम का नाम बदलकर "Sphere" करें और आइटम पूर्वावलोकन को एक गोले की छवि में बदलें
अब प्रत्येक आइटम को प्रीफ़ैब में सहेजें:
आइटम अब तैयार हैं.
अंतिम चरण इन्वेंटरी सिस्टम स्थापित करना है:
- SC_InventorySystem को "Player" ऑब्जेक्ट से जोड़ें
- एक क्रॉसहेयर टेक्सचर वैरिएबल असाइन करें (आप नीचे दी गई छवि का उपयोग कर सकते हैं या यहां से उच्च-गुणवत्ता वाले क्रॉसहेयर टेक्सचर प्राप्त कर सकते हैं):
- SC_InventorySystem में "Player Controller" वेरिएबल को SC_CharacterController असाइन करें
- "Available Items" के लिए पहले से बनाए गए आइटम प्रीफ़ैब्स को असाइन करें (नोट: यह प्रोजेक्ट व्यू से प्रीफ़ैब इंस्टेंसेस होना चाहिए न कि सीन ऑब्जेक्ट्स):
इन्वेंट्री सिस्टम अब तैयार है, आइए इसका परीक्षण करें:
सब कुछ उम्मीद के मुताबिक काम करता है!