मैं विशाल खिलाड़ी वर्गों से कैसे बच सकता हूं?


46

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


26
कई फाइलें या एक फाइल, कोड कहीं जाना है। खेल जटिल हैं। आपको क्या चाहिए, यह जानने के लिए, अच्छी विधि के नाम और वर्णनात्मक टिप्पणियां लिखें। परिवर्तन करने से डरें नहीं - बस परीक्षण करें। और अपने काम का बैकअप लें :)
क्रिस मैकफारलैंड

7
मुझे लगता है कि इसे कहीं और जाना है लेकिन लचीलेपन और रखरखाव में कोड डिज़ाइन मायने रखता है। हजारों पंक्तियों वाली एक कक्षा या समूह का समूह होना मुझे या तो हड़ताल नहीं करता है।
user441521

17
@ChrisMcFarland बैकअप का सुझाव नहीं देता, संस्करण XD का सुझाव दें।
GameDeveloper

1
@ChrisMcFarland मैं GameDeveloper से सहमत हूं। Git, svn, TFS, ... जैसे संस्करण नियंत्रण होने के कारण, विकास को इतना आसान बना देता है कि यह बड़े बदलावों को आसानी से पूर्ववत करने में सक्षम होने के कारण और आसानी से अपने प्रोजेक्ट को हटाने, हार्डवेयर की विफलता या फ़ाइल भ्रष्टाचार जैसी चीज़ों से आसानी से उबरने में सक्षम होता है।
नजला

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

जवाबों:


67

आप आमतौर पर एक इकाई घटक प्रणाली का उपयोग करेंगे (एक इकाई घटक प्रणाली एक घटक आधारित वास्तुकला है)। यह अन्य संस्थाओं को भी आसान बनाता है, और दुश्मन / NPCs के खिलाड़ी के समान घटक भी बना सकता है।

यह दृष्टिकोण एक वस्तु उन्मुख दृष्टिकोण के रूप में सटीक विपरीत दिशा में जाता है। खेल में सब कुछ एक इकाई है। यह इकाई बिना किसी खेल यांत्रिकी के एक मामला है। इसमें घटकों की एक सूची है और उन्हें हेरफेर करने का एक तरीका है।

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

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

यह कोड को छोटे, पुन: प्रयोज्य मॉड्यूल में तोड़ने में मदद करता है, और इसके परिणामस्वरूप अधिक संगठित परियोजना हो सकती है।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
MichaelHouse

8
जब मैं चलती टिप्पणियों को समझता हूं जिन्हें स्थानांतरित करने की आवश्यकता होती है, तो उन लोगों को स्थानांतरित न करें जो उत्तर की सटीकता को चुनौती देते हैं। यह स्पष्ट होना चाहिए, नहीं?
बग-ए-लॉट

20

खेल इस में अद्वितीय नहीं हैं; देव-वर्ग हर जगह एक विरोधी स्वरूप हैं।

एक सामान्य समाधान यह है कि छोटी कक्षाओं के पेड़ में बड़ी कक्षा को तोड़ दिया जाए। यदि खिलाड़ी के पास इन्वेंट्री है, तो इन्वेंट्री प्रबंधन का हिस्सा न बनाएं class Player। इसके बजाय, बनाएँ class Inventory। यह एक सदस्य है class Player, लेकिन आंतरिक रूप से class Inventoryबहुत सारे कोड लपेट सकता है।

एक अन्य उदाहरण: एक खिलाड़ी के चरित्र के एनपीसी के साथ संबंध हो सकते हैं, इसलिए आपके पास ऑब्जेक्ट और ऑब्जेक्ट class Relationदोनों का संदर्भ हो सकता है , लेकिन दोनों से संबंधित नहीं।PlayerNPC


हाँ, मैं बस यह कैसे करना है पर विचारों की तलाश कर रहा था। मानसिकता क्या थी क्योंकि कार्यक्षमता के उन छोटे टुकड़ों को तोड़ने के लिए, वैसे भी मेरे लिए यह स्वाभाविक नहीं है, कोडिंग करते हुए बहुत कम टुकड़े हैं। हालांकि यह स्पष्ट हो जाता है कि कार्यक्षमता के उन सभी छोटे टुकड़े खिलाड़ी वर्ग को विशाल बनाने के लिए शुरू करते हैं।
user441521

1
लोग आमतौर पर कहते हैं कि कुछ ईश्वर वर्ग या ईश्वर वस्तु है, जब इसमें खेल शामिल होता है और खेल में हर दूसरे वर्ग / वस्तु का प्रबंधन करता है।
बैलिंट

11

1) खिलाड़ी: राज्य-मशीन + घटक आधारित वास्तुकला।

खिलाड़ी के लिए सामान्य घटक: हेल्थ सिस्टम, मूवमेंट सिस्टम, इन्वेंटरी सिस्टम, एक्शन सिस्टम। वे सभी वर्ग जैसे हैं class HealthSystem

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

स्टेट्स: लूटस्टेट, रनस्टेट, वॉकस्टेट, अटैकस्टेट, आईडीलेट।

हर राज्य से विरासत में मिलता है interface IStateIStateहमारे मामले में सिर्फ एक उदाहरण के लिए 4 तरीके हैं।Loot() Run() Walk() Attack()

इसके अलावा, हमारे पास class InputControllerउपयोगकर्ता के हर इनपुट के लिए जांच होती है।

अब वास्तविक उदाहरण के लिए: InputControllerहम जाँचते हैं कि क्या खिलाड़ी किसी WASD or arrowsभी चीज़ को दबाता है और यदि वह दबाता है Shift। यदि वह केवल तभी दबाता है, जब WASDहम _currentPlayerState.Walk();इस पर खुशी जताते हैं और हमें उसके currentPlayerStateबराबर होना चाहिए, WalkStateतो WalkState.Walk() हमारे पास इस राज्य के लिए आवश्यक सभी घटक हैं - इस मामले में MovementSystem, इसलिए हम खिलाड़ी को स्थानांतरित करते हैं public void Walk() { _playerMovementSystem.Walk(); }- आप देखते हैं कि हमारे पास यहां क्या है? हमारे पास व्यवहार की दूसरी परत है और यह कोड रखरखाव और डिबगिंग के लिए बहुत अच्छा है।

अब दूसरे मामले में: क्या होगा अगर हमने WASD+ Shiftदबाया है? लेकिन हमारा पिछला राज्य था WalkState। इस मामले Run()में कहा जाएगा InputController(इसे मत मिलाइए, Run()इसलिए कहा जाता है क्योंकि हमारे पास WASD+ Shiftचेक इन InputControllerहोने के कारण नहीं WalkState)। जब हम कहते हैं _currentPlayerState.Run();में WalkState- हम जानते हैं कि हम स्विच करने के लिए है कि _currentPlayerStateकरने के लिए RunStateऔर हम में ऐसा Run()की WalkStateऔर इस विधि के अंदर फिर से इसे कहते है, लेकिन अब एक अलग राज्य के साथ क्योंकि हम खो कार्रवाई करने के लिए इस फ्रेम नहीं करना चाहती। और अब निश्चित रूप से हम कहते हैं _playerMovementSystem.Run();

लेकिन क्या होगा LootStateजब खिलाड़ी अनिल्ट नहीं कर सकता है या अनफिल्ट नहीं कर सकता है जब वह बटन रिलीज करता है? खैर इस मामले में जब हमने लूटपाट शुरू की थी, उदाहरण के लिए जब बटन Eदबाया गया था तो हम कॉल _currentPlayerState.Loot();करते हैं जिसे हम स्विच करते हैं LootStateऔर अब वहां से कॉल किया जाता है। वहाँ उदाहरण के लिए कॉल कोलॉशन विधि को प्राप्त करने के लिए अगर रेंज में कुछ लूटना है। और हम कॉरआउट को कहते हैं, जहां हमारे पास एक एनीमेशन है या जहां हम इसे शुरू करते हैं और यह भी जांचते हैं कि क्या खिलाड़ी अभी भी बटन रखता है, अगर कॉरआउट नहीं टूटता है, यदि हां तो हम उसे कॉरआउट के अंत में लूट देते हैं। लेकिन अगर खिलाड़ी दबाव डाले तो क्या होगा WASD? - _currentPlayerState.Walk();कहा जाता है, लेकिन यहाँ राज्य-मशीन के बारे में सुंदर बात है, मेंLootState.Walk()हमारे पास एक खाली तरीका है जो कुछ भी नहीं करता है या जैसा कि मैं एक सुविधा के रूप में करता हूं - खिलाड़ी कहते हैं: "अरे यार, मैंने अभी तक इसे नहीं लूटा है, क्या आप इंतजार कर सकते हैं?"। जब वह लूटपाट खत्म करता है, तो हम बदल जाते हैं IDLEState

इसके अलावा, आप एक और स्क्रिप्ट कर सकते हैं जिसे कहा जाता class BaseState : IStateहै जिसमें इन सभी डिफ़ॉल्ट तरीकों को लागू किया गया है, लेकिन उनके पास है virtualताकि आप overrideउन्हें class LootState : BaseStateकक्षाओं में टाइप कर सकें ।


घटक आधारित प्रणाली महान है, केवल एक चीज जो मुझे इसके बारे में परेशान करती है वह है उदाहरण, उनमें से कई। और यह कचरा कलेक्टर के लिए अधिक मेमोरी और काम लेता है। उदाहरण के लिए, यदि आपके पास दुश्मन के 1000 उदाहरण हैं। उन सभी के 4 घटक हैं। 1000 के बजाय 4000 ऑब्जेक्ट। एमबी यह इतना बड़ा सौदा नहीं है (मैंने प्रदर्शन परीक्षण नहीं चलाया है) अगर हम उन सभी घटकों पर विचार करते हैं जो एकता गेमबाय है।


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

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

सबसे पहले मैं यह कहने जा रहा हूं कि इन्वेंटरी, क्राफ्टिंग, मूवमेंट, बिल्डिंग को घटक आधारित होना चाहिए क्योंकि यह खिलाड़ी की जिम्मेदारी नहीं है जैसे कि तरीके हैं AddItemToInventoryArray()- हालांकि खिलाड़ी के पास एक तरीका हो सकता है जो PutItemToInventory()पिछले वर्णित विधि (2 परतों) को कॉल करेगा - हम कर सकते हैं विभिन्न परतों के आधार पर कुछ शर्तें जोड़ें)।

इमारत के साथ एक और उदाहरण। प्लेयर जैसे कुछ कह सकता है OpenBuildingWindow(), लेकिन Buildingबाकी सभी का ख्याल रखेगा, और जब उपयोगकर्ता कुछ विशिष्ट इमारत बनाने का निर्णय लेता है, तो वह खिलाड़ी को सभी आवश्यक जानकारी पास करता है Build(BuildingInfo someBuildingInfo)और खिलाड़ी इसे सभी एनिमेशन के साथ बनाना शुरू कर देता है।

ठोस - ओओपी सिद्धांत। एस - एकल जिम्मेदारी: जो हमने पिछले उदाहरणों में देखा है। हाँ ठीक है, लेकिन वंशानुक्रम कहाँ है?

यहाँ: स्वास्थ्य और खिलाड़ी की अन्य विशेषताओं को किसी अन्य संस्था द्वारा नियंत्रित किया जाना चाहिए? मुझे नहीं लगता। स्वास्थ्य के बिना कोई खिलाड़ी नहीं हो सकता है, अगर कोई एक है, तो हम सिर्फ विरासत में नहीं हैं। उदाहरण के लिए, हमारे पास IDamagable, LivingEntity, IGameActor, GameActorIDamagableबेशक है TakeDamage()

class LivinEntity : IDamagable {

   private float _health; // For fields that are the same between Instances I would use Flyweight Pattern.

   public void TakeDamage() {
       ....
   }
}

class GameActor : LivingEntity, IGameActor {
    // Here goes state machine and other attached components needed.
}

class Player : GameActor {
   // Inventory, Building, Crafting.... components.
}

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

OrganicBuilding : Building, TechBuilding : Building। आपको सामान्य संचालन या भवन के गुणों के लिए 2 घटक बनाने और दो बार कोड लिखने की आवश्यकता नहीं है। और फिर उन्हें अलग तरह से जोड़ें, आप विरासत की शक्ति और बाद में बहुरूपता और गुप्तता की शक्ति का उपयोग कर सकते हैं।


मैं बीच में कुछ का उपयोग करने का सुझाव दूंगा। और घटकों का उपयोग न करें।


मैं गेम प्रोग्रामिंग पैटर्न के बारे में इस पुस्तक को पढ़ने की अत्यधिक सलाह देता हूं - यह WEB पर मुफ्त है।


मैं आज रात बाद में खुदाई करूँगा लेकिन FYI करें मैं एकता का उपयोग नहीं कर रहा हूँ इसलिए मुझे कुछ को समायोजित करना होगा जो ठीक है।
user441521

ओह, मुझे लगता है कि यहाँ एक एकता टैग था, मेरा बुरा। केवल एक चीज मोनोहेवियर है - यह एकता संपादक में दृश्य पर हर उदाहरण के लिए सिर्फ एक आधार वर्ग है। जैसा कि Physics.OverlapSphere () के लिए है - यह एक ऐसा तरीका है जो फ्रेम के दौरान एक गोले कोलाइडर बनाता है और जांचता है कि वह क्या छूता है। Coroutines नकली अपडेट की तरह हैं, खिलाड़ियों की पीसी पर एफपीएस की तुलना में उनकी कॉल को कम मात्रा में कम किया जा सकता है - प्रदर्शन के लिए अच्छा। प्रारंभ () - बस एक विधि जिसे एक बार बुलाया जाता है जब इंस्टेंस बनाया जाता है। बाकी सभी चीजों को हर जगह लागू करना चाहिए। अगले भाग में मैं एकता के साथ कुछ भी उपयोग नहीं करूंगा। Sry। आशा है कि यह कुछ स्पष्ट किया।
उम्मीदवार चंद्रमा _Max_

मैंने एकता का उपयोग किया है, इसलिए मैं इस विचार को समझता हूं। मैं लुआ का उपयोग कर रहा हूं जिसमें कोरटाइन है और साथ ही चीजों का भी अच्छी तरह से अनुवाद होना चाहिए।
user441521

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

@CandidMoon हाँ, यह बेहतर है।
चरण

4

इस समस्या के लिए कोई चांदी की गोली नहीं है, लेकिन विभिन्न दृष्टिकोण हैं, जिनमें से लगभग सभी 'चिंताओं को अलग करने' के सिद्धांत के आसपास घूमते हैं। अन्य उत्तरों ने पहले से ही लोकप्रिय घटक-आधारित दृष्टिकोण पर चर्चा की है, लेकिन ऐसे अन्य दृष्टिकोण हैं जो घटक-आधारित समाधान के साथ या इसके बजाय उपयोग किए जा सकते हैं। मैं इकाई-नियंत्रक दृष्टिकोण पर चर्चा करने जा रहा हूं क्योंकि यह इस समस्या के लिए मेरे पसंदीदा समाधानों में से एक है।

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

सोचने का यह तरीका एक दृष्टिकोण की ओर ले जाता है जहां खिलाड़ी पात्रों, गैर खिलाड़ी पात्रों और राक्षसों / दुश्मनों सभी को Entityअलग-अलग व्यवहार किए जाने के बजाय ' एस' माना जाता है। स्वाभाविक रूप से, हालांकि, उन्हें अलग तरह से व्यवहार करना होगा - खिलाड़ी के चरित्र को इनपुट के माध्यम से नियंत्रित करना होगा और npcs को ai की आवश्यकता होगी।

इसका समाधान उन Controllerवर्गों के पास है जिनका उपयोग Entitys को नियंत्रित करने के लिए किया जाता है । ऐसा करने से, नियंत्रक में सभी भारी तर्क समाप्त हो जाते हैं और सभी डेटा और समानता इकाई में जमा हो जाती है।

इसके अलावा, और Controllerमें उप-वर्ग करके , यह खिलाड़ी को कमरे में किसी भी तरह से प्रभावी ढंग से नियंत्रित करने की अनुमति देता है । यह दृष्टिकोण एक या एक वर्ग है जो एक नेटवर्क स्ट्रीम से आदेशों के माध्यम से संचालित करके मल्टीप्लेयर के साथ मदद करता है।InputControllerAIControllerEntityRemoteControllerNetworkController

Controllerयदि आप सावधान नहीं हैं तो यह बहुत सारे तर्क को एक में बदल दिया जा सकता है। इससे बचने का तरीका यह है Controllerकि अन्य Controllerएस से बना है , या Controllerकार्यक्षमता बनाने के विभिन्न गुणों पर निर्भर हैं Controller। उदाहरण के लिए, AIControllerइसके साथ एक DecisionTreeअटैचमेंट होगा, और इसे PlayerCharacterControllerविभिन्न अन्य Controllerएस से बनाया जा सकता है जैसे कि ए MovementController, JumpController( राज्यों के साथ एक स्टेट मशीन , ऑनग्रेड, आरोही और अवरोही के साथ), ए InventoryUIController। इसका एक अतिरिक्त लाभ यह है कि नई Controllerएस को जोड़ा जा सकता है क्योंकि नई सुविधाओं को जोड़ा जाता है - अगर कोई गेम इन्वेंट्री सिस्टम के बिना शुरू होता है और एक जोड़ा जाता है, तो इसके लिए एक नियंत्रक को बाद में निपटाया जा सकता है।


मुझे इसका विचार पसंद है लेकिन ऐसा लगता है कि मुझे एक ही मुद्दे के साथ छोड़कर सभी कोड को नियंत्रक वर्ग में स्थानांतरित कर दिया गया है।
user441521

@ user441521 बस एहसास हुआ कि एक अतिरिक्त पैराग्राफ है जिसे मैं जोड़ने जा रहा था, लेकिन जब मेरा ब्राउज़र क्रैश हो गया, तो मैंने इसे खो दिया। मैं इसे अभी जोड़ दूँगा। मूल रूप से, आपके पास अलग-अलग नियंत्रक हो सकते हैं जो उन्हें कुल नियंत्रक में लिख सकते हैं इसलिए प्रत्येक नियंत्रक अलग-अलग चीजों को संभाल रहा है। जैसे AggregateController.Controllers = {JumpController (कीबाइंड), MoveController (कीबाइंड्स), InventoryUIController (कीबाइंड, uisystem)}
चरण
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.