घटक आधारित इकाई प्रणाली के लिए कौन से डिज़ाइन हैं जो उपयोगकर्ता के अनुकूल हैं लेकिन फिर भी लचीले हैं?


23

मैं थोड़ी देर के लिए घटक आधारित इकाई प्रणाली में दिलचस्पी किया गया है, और उस पर अनगिनत लेख पढ़ा ( Insomiac खेल , सुंदर मानक Evolve अपने पदानुक्रम , टी मशीन , Chronoclast ... बस कुछ नाम हैं)।

वे सभी कुछ के बाहर की तरह एक संरचना है लगता है:

Entity e = Entity.Create();
e.AddComponent(RenderComponent, ...);
//do lots of stuff
e.GetComponent<PositionComponent>(...).SetPos(4, 5, 6);

और यदि आप साझा किए गए डेटा के विचार में लाते हैं (यह सबसे अच्छा डिज़ाइन है जो मैंने अब तक देखा है, हर जगह डेटा की नकल नहीं करने के मामले में)

e.GetProperty<string>("Name").Value = "blah";

हां, यह बहुत कुशल है। हालाँकि, यह पढ़ना या लिखना बिल्कुल आसान नहीं है; यह बहुत ही भद्दा और काम करने वाला-आपको लगता है।

मैं व्यक्तिगत रूप से कुछ करना चाहूंगा:

e.SetPosition(4, 5, 6);
e.Name = "Blah";

हालांकि निश्चित रूप से इस तरह के डिजाइन को प्राप्त करने का एकमात्र तरीका वापस निकाय में है-> एनपीसी-> दुश्मन-> फ्लाइंग इनेमी-> फ्लाइंगएनिमीविथाहैटऑन इस तरह के पदानुक्रम से बचने की कोशिश करता है।

क्या किसी ने इस तरह की घटक प्रणाली के लिए एक डिजाइन देखा है जो अभी भी लचीला है, फिर भी उपयोगकर्ता मित्रता का एक स्तर बनाए रखता है? और उस मामले के लिए, एक अच्छे तरीके से डेटा भंडारण के आसपास (शायद सबसे कठिन समस्या) प्राप्त करने का प्रबंधन करता है?

घटक आधारित इकाई प्रणाली के लिए कौन से डिज़ाइन हैं जो उपयोगकर्ता के अनुकूल हैं लेकिन फिर भी लचीले हैं?

जवाबों:


13

उन चीजों में से एक जो यूनिटी करती है, माता-पिता की गेम ऑब्जेक्ट पर कुछ सहायक एक्सेसर्स प्रदान करती है ताकि आम घटकों को अधिक उपयोगकर्ता के अनुकूल पहुंच प्रदान की जा सके।

उदाहरण के लिए, आपके पास एक ट्रांसफ़ॉर्म घटक में संग्रहीत स्थिति हो सकती है। अपने उदाहरण का उपयोग करते हुए आपको कुछ लिखना होगा

e.GetComponent<Transform>().position = new Vector3( whatever );

लेकिन एकता में, यह सरल हो जाता है

e.transform.position = ....;

जहाँ transformका शाब्दिक आधार GameObjectवर्ग ( Entityआपके मामले में वर्ग) में एक सरल सहायक विधि है जो करता है

Transform transform
{ 
    get { return this.GetComponent<Transform>(); }
}

एकता कुछ अन्य चीजें भी करती हैं, जैसे गेम ऑब्जेक्ट पर "नाम" संपत्ति सेट करने के बजाय अपने बच्चे के घटकों में।

व्यक्तिगत रूप से मुझे आपके साझा-डेटा-नाम के डिजाइन का विचार पसंद नहीं है। एक चर के बजाय नाम से संपत्तियों तक पहुँचने और उपयोगकर्ता को यह भी जानना होगा कि यह किस प्रकार का है, यह वास्तव में मुझे त्रुटि वाला लगता है। यूनिटी क्या करती है कि वे समान घटकों का उपयोग कक्षाओं के GameObject transformभीतर संपत्ति के रूप में Componentसिबलिंग घटकों तक पहुंचने के लिए करते हैं। तो आपके द्वारा लिखा गया कोई भी मनमाना घटक बस यही कर सकता है:

var myPos = this.transform.position;

स्थिति तक पहुँचने के लिए। जहां वह transformसंपत्ति कुछ ऐसा करती है

Transform transform
{
    get { return this.gameObject.GetComponent<Transform>(); }
}

ज़रूर, यह सिर्फ कहने से थोड़ा अधिक क्रिया है e.position = whatever, लेकिन आपको इसकी आदत है, और यह सामान्य गुणों के रूप में बुरा नहीं दिखता है। और हाँ, आपको इसे अपने क्लाइंट घटकों के लिए राउंडअबाउट करना होगा, लेकिन विचार यह है कि आपके सभी सामान्य "इंजन" घटकों (रेंडरर्स, कोलाइडर, ऑडियो स्रोत, ट्रांसफ़ॉर्म, आदि) में आसान एक्सेसर्स हैं।


मैं देख सकता हूं कि साझा-डेटा-बाय-नेम सिस्टम एक मुद्दा क्यों हो सकता है, लेकिन मैं डेटा की आवश्यकता वाले कई घटकों की समस्या से कैसे बच सकता हूं (यानी जो मैं कुछ स्टोर करता हूं)? बस डिजाइन चरण में बहुत सावधान रहना?
कम्युनिस्ट डक

इसके अलावा, 'उपयोगकर्ता को यह भी जानना होगा कि यह किस प्रकार का है', क्या मैं इसे प्रॉपर्टी में नहीं डाल सकता हूं। गेट टाइप () मेथड () विधि में?
कम्युनिस्ट डक

1
आप किस प्रकार के वैश्विक (इकाई दायरे में) डेटा इन साझा डेटा रिपॉजिटरी पर जोर दे रहे हैं? किसी भी प्रकार के गेम ऑब्जेक्ट डेटा को आपके "इंजन" कक्षाओं (ट्रांसफ़ॉर्म, कोलाइडर, रेंडरर्स, आदि) के माध्यम से आसानी से सुलभ होना चाहिए। यदि आप इस "साझा किए गए डेटा" के आधार पर गेम लॉजिक परिवर्तन कर रहे हैं, तो यह एक वर्ग के स्वयं के लिए बहुत सुरक्षित है और वास्तव में यह सुनिश्चित करता है कि घटक उस गेम ऑब्जेक्ट पर है, जो नेत्रहीन पूछने की तुलना में यह देखने के लिए है कि क्या कोई नामित चर मौजूद है। तो हां, आपको इसे डिजाइन चरण में संभालना चाहिए। आप हमेशा एक घटक बना सकते हैं जो सिर्फ डेटा संग्रहीत करता है यदि आपको वास्तव में इसकी आवश्यकता है।
तेतराड

@Tetrad - क्या आप इस बात पर विस्तार से जानकारी दे सकते हैं कि आपका प्रस्तावित गेटकॉमपेंटेंट <ट्रांसफॉर्म> विधि कैसे काम करेगी? यह GameObject के सभी अवयवों के माध्यम से लूप करेगा और टाइप टी के पहले घटक को वापस करेगा? या वहाँ कुछ और चल रहा है?
माइकल

@ मिचेल जो काम करेगा। आप इसे कॉल करने वाले को जिम्मेदारी दे सकते हैं कि वह स्थानीय स्तर पर प्रदर्शन में सुधार करे।
टेट्राद

3

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

एक उच्च स्तर पर बहुत बुनियादी, वर्ग इकाई को सवाल में लेता है और निम्नानुसार घटक भागों को आसान-से-उपयोग इंटरफ़ेस प्रदान करता है:

public class EntityInterface
{
    private Entity entity { get; set };
    public EntityInterface(Entity e)
    {
        entity = e;
    }

    public Vector3 Position
    {
        get
        {
            return entity.GetProperty<Vector3>("Position");
        }
        set
        {
            entity.GetProperty<Vector3>("Position") = value;
        }
    }
}

अगर मुझे लगता है कि मुझे सिर्फ NoPositionComponentException या किसी चीज़ के लिए संभालना होगा, तो इस बात का क्या फायदा है कि सिर्फ Entity क्लास में ही सब कुछ डाल दिया जाए?
कम्युनिस्ट बतख

अनिश्चित होने के नाते आपके इकाई वर्ग में वास्तव में मैं यह नहीं कह सकता कि कोई फायदा है या नहीं। मैं व्यक्तिगत रूप से घटक प्रणालियों की वकालत करता हूं, जहां ate खैर इसमें क्या निहित है ’के सवाल से बचने के लिए बस प्रति-आधार कोई इकाई नहीं है। हालाँकि मैं इस तरह के एक इंटरफ़ेस वर्ग को मौजूदा के लिए एक अच्छा तर्क मानूंगा।
जेम्स

मैं देख सकता हूं कि यह कैसे आसान है (मुझे गेटप्रॉपर के माध्यम से स्थिति को क्वेरी करने की आवश्यकता नहीं है), लेकिन मुझे एंटिटी क्लास में रखने के बजाय इस तरह से फायदा नहीं दिखता है।
कम्युनिस्ट डक

2

पायथन में आप एंटिटी पर e.SetPosition(4,5,6)एक __getattr__समारोह की घोषणा के माध्यम से 'सेटपोजिशन' के हिस्से को रोक सकते हैं । यह फ़ंक्शन घटकों के माध्यम से पुनरावृत्ति कर सकता है और उचित विधि या संपत्ति ढूंढ सकता है और वापस लौट सकता है, ताकि फ़ंक्शन कॉल या असाइनमेंट सही जगह पर जाए। शायद C # में एक समान प्रणाली है, लेकिन यह संभव नहीं है कि इसे सांख्यिकीय रूप से टाइप करने के कारण - यह संभवत: संकलित नहीं कर सकता है e.setPositionजब तक कि ई ने कहीं इंटरफ़ेस में सेट न किया हो।

आप शायद अपने सभी संस्थाओं को उन सभी घटकों के लिए प्रासंगिक इंटरफेस को लागू कर सकते हैं जिन्हें आप कभी भी जोड़ सकते हैं, स्टब विधियों के साथ जो एक अपवाद को बढ़ाते हैं। फिर जब आप addComponent को कॉल करते हैं, तो आप उस घटक के इंटरफ़ेस के घटक के लिए इकाई के कार्यों को पुनर्निर्देशित करते हैं। हालांकि थोड़ा फिजूल।

लेकिन शायद सबसे आसान होगा अपने वैधता की उपस्थिति के लिए संलग्न घटकों की खोज करने और वापस लौटने के लिए अपने इकाई वर्ग पर [] ऑपरेटर को अधिभारित करना, ताकि इसे इस तरह से सौंपा जा सके e["Name"] = "blah":। प्रत्येक घटक को अपने स्वयं के GetPropertyByName को लागू करने की आवश्यकता होगी और इकाई प्रत्येक को बारी-बारी से बुलाती है जब तक कि वह प्रश्न में संपत्ति के लिए जिम्मेदार घटक को नहीं पाता है।


मैंने इंडेक्सर्स का उपयोग करने के बारे में सोचा था..यह वास्तव में अच्छा है। मैं यह देखने के लिए थोड़ा इंतजार करूंगा कि और क्या प्रतीत होता है, लेकिन मैं इसके मिश्रण की ओर बढ़ रहा हूं, सामान्य गेटकॉमपेंटेंट (), और कुछ गुणों जैसे @ जेम्स ने सामान्य घटकों के लिए सुझाव दिया है।
कम्युनिस्ट डक

मुझे याद आ रहा है कि यहाँ क्या कहा जा रहा है, लेकिन यह e.GetProperty <type> ("NameOfProperty") के प्रतिस्थापन के लिए अधिक लगता है। जो कुछ भी ई के साथ है।
जेम्स

@ जेम्स यह इसके लिए एक आवरण है (आपके डिजाइन की तरह) बहुत अधिक है .. सिवाय इसके कि यह अधिक गतिशील है - यह स्वचालित रूप से और GetPropertyविधि की तुलना में क्लीनर करता है। केवल नकारात्मक पक्ष स्ट्रिंग हेरफेर और तुलना है। लेकिन वह एक मूट प्वाइंट है।
कम्युनिस्ट डक

आह, मेरी गलतफहमी वहाँ। मैंने माना कि गेटप्रॉपर्टी इकाई का हिस्सा था (और ऐसा ही ऊपर वर्णित है) और सी # विधि नहीं होगी।
जेम्स

2

Kylotan के उत्तर पर विस्तार करने के लिए, यदि आप C # 4.0 का उपयोग कर रहे हैं, तो आप स्टेटिक रूप से डायनामिक होने के लिए एक वेरिएबल टाइप कर सकते हैं ।

यदि आप System.Dynamic.DynamicObject से इनहेरिट करते हैं, तो आप TryGetMember और TrySetMember (कई वर्चुअल विधियों के बीच) को घटक नामों को इंटरसेप्ट कर सकते हैं और अनुरोधित घटक को वापस कर सकते हैं।

dynamic entity = EntityRegistry.Entities[1000];
entity.MyComponent.MyValue = 100;

मैंने वर्षों में इकाई प्रणालियों के बारे में थोड़ा लिखा है, लेकिन मुझे लगता है कि मैं दिग्गजों के साथ प्रतिस्पर्धा नहीं कर सकता। कुछ ईएस नोट (कुछ हद तक पुराने हैं और यह जरूरी नहीं कि मैं इकाई प्रणालियों की वर्तमान समझ को प्रतिबिंबित करूं, लेकिन यह पढ़ने लायक है, इम्हो)।


इसके लिए धन्यवाद, मदद करेगा :) क्या गतिशील तरीके से सिर्फ उसी तरह का प्रभाव नहीं होगा जैसा कि प्रॉपर्टी को कास्टिंग करना है। गेट टाइप (), हालांकि?
कम्युनिस्ट बतख

कुछ बिंदु पर एक कलाकार होगा, क्योंकि TryGetMember का एक आउट objectपैरामीटर है - लेकिन यह सब रनटाइम पर हल हो जाएगा। मुझे लगता है कि यह एक धाराप्रवाह इंटरफ़ेस रखने का एक शानदार तरीका है, जैसे कि एंटिटी .TryGetComponent (compName, आउट कंपोनेंट)। मुझे यकीन नहीं है कि मैं इस सवाल को समझ गया, लेकिन :)
राईन

प्रश्न यह है कि मैं हर जगह गतिशील प्रकारों का उपयोग कर सकता हूं, या (AFAICS) रिटर्निंग (संपत्ति ।GetType ()) संपत्ति।
कम्युनिस्ट बतख

1

मैं यहाँ ES पर C # में बहुत रुचि देखता हूं। उत्कृष्ट ES कार्यान्वयन आर्टेमिस के मेरे पोर्ट को देखें:

https://github.com/thelinuxlich/artemis_CSharp

इसके अलावा, एक उदाहरण गेम जो आपको शुरू करने के लिए है कि यह कैसे काम करता है (XNA 4 का उपयोग करके): https://github.com/tilianuxlich/starwarrior_CSharp

सुझावों का स्वागत है!


निश्चित रूप से अच्छा लग रहा है। मैं व्यक्तिगत रूप से इतने सारे EntityProcessingSystems के विचार का प्रशंसक नहीं हूं - कोड में, उपयोग के संदर्भ में यह कैसे काम करता है?
कम्युनिस्ट डक

हर सिस्टम एक पहलू है जो उसके आश्रित अवयवों को संसाधित करता है। इस विचार को प्राप्त करने के लिए इसे देखें: github.com/tilinuxlich/starwarrior_CSharp/tree/master/…
thelinuxlich

0

मैंने C # उत्कृष्ट प्रतिबिंबन प्रणाली का उपयोग करने का निर्णय लिया। मैंने इसे सामान्य घटक प्रणाली से दूर रखा, साथ ही यहाँ उत्तरों का मिश्रण भी।

घटकों को बहुत आसानी से जोड़ा जाता है:

Entity e = new Entity(); //No templates/archetypes yet.
e.AddComponent(new HealthComponent(100));

और गुणों के आधार पर या तो नाम, प्रकार, या (यदि स्वास्थ्य और स्थिति की तरह सामान्य वाले) के द्वारा उद्धृत किया जा सकता है:

e["Health"] //This, unfortunately, is a bit slower since it requires a dict search
e.GetComponent<HealthComponent>()
e.Health

और हटा दिया गया:

e.RemoveComponent<HealthComponent>();

डेटा, फिर से, आमतौर पर संपत्तियों द्वारा पहुँचा जाता है:

e.MaxHP

जो संग्रहीत घटक-पक्ष है; कम ओवरहेड अगर इसमें एचपी नहीं है:

public int? MaxHP
{
get
{

    return this.Health.MaxHP; //Error handling returns null if health isn't here. Excluded for clarity
}
set
{
this.Health.MaxHP = value;
}

वी.एस. में Intellisense द्वारा बड़ी मात्रा में टाइपिंग की गई है, लेकिन अधिकांश भाग के लिए बहुत कम टाइपिंग है - वह चीज जिसकी मुझे तलाश थी।

पर्दे के पीछे, मैं एक भंडारण कर रहा हूं Dictionary<Type, Component>और नाम की जांच के लिए विधि को Componentsओवरराइड कर रहा हूं ToString। मेरे मूल डिजाइन में मुख्य रूप से नाम और चीजों के लिए तार का उपयोग किया गया था, लेकिन यह काम करने के लिए एक सुअर बन गया (ओह देखो, मुझे हर जगह कास्ट करना है और साथ ही गुणों को एक्सेस करना आसान है)।

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