एनिमेशन और ड्रा लूप से गेम लॉजिक को अलग करने के कुछ तरीके क्या हैं?


9

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

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

मैं इस मानसिकता से कैसे बाहर निकल सकता हूं और उन पैटर्नों के बारे में सोचना शुरू कर सकता हूं जो खेलों के लिए अधिक मायने रखते हैं?

जवाबों:


6

एनिमेशन को अभी भी तर्क और प्रतिपादन के बीच पूरी तरह से विभाजित किया जा सकता है। एनीमेशन की सार डेटा स्थिति वह जानकारी होगी जो आपके ग्राफिक्स एपीआई के लिए एनीमेशन को रेंडर करने के लिए आवश्यक है।

उदाहरण के लिए 2 डी गेम में, यह एक आयत क्षेत्र हो सकता है जो उस क्षेत्र को चिह्नित करता है जो आपकी स्प्राइट शीट के वर्तमान भाग को प्रदर्शित करता है जिसे खींचने की आवश्यकता होती है (जब आपके पास एक शीट होती है जिसमें 30 से 808080 चित्र होते हैं, जिसमें आपके चरित्र के विभिन्न चरण होते हैं कूदना, बैठना, आगे बढ़ना आदि)। यह किसी भी प्रकार का डेटा भी हो सकता है जिसे आपको रेंडर करने की आवश्यकता नहीं है, लेकिन शायद एनीमेशन के प्रबंधन के लिए खुद को बताता है, जैसे कि वर्तमान एनीमेशन चरण समाप्त होने तक का समय बचा है या एनीमेशन का नाम ("चलना", "खड़ा है") आदि) उस सभी का प्रतिनिधित्व किसी भी तरह से आप चाहते हैं। यही लॉजिक पार्ट है।

रेंडरिंग भाग में, आप इसे हमेशा की तरह करते हैं, अपने मॉडल से उस आयत को प्राप्त करें और ग्राफिक्स रेंडर को वास्तव में कॉल करने के लिए अपने रेंडरर का उपयोग करें।

कोड में (C ++ सिंटैक्स का उपयोग करके):

class Sprite //Model
{
    private:
       Rectangle subrect;
       Vector2f position;
       //etc.

    public:
       Rectangle GetSubrect() 
       {
           return subrect;
       }
       //etc.
};

class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
    AnimationController animation_controller;
    //etc.
    public:
        void Update()
        {
            animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
            this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
        }
        //etc.
};

वह डेटा है। आपका रेंडरर उस डेटा को ले जाएगा और उसे आकर्षित करेगा। चूंकि सामान्य स्प्राइट्स और एनिमेटेड दोनों समान हैं, इसलिए आप यहां बहुरूपता का उपयोग कर सकते हैं!

class Renderer
{
    //etc.
    public:
       void Draw(const Sprite &spr)
       {
           graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
       }
};

TMV:

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

दुनिया का नक्शा सीधे खिलाड़ियों की स्थिति के बारे में नहीं जानता है (इसमें वेक्टर 2 एफ या ऐसा कुछ नहीं है जो सीधे खिलाड़ियों की स्थिति को स्टोर करता है =, इसके बजाय इसका सीधा संदर्भ खिलाड़ी ऑब्जेक्ट के लिए है, जो बदले में एनिमेटेडड्रेसप्रिट से प्राप्त होता है। इसलिए आप इसे रेंडरर को आसानी से पास कर सकते हैं, और इससे सभी आवश्यक डेटा प्राप्त कर सकते हैं।

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

जब खिलाड़ी चलता है तो आप मॉडल को कैसे अपडेट करना चाहते हैं यह इस बात पर निर्भर करता है कि आप क्या करना चाहते हैं।

क्या आपके खिलाड़ी को स्वतंत्र रूप से टाइलों (ज़ेल्डा शैली) के बीच जाने की अनुमति है? बस इनपुट को संभालें और खिलाड़ी को हर फ्रेम के अनुसार आगे बढ़ाएं। या क्या आप चाहते हैं कि खिलाड़ी "दाएं" दबाए और आपका चरित्र स्वचालित रूप से एक टाइल को दाईं ओर ले जाए? अपने RPGMap वर्ग को अपने पद पर पहुंचने तक खिलाड़ियों की स्थिति को अलग करने दें और इस बीच सभी आंदोलन-कुंजी इनपुट हैंडलिंग को लॉक करें।

किसी भी तरह से, यदि आप इसे अपने आप पर आसान बनाना चाहते हैं, तो आपके सभी मॉडलों में अपडेट () तरीके होंगे यदि उन्हें वास्तव में खुद को अपडेट करने के लिए कुछ तर्क की आवश्यकता है (केवल चर के मूल्यों को बदलने के बजाय) - आप नियंत्रक को दूर नहीं करते हैं इस तरह से एमवीसी पैटर्न में, आप कोड को "एक कदम ऊपर" (नियंत्रक) से मॉडल में नीचे ले जाते हैं, और सभी नियंत्रक इस अद्यतन को कॉल करते हैं मॉडल का तरीका (हमारे मामले में नियंत्रक होगा) The RPGMap)। आप अभी भी लॉगिक्स कोड को आसानी से स्वैप कर सकते हैं - आप सीधे कक्षा के कोड को बदल सकते हैं या यदि आपको पूरी तरह से अलग व्यवहार की आवश्यकता है, तो आप केवल अपने मॉडल वर्ग से प्राप्त कर सकते हैं और केवल अपडेट () विधि को ओवरराइड कर सकते हैं।

यह तरीका मेथड कॉल और उस तरह की चीजों को कम करता है - जो कि शुद्ध MVC पैटर्न के मुख्य नुकसानों में से एक हुआ करता था (आप GetThis () GetThat () बहुत बार कॉल करना समाप्त करते हैं) - यह कोड को लंबा और एक दोनों बनाता है पढ़ने में थोड़ा कठिन और धीमा भी - भले ही आपके कंपाइलर द्वारा इस बात का ध्यान रखा जाए जो इस तरह के बहुत सारे सामान का अनुकूलन करता है।


क्या आप गेम लॉजिक वाले क्लास में एनीमेशन डेटा रखेंगे, गेम लूप वाली क्लास, या दोनों से अलग? इसके अलावा, यह पूरी तरह से लूप या क्लास से युक्त है, यह समझने के लिए कि एनीमेशन डेटा को वास्तव में स्क्रीन पर आरेखित करने के तरीके का अनुवाद कैसे करें? यह अक्सर उतना सरल नहीं होगा जितना कि केवल एक परत प्राप्त करना जो स्प्राइट शीट के एक भाग का प्रतिनिधित्व करता है और जो कि स्प्राइट शीट से बिटमैप ड्रॉ को क्लिप करने के लिए उपयोग करता है।
TMV

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

मैंने आपके प्रश्न के उत्तर में संपादित किया, क्योंकि टिप्पणियां इसके लिए पर्याप्त वर्णों की अनुमति नहीं देती हैं।
ट्रैविसग

अगर मैं सब कुछ सही तरीके से
समझूं

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

2

यदि आप चाहें तो मैं इस पर विकास कर सकता हूं, लेकिन मेरे पास एक केंद्रीय रेंडर है जो लूप में आकर्षित करने के लिए कहा जाता है। बजाय

handle input

for every entity:
    update entity

for every entity:
    draw entity

मेरे पास एक सिस्टम अधिक है

handle input (well, update the state. Mine is event driven so this is null)

for every entity:
    update entity //still got game logic here

renderer.draw();

रेंडरर वर्ग केवल वस्तुओं के ड्रॉबल घटकों के संदर्भों की एक सूची रखता है। इन्हें निर्माणकर्ताओं को सादगी के लिए सौंपा गया है।

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

फ़्रेम स्वतंत्र एनीमेशन को ड्रॉ लूप के साथ करने के लिए बहुत अधिक आवश्यकता नहीं होनी चाहिए।


0

मैं पिछले कुछ समय से खुद के प्रतिमानों को सीख रहा हूं, इसलिए यदि यह उत्तर अधूरा है, तो मुझे यकीन है कि कोई इसे जोड़ देगा।

गेम डिज़ाइन के लिए सबसे अधिक समझ में आने वाली कार्यप्रणाली तर्क को स्क्रीन आउटपुट से अलग करना है।

ज्यादातर मामलों में आप एक बहु थ्रेडेड दृष्टिकोण का उपयोग करना चाहते हैं, यदि आप उस विषय से परिचित नहीं हैं, तो यह अपने आप में एक सवाल है, यहाँ विकी प्राइमर है । अनिवार्य रूप से आप चाहते हैं कि आपका गेम लॉजिक एक थ्रेड में निष्पादित हो, जिसमें वैरिएबल को लॉक किया जा सके ताकि डेटा अखंडता सुनिश्चित करने के लिए इसे एक्सेस करने की आवश्यकता हो। यदि आपका लॉजिक लूप अविश्वसनीय रूप से तेज़ है (सुपर मेगा एनिमेटेड 3 डी पोंग?) तो आप छोटी अवधि के लिए थ्रेड को सोते हुए लूप निष्पादित करने की आवृत्ति को ठीक करने की कोशिश कर सकते हैं (गेम भौतिकी लूप के लिए इस फ़ोरम में 120 हर्ट्ज का सुझाव दिया गया है)। समांतर रूप से, अपडेट किए गए चरों के साथ दूसरे थ्रेड स्क्रीन को फिर से खोलना (60 हर्ट्ज अन्य विषयों में सुझाया गया है), फिर से वैरिएबल पर लॉक लगाने के लिए कहने से पहले उन्हें एक्सेस करें।

उस स्थिति में, एनिमेशन या संक्रमण, आदि, ड्राइंग थ्रेड में जाते हैं, लेकिन आपको किसी प्रकार के झंडे के माध्यम से संकेत देना चाहिए (एक वैश्विक राज्य चर शायद) कि गेम लॉजिक थ्रेड कुछ भी नहीं करना चाहिए (या कुछ अलग करना ... सेटअप) नए नक्शे पैरामीटर शायद)।

एक बार जब आप अपना सिर कंसीडर के चारों ओर कर लेते हैं, तो बाकी काफी समझ में आता है। यदि आपके पास संगामिति के साथ अनुभव नहीं है, तो मैं दृढ़ता से आपको कुछ सरल परीक्षण कार्यक्रम लिखने का सुझाव देता हूं ताकि आप समझ सकें कि प्रवाह कैसे होता है।

उम्मीद है की यह मदद करेगा :)

[संपादित करें] उन सिस्टमों पर जो मल्टीथ्रेडिंग का समर्थन नहीं करते हैं, एनीमेशन अभी भी ड्रॉ लूप में जा सकता है, लेकिन आप राज्य को इस तरह सेट करना चाहते हैं कि लॉजिक को संकेत मिले कि कुछ अलग हो रहा है और प्रोसेसिंग नहीं रखें वर्तमान स्तर / नक्शा / आदि ...


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

@ TheCununistDuck फेयर पर्याप्त, ओवरहेड और मल्टीथ्रेडिंग के लिए जटिलता निश्चित रूप से इसे ओवरकिल बना सकती है, साथ ही अगर खेल छोटा है, तो यह जल्दी से अपडेट करने में सक्षम होना चाहिए।
स्टीफन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.