जो भी वस्तु है उसके आधार पर संदर्भ मेनू कैसे डिजाइन करें?


21

मैं "राइट क्लिक विकल्प" व्यवहार के लिए एक समाधान की तलाश कर रहा हूं।

मूल रूप से खेल में कोई भी और हर वस्तु, जब दायां क्लिक किया जाता है, तो जो भी वस्तु होती है, उसके आधार पर विकल्पों का एक सेट प्रदर्शित कर सकता है।

विभिन्न परिदृश्यों के लिए राइट क्लिक उदाहरण :

इन्वेंटरी: हेलमेट विकल्प दिखाता है (लैस, उपयोग, ड्रॉप, विवरण)

बैंक: हेलमेट से पता चलता है कि विकल्प (1, टेक एक्स, टेक ऑल, डिटेल्स लें)

मंजिल: हेलमेट विकल्प दिखाता है (लो, यहां चलें, विवरण)

जाहिर है कि प्रत्येक विकल्प किसी न किसी तरीके की ओर इशारा करता है जो कहता है। यह उस मुद्दे का हिस्सा है जिसे मैं जानने की कोशिश कर रहा हूं। किसी एक आइटम के लिए इतने पोटेंशियल ऑप्शन के साथ, मैं अपनी क्लासेस को इस तरह से कैसे डिजाइन करूँगी, जो बहुत गन्दा न हो?

  • मैंने वंशानुक्रम के बारे में सोचा है, लेकिन यह वास्तव में लंबा हो सकता है और श्रृंखला बहुत बड़ी हो सकती है।
  • मैंने इंटरफेस का उपयोग करने के बारे में सोचा है, लेकिन यह शायद मुझे थोड़ा प्रतिबंधित करेगा क्योंकि मैं एक एक्सएमएल फ़ाइल से आइटम डेटा लोड करने और इसे एक सामान्य "आइटम" वर्ग में रखने में सक्षम नहीं होगा।

मैं Runescape नामक गेम पर अपने वांछित अंतिम परिणाम को आधार बना रहा हूं। हर वस्तु को खेल में दायाँ क्लिक किया जा सकता है और यह क्या है, और यह कहाँ है (इन्वेंट्री, फ्लोर, बैंक इत्यादि) पर निर्भर करता है कि खिलाड़ी के साथ बातचीत करने के लिए उपलब्ध विकल्पों का एक अलग सेट प्रदर्शित करता है।

मैं इसे कैसे प्राप्त करूंगा? मुझे सबसे पहले किस दृष्टिकोण पर जाना चाहिए, यह तय करें कि कौन से विकल्प प्रदर्शित होने चाहिए और एक बार क्लिक करने के बाद, संबंधित पद्धति को कैसे कॉल किया जाए।

मैं C # और Unity3D का उपयोग कर रहा हूं, लेकिन प्रदान किए गए किसी भी उदाहरण को उनमें से किसी से संबंधित होने की आवश्यकता नहीं है क्योंकि मैं एक पैटर्न के बाद वास्तविक कोड के विपरीत हूं।

किसी भी मदद की बहुत सराहना की जाती है और अगर मैं अपने प्रश्न या वांछित परिणामों में स्पष्ट नहीं हुआ हूं, तो कृपया एक टिप्पणी पोस्ट करें और मैं इसे ASAP में लिखूंगा।

यहाँ मैंने अभी तक कोशिश की है:

  • मैं वास्तव में एक सामान्य "आइटम" वर्ग को लागू करने में कामयाब रहा हूं जो विभिन्न प्रकार की वस्तुओं (अतिरिक्त हमले, अतिरिक्त रक्षा, लागत आदि ...) के लिए सभी मान रखता है। ये वैरिएबल Xml फ़ाइल के डेटा से पॉप्युलेट होते हैं।
  • मैंने आइटम वर्ग के अंदर हर एक संभव बातचीत विधि रखने के बारे में सोचा है, लेकिन मुझे लगता है कि यह अविश्वसनीय रूप से गड़बड़ और खराब रूप है। मैंने शायद इस तरह की प्रणाली को केवल एक वर्ग का उपयोग करके और अलग-अलग वस्तुओं के लिए उप-क्लासिंग नहीं करने के लिए गलत तरीका अपनाया है, लेकिन इसका एकमात्र तरीका मैं एक एक्सएमएल से डेटा लोड कर सकता हूं और इसे कक्षा में संग्रहीत कर सकता हूं।
  • मेरे द्वारा Xml फ़ाइल से अपने सभी आइटम लोड करने का जो कारण चुना गया है, वह इस गेम के कारण 40,000+ आइटम के लिए संभावना है। यदि मेरा गणित सही है, तो प्रत्येक आइटम के लिए एक वर्ग बहुत सारी कक्षाएं हैं।

"इक्विप" के अपवाद के साथ आपकी कमांड की सूची को देखते हुए, ऐसा लगता है कि ये सभी सामान्य हैं और इस बात की परवाह किए बिना कि आइटम क्या है - ले, ड्रॉप, डिस्क्रिप्शन, यहां ले जाएं, आदि
ashes999

यदि कोई आइटम अन-ट्रेडेबल था, तो "ड्रॉप" के बजाय "नष्ट" हो सकता था
माइक हंट

पूरी तरह से फ्रैंक होने के लिए, कई गेम एक डीएसएल का उपयोग करके इसे हल करते हैं - खेल के लिए एक कस्टम स्क्रिप्टिंग भाषा।
corsiKa

1
RuneScape के बाद अपने गेम की मॉडलिंग के लिए +1। मुझे वह खेल बहुत पसंद है।
जिनाडिक्स

जवाबों:


22

जैसा कि सॉफ्टवेयर विकास में सब कुछ है, कोई आदर्श समाधान नहीं है। केवल वह समाधान जो आपके और आपकी परियोजना के लिए आदर्श है। यहाँ कुछ आप उपयोग कर सकते हैं।

विकल्प 1: प्रक्रियात्मक मॉडल

प्राचीन अप्रचलित ओल्ड-स्कूल विधि।

सभी आइटम बिना किसी विधि के गूंगे सादे-पुराने-डेटा प्रकार हैं, लेकिन बहुत सारी सार्वजनिक विशेषताएँ, जो एक आइटम के सभी गुणों का प्रतिनिधित्व करती हैं, जिसमें कुछ बूलियन झंडे शामिल हैं isEdible, isEquipableआदि , जो यह निर्धारित करते हैं कि इसके लिए कौन सी संदर्भ मेनू प्रविष्टियां उपलब्ध हैं (शायद आप भी कर सकते हैं) इन झंडों के बिना करें जब आप इसे अन्य विशेषताओं के मूल्यों से प्राप्त कर सकते हैं)। अपने खिलाड़ी वर्ग में कुछ तरीके जैसे आदि Eat, Equipजो एक आइटम लेता है और जिसमें विशेषता मानों के अनुसार इसे संसाधित करने के सभी तर्क हैं।

विकल्प 2: ऑब्जेक्ट-ओरिएंटेड मॉडल

यह एक OOP-by-the-book समाधान है जो विरासत और बहुरूपता पर आधारित है।

एक बेस-क्लास Itemहै जिसमें से अन्य आइटम जैसे EdibleItem, EquipableItemआदि विरासत में मिलते हैं। बेस क्लास के पास एक सार्वजनिक तरीका होना चाहिए GetContextMenuEntriesForBank, GetContextMenuEntriesForFloorआदि जो की एक सूची लौटाते हैं ContextMenuEntry। प्रत्येक इनहेरिटिंग क्लास संदर्भ मेनू प्रविष्टियों को वापस करने के लिए इन तरीकों को ओवरराइड करेगा जो इस आइटम प्रकार के लिए उपयुक्त हैं। यह कुछ डिफ़ॉल्ट प्रविष्टियों को प्राप्त करने के लिए आधार वर्ग की उसी पद्धति को भी कह सकता है जो किसी भी आइटम प्रकार के लिए लागू होती हैं। ContextMenuEntryएक विधि के साथ एक वर्ग होगा Performजो तब मद जो इसे बनाया (यदि आप एक से इस्तेमाल कर सकते हैं प्रासंगिक प्रणाली को बुलाती प्रतिनिधि इस बात के लिए)।

XML फ़ाइल से डेटा पढ़ते समय इस पैटर्न को लागू करने में आपकी समस्याओं के बारे में: पहले आइटम के प्रकार को निर्धारित करने के लिए प्रत्येक आइटम के लिए XML नोड की जांच करें, फिर उपयुक्त उप-वर्ग का एक उदाहरण बनाने के लिए प्रत्येक प्रकार के लिए विशेष कोड का उपयोग करें।

विकल्प 3: घटक-आधारित मॉडल

यह पैटर्न वंशानुक्रम के बजाय रचना का उपयोग करता है और यह बताता है कि बाकी एकता कैसे काम करती है। इस बात पर निर्भर करता है कि आप अपने खेल की संरचना कैसे करते हैं, इसके लिए यूनिटी घटक प्रणाली का उपयोग करना संभव / लाभदायक हो सकता है ... या नहीं, आपका माइलेज भिन्न हो सकता है।

वर्ग की प्रत्येक वस्तु Itemकी तरह घटकों की सूची के लिए होता है Equipable, Edible, Sellable, Drinkable, आदि एक आइटम एक या प्रत्येक घटक में से कोई भी हो सकता है (उदाहरण के लिए, एक हेलमेट चॉकलेट से बना दोनों होगा Equipableऔर Edible, और जब यह एक साजिश-महत्वपूर्ण नहीं है खोज आइटम भी Sellable)। प्रोग्रामिंग तर्क जो घटक के लिए विशिष्ट है, उस घटक में कार्यान्वित किया जाता है। जब उपयोगकर्ता किसी आइटम पर राइट-क्लिक करता है, तो आइटम के घटक पुनरावृत्त होते हैं और संदर्भ-मेनू प्रविष्टियों को प्रत्येक घटक के लिए जोड़ा जाता है जो मौजूद हैं। जब उपयोगकर्ता इन प्रविष्टियों में से एक का चयन करता है, तो उस प्रविष्टि को जोड़ने वाले घटक विकल्प को संसाधित करता है।

आप प्रत्येक घटक के लिए एक उप-नोड होने पर अपनी XML-फ़ाइल में इसका प्रतिनिधित्व कर सकते हैं। उदाहरण:

   <item>
      <name>Chocolate Helmet</name>
      <sprite>helmet-chocolate.png</sprite>
      <description>Protects you from enemies and from starving</description>
      <edible>
          <taste>sweet</taste>
          <calories>2560</calories>
      </edible>
      <equipable>
          <slot>head</slot>
          <def>20</def>
      </equipable>
      <sellable>
          <value>120</value>
      </sellable>
   </item>

आपके बहुमूल्य स्पष्टीकरण और मेरे प्रश्न का उत्तर देने के लिए आपने जो समय दिया, उसके लिए धन्यवाद। हालांकि मैंने अभी तक यह तय नहीं किया है कि मैं किस विधि के साथ जाऊंगा, मैं आपके द्वारा प्रदान किए गए कार्यान्वयन के वैकल्पिक तरीकों की सराहना करता हूं। मैं बैठकर सोचूंगा कि कौन सी विधि मेरे लिए बेहतर काम करेगी और वहां से चली जाएगी। धन्यवाद :)
माइक हंट

@MikeHunt लिस्ट-ऑफ-कंपोनेंट्स मॉडल निश्चित रूप से एक ऐसी चीज है जिसकी आपको जांच करनी चाहिए, क्योंकि यह किसी फाइल से लोडिंग आइटम परिभाषाओं के साथ अच्छी तरह से काम करती है।
user253751

@ मिनीबिस वह है जो मैं पहले कोशिश कर रहा हूं क्योंकि मेरा प्रारंभिक प्रयास उसी के समान था। धन्यवाद :)
माइक हंट

पुराना उत्तर, लेकिन क्या "सूची-के-घटकों" मॉडल को लागू करने के बारे में कोई दस्तावेज है?
जेफ

@ जेफ यदि आप इस पैटर्न को अपने खेल में लागू करना चाहते हैं और इस बारे में कोई सवाल है कि कैसे, कृपया एक नया प्रश्न पोस्ट करें।
फिलिप

9

तो, माइक हंट, आपके सवाल ने मुझे बहुत दिलचस्पी दी, मैंने एक पूर्ण समाधान लागू करने का फैसला किया। अलग-अलग चीजों की कोशिश करने के तीन घंटे के बाद, मैं इस चरण-दर-चरण समाधान के साथ समाप्त हुआ:

(कृपया ध्यान दें, कि यह बहुत अच्छा कोड नहीं है, इसलिए मैं किसी भी संपादन को स्वीकार करूंगा)

कंटेंट पैनल बनाना

(यह पैनल हमारे संदर्भ मेनू बटन के लिए एक कंटेनर होगा)

  • नया बनाओ UI Panel
  • anchorनीचे-बाएँ सेट करें
  • width300 पर सेट करें (जैसा आप चाहें)
  • पैनल में एक नया घटक जोड़ें Vertical Layout Groupऔर Child Alignmentऊपरी-केंद्र पर सेट करें, Child Force Expandचौड़ाई के लिए (ऊंचाई नहीं)
  • एक पैनल में एक नया घटक जोड़ें Content Size Fitterऔर Vertical Fitन्यूनतम आकार पर सेट करें
  • इसे प्रीफ़ैब के रूप में सहेजें

(इस बिंदु पर हमारा पैनल एक पंक्ति में सिकुड़ जाएगा। यह सामान्य है। यह पैनल बच्चों के रूप में बटन स्वीकार करेगा, उन्हें लंबवत रूप से संरेखित करेगा और सारांश सामग्री की ऊँचाई तक ले जाएगा)

नमूना बटन बनाना

(यह बटन संदर्भ-मेनू आइटम दिखाने के लिए त्वरित और अनुकूलित होगा)

  • नया UI बटन बनाएं
  • anchorऊपरी-बाएँ सेट करें
  • बटन को एक नया घटक में जोड़ें Layout Element, Min Height30 पर सेट , Preferred Height30 तक
  • इसे प्रीफ़ैब के रूप में सहेजें

ContextMenu.cs स्क्रिप्ट बनाना

(इस स्क्रिप्ट में एक विधि है जो संदर्भ मेनू बनाती है और दिखाती है)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[System.Serializable]
public class ContextMenuItem
{
    // this class - just a box to some data

    public string text;             // text to display on button
    public Button button;           // sample button prefab
    public Action<Image> action;    // delegate to method that needs to be executed when button is clicked

    public ContextMenuItem(string text, Button button, Action<Image> action)
    {
        this.text = text;
        this.button = button;
        this.action = action;
    }
}

public class ContextMenu : MonoBehaviour
{
    public Image contentPanel;              // content panel prefab
    public Canvas canvas;                   // link to main canvas, where will be Context Menu

    private static ContextMenu instance;    // some kind of singleton here

    public static ContextMenu Instance
    {
        get
        {
            if(instance == null)
            {
                instance = FindObjectOfType(typeof(ContextMenu)) as ContextMenu;
                if(instance == null)
                {
                    instance = new ContextMenu();
                }
            }
            return instance;
        }
    }

    public void CreateContextMenu(List<ContextMenuItem> items, Vector2 position)
    {
        // here we are creating and displaying Context Menu

        Image panel = Instantiate(contentPanel, new Vector3(position.x, position.y, 0), Quaternion.identity) as Image;
        panel.transform.SetParent(canvas.transform);
        panel.transform.SetAsLastSibling();
        panel.rectTransform.anchoredPosition = position;

        foreach(var item in items)
        {
            ContextMenuItem tempReference = item;
            Button button = Instantiate(item.button) as Button;
            Text buttonText = button.GetComponentInChildren(typeof(Text)) as Text;
            buttonText.text = item.text;
            button.onClick.AddListener(delegate { tempReference.action(panel); });
            button.transform.SetParent(panel.transform);
        }
    }
}
  • इस स्क्रिप्ट को कैनवस पर अटैच करें और खेतों को आबाद करें। ड्रैग-एन-ड्रॉप ContentPanelप्रीफैब को संबंधित स्लॉट में, और कैनवस को स्लॉट में खींचें Canvas

ItemController.cs स्क्रिप्ट बना रहा है

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ItemController : MonoBehaviour
{
    public Button sampleButton;                         // sample button prefab
    private List<ContextMenuItem> contextMenuItems;     // list of items in menu

    void Awake()
    {
        // Here we are creating and populating our future Context Menu.
        // I do it in Awake once, but as you can see, 
        // it can be edited at runtime anywhere and anytime.

        contextMenuItems = new List<ContextMenuItem>();
        Action<Image> equip = new Action<Image>(EquipAction);
        Action<Image> use = new Action<Image>(UseAction);
        Action<Image> drop = new Action<Image>(DropAction);

        contextMenuItems.Add(new ContextMenuItem("Equip", sampleButton, equip));
        contextMenuItems.Add(new ContextMenuItem("Use", sampleButton, use));
        contextMenuItems.Add(new ContextMenuItem("Drop", sampleButton, drop));
    }

    void OnMouseOver()
    {
        if(Input.GetMouseButtonDown(1))
        {
            Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
            ContextMenu.Instance.CreateContextMenu(contextMenuItems, new Vector2(pos.x, pos.y));
        }

    }

    void EquipAction(Image contextPanel)
    {
        Debug.Log("Equipped");
        Destroy(contextPanel.gameObject);
    }

    void UseAction(Image contextPanel)
    {
        Debug.Log("Used");
        Destroy(contextPanel.gameObject);
    }

    void DropAction(Image contextPanel)
    {
        Debug.Log("Dropped");
        Destroy(contextPanel.gameObject);
    }
}
  • दृश्य में नमूना वस्तु बनाएं (यानी Cube), इसे कैमरे में दिखाई देने के लिए रखें और इसे इस स्क्रिप्ट को संलग्न करें। ड्रैग-एन-ड्रॉप sampleButtonप्रीफैब को संबंधित स्लॉट में।

अब, इसे चलाने का प्रयास करें। जब आप ऑब्जेक्ट को राइट-क्लिक कर रहे हैं, तो संदर्भ मेनू दिखाई देनी चाहिए, जो हमारे द्वारा बनाई गई सूची के साथ पॉपुलेटेड है। बटन दबाने से कंसोल कुछ टेक्स्ट में प्रिंट हो जाएगा, और संदर्भ मेनू नष्ट हो जाएगा।

संभावित सुधार:

  • और भी सामान्य!
  • बेहतर मेमोरी मैनेजमेंट (गंदे लिंक, पैनल को नष्ट नहीं करना, अक्षम करना)
  • कुछ फैंसी सामान

नमूना परियोजना (एकता व्यक्तिगत 5.2.0, विज़ुअलस्टैडियो प्लगइन): https://drive.google.com/file/d/0B7iGjyVbWvFwUnRQRVaOGdDc2M/view?usp/sharing


इसे लागू करने के लिए अपने दिन का समय निकालने के लिए वाह बहुत-बहुत धन्यवाद। जैसे ही मैं अपने कंप्यूटर पर वापस आऊंगा, मैं आपके कार्यान्वयन का परीक्षण करूंगा। मैं स्पष्टीकरण के प्रयोजनों के लिए सोचता हूं, मैं फिलिप के उत्तर को विभिन्न तरीकों के स्पष्टीकरण के आधार पर स्वीकार करूंगा, जिनका उपयोग किया जा सकता है। मैं आपके उत्तर को यहां छोड़ दूंगा क्योंकि मेरा मानना ​​है कि यह अत्यंत मूल्यवान है और भविष्य में इस प्रश्न को देखने वाले लोगों के पास वास्तविक कार्यान्वयन के साथ-साथ किसी खेल में इस तरह की चीज को लागू करने के लिए कुछ तरीके होंगे। बहुत बहुत धन्यवाद और अच्छा किया। मैंने भी यह मतदान किया है :)
माइक हंट

1
आपका स्वागत है। यह बहुत अच्छा होगा अगर यह जवाब किसी की मदद करेगा।
निर्गमन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.