1) खिलाड़ी: राज्य-मशीन + घटक आधारित वास्तुकला।
खिलाड़ी के लिए सामान्य घटक: हेल्थ सिस्टम, मूवमेंट सिस्टम, इन्वेंटरी सिस्टम, एक्शन सिस्टम। वे सभी वर्ग जैसे हैं class HealthSystem
।
मैं Update()
वहां उपयोग करने की अनुशंसा नहीं करता (स्वास्थ्य मामलों में अद्यतन करने के लिए सामान्य मामलों में इसका कोई मतलब नहीं है जब तक कि आपको हर फ्रेम में कुछ कार्यों के लिए इसकी आवश्यकता न हो, ये शायद ही कभी होते हैं। एक मामला जो आप भी सोच सकते हैं - खिलाड़ी जहर हो जाता है और आपको उसकी आवश्यकता है समय-समय पर स्वास्थ्य खोने के लिए - यहाँ मैं सुझाव देता हूं कि आप कोरटाइन का उपयोग करें। एक और लगातार स्वास्थ्य या शक्ति को फिर से जीवित करने के लिए, आप बस वर्तमान स्वास्थ्य या शक्ति लेते हैं और समय आने पर उस स्तर को भरने के लिए कोरटाइन को बुलाते हैं। स्वास्थ्य पूर्ण होने पर कोरटाइन तोड़ दें। वह क्षतिग्रस्त हो गया था या उसने फिर से वगैरह चलाना शुरू कर दिया था। ठीक है कि थोड़ा बहुत ऑफॉपिक था लेकिन मुझे उम्मीद है कि यह उपयोगी था) ।
स्टेट्स: लूटस्टेट, रनस्टेट, वॉकस्टेट, अटैकस्टेट, आईडीलेट।
हर राज्य से विरासत में मिलता है interface IState
। IState
हमारे मामले में सिर्फ एक उदाहरण के लिए 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
, GameActor
। IDamagable
बेशक है 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 पर मुफ्त है।