खेल एक साथ सभी पात्रों को कैसे संभाल सकता है?


31

यह प्रश्न केवल ज्ञान प्राप्त करने के लिए है कि एक खेल एक साथ कई पात्रों को कैसे संभाल सकता है। मैं जुआ खेलने के लिए नया हूं इसलिए मैं आपके क्षमा को अग्रिम रूप से मांगता हूं।

उदाहरण

मैं एक टॉवर रक्षा खेल बना रहा हूं जिसमें 15 टॉवर स्लॉट हैं जहां टॉवर बनाए जाते हैं और प्रत्येक टॉवर एक निश्चित दर पर प्रक्षेप्य को बाहर निकालता है; बता दें कि प्रत्येक सेकंड, 2 प्रोजेक्टाइल प्रत्येक टावरों द्वारा बनाए जाते हैं और युद्ध के मैदान पर मार्च करते हुए दुश्मन होते हैं , 70 को कहते हैं (प्रत्येक 10 प्रकार की विशेषताओं जैसे कि एचपी, मैना, आदि, जो बदलते समय के साथ-साथ बदलते रहेंगे। लड़ाई का मैदान)।

सारांश

टॉवर गणना = 15
प्रोजेक्टाइल प्रति सेकंड प्रत्येक टावर द्वारा बनाए गए = 2
प्रोजेक्टाइल की कुल संख्या प्रति सेकंड = 30
यूनिट्स बैटल काउंट / 70 में बनाई गई

अब, क्या खेल उन 30 प्रोजेक्टाइल और 70 इकाइयों को 100 अलग-अलग थ्रेड्स (जो कि पीसी के लिए बहुत अधिक है) या 1 थ्रेड जो उन सभी को स्थानांतरित करता है, उनके मूल्य को कम कर देता है, आदि को संभालता है। , मुझे लगता है)?

मेरे पास इस बारे में कोई सुराग नहीं है, तो क्या कोई मुझे बता सकता है कि यह कैसे काम करेगा?


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

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

जवाबों:


77

अब खेल उन 30 प्रोजेक्टाइल और 70 इकाइयों को 100 अलग-अलग थ्रेड्स पर कैसे हैंडल करता है

नहीं, ऐसा कभी न करें। प्रति संसाधन एक नया सूत्र कभी न बनाएं, यह नेटवर्किंग में कोई पैमाना नहीं है, न ही इसे अपडेट करने वाली संस्थाओं में। (किसी को वह समय याद है जब आपके पास जावा में सॉकेट पढ़ने के लिए एक धागा था?)

1 धागा जो उन सभी को स्थानांतरित करता है, उनके मूल्य आदि को कम करता है?

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

(जो मुझे लगता है कि धीमी की तरह होगा)

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


34
जब तक आप कुछ अजीब नहीं कर रहे हों, आधे से अधिक माइक्रोसेकंड की तरह। लेकिन सबसे महत्वपूर्ण बात यह है कि कई थ्रेड्स पर काम को विभाजित करने से सब कुछ खराब हो जाएगा , बेहतर नहीं। इस बात का जिक्र नहीं है कि मल्टीथ्रेडिंग बेहद कठिन है।
लुआं

13
+1 अधिकांश आधुनिक गेम इंजन वास्तविक समय में हजारों या दसियों पॉलीगोन का प्रतिपादन कर रहे हैं, जो स्मृति में मात्र 100 वस्तुओं की गति को ट्रैक करने की तुलना में कहीं अधिक गहन है।
फेयरफॉक्स

1
"एक टॉवर रक्षा खेल की तरह एक सरल खेल।" हम्म ... क्या आपने कभी डिफेंस ग्रिड: द जागिंग , या इसकी अगली कड़ी निभाई है ?
मेसन व्हीलर

4
"कभी संसाधन प्रति एक नया धागा बनाने के लिए, इस नेटवर्किंग ... में स्केल नहीं करता है" अहम कुछ बहुत स्केलेबल आर्किटेक्चर ठीक ऐसा करने के!
NPSF3000 4

2
@BarafuAlbino: यह कहने के लिए एक अजीब बात है। उपलब्ध कोर की तुलना में अधिक थ्रेड्स बनाने के लिए पर्याप्त वैध कारण हैं। यह किसी अन्य डिजाइन निर्णय की तरह एक जटिलता / प्रदर्शन / आदि व्यापार-बंद है।
डिट्रिच एप्प

39

मल्टीथ्रेडिंग का नियम नंबर एक है: प्रदर्शन या जवाबदेही के लिए कई सीपीयू कोर पर समानांतर करने की आवश्यकता न होने तक इसका उपयोग न करें । उपयोगकर्ताओं के दृष्टिकोण से एक आवश्यकता "x और y एक साथ होनी चाहिए" मल्टीथ्रेडिंग का उपयोग करने के लिए अभी तक पर्याप्त कारण नहीं है।

क्यूं कर?

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

आमतौर पर ऐसे खेल जो वस्तुओं की इतनी कम संख्या के साथ कुछ सौ (हाँ, यह खेल के विकास में उतना नहीं है) आमतौर पर उन्हें क्रमबद्ध तरीके से प्रत्येक तर्क-टिक का उपयोग करके एक आम forलूप का उपयोग करते हैं ।

यहां तक ​​कि अपेक्षाकृत कमजोर स्मार्टफोन सीपीयू प्रति सेकंड अरबों निर्देशों का प्रदर्शन कर सकते हैं । इसका मतलब है कि तब भी जब आपकी वस्तुओं का अद्यतन तर्क जटिल है और प्रति वस्तु और टिक के बारे में 1000 निर्देश लेता है, और आप प्रति सेकंड एक उदार 100 टिक का लक्ष्य बना रहे हैं, आपके पास दसियों हजारों वस्तुओं के लिए पर्याप्त सीपीयू क्षमता है। हां, यह एक व्यापक रूप से ओवरसाइज़्ड बैक-ऑफ़-द-लिफाफा गणना है, लेकिन यह आपको एक विचार देता है।

इसके अलावा, खेल के विकास में सामान्य ज्ञान यह है कि गेम लॉजिक बहुत कम ही किसी खेल की अड़चन है। प्रदर्शन-महत्वपूर्ण हिस्सा लगभग हमेशा ग्राफिक्स होता है। हां, 2 डी गेम के लिए भी।


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

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

दुर्भाग्यवश, कई बार मैंने गेम लॉजिक को पूरे गेम को देखने के लिए उकसाया है यदि पैथफाइंडिंग समस्याएं हैं।
लोरेन Pechtel

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

1
@LorenPechtel एक टॉवर रक्षा खेल में उदाहरण के लिए, आप इस तथ्य का उपयोग कर सकते हैं कि आमतौर पर केवल कुछ मुट्ठी भर गंतव्य बिंदु होते हैं। इसलिए आप दिशा निर्देश मानचित्र की गणना करने के लिए प्रत्येक गंतव्य के लिए दिज्कस्ट्रा के एल्गोरिथ्म को चला सकते हैं, जो सभी इकाइयों का मार्गदर्शन करता है। यहां तक ​​कि एक गतिशील वातावरण में जहां आपको हर फ्रेम में इन मानचित्रों को फिर से बनाना पड़ता है, यह अभी भी सस्ती होनी चाहिए।
कोडइन्चोस 8

26

अन्य उत्तरों ने आधुनिक कंप्यूटरों के प्रसार और शक्ति को संभाला है। हालांकि बड़े सवाल का समाधान करने के लिए, आप यहां क्या करने की कोशिश कर रहे हैं, "एन स्क्वेर्ड" स्थितियों से बचें।

उदाहरण के लिए यदि आपके पास 1000 प्रोजेक्टाइल और 1000 शत्रु हैं तो भोले समाधान बस एक दूसरे के खिलाफ उन सभी की जांच करना है।

इसका मतलब है कि आप पी * ई = 1,000 * 1,000 = 1,000,000 अलग-अलग चेक के साथ समाप्त करते हैं! यह O (n ^ 2) है।

दूसरी ओर यदि आप अपने डेटा को बेहतर तरीके से व्यवस्थित करते हैं तो आप उससे काफी हद तक बच सकते हैं।

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

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


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

@phyrfox निश्चित रूप से, इसे करने के विभिन्न तरीके हैं - आपके उपयोग-मामले के आधार पर जो बेहतर है वह काफी हद तक भिन्न होगा।
टिम बी।

17

प्रति संसाधन / वस्तु पर धागे का निर्माण न करें बल्कि आपके कार्यक्रम के तर्क के अनुसार। उदाहरण के लिए:

  1. इकाइयों और प्रोजेक्टाइल को अपडेट करने के लिए थ्रेड - लॉजिक थ्रेड
  2. स्क्रीन को प्रस्तुत करने के लिए थ्रेड - GUI थ्रेड
  3. नेटवर्क के लिए धागा (जैसे। मल्टीप्लेयर) - IO धागा

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


1
For a beginner I wouldn't recommend using separate graphic and logic threads, since unless you copy the required data, rendering the game state requires read access to the game state, so you can't modify the game state while drawing it.
CodesInChaos

1
Not drawing too often (eg. more than 50 times per second) is kinda important and this question was about performance. Dividing program is the simplest thing to do for a real performance benefit. It's truth this requires some knowledge about threads, but acquiring that knowledge is worthwhile.
Tomáš Zato - Reinstate Monica

Dividing a Program into multiple Threads is kind of the hardest thing for a programmer to do. Most really annoying bugs stem from multi-threading and it is a giant amount of hassle and most of the time just not worth it - First rule: Check if you have a performance problem, THEN optimize. And optimize right where the bottleneck is. Maybe a single external thread for a certain complex algorithm. But even then you have to think how your game logic will advance when this algorithm takes 3 seconds to finish...
Falco

@Falco You're overseeing the long term advantages of this model - both for the project and the programmer experience. Your claim that it's hardest think can't really be addressed, that's just an opinion. To me GUI design is much more terrifying. All evolved languages (C++, Java) have pretty clear multithreading models. And if you're really not sure, you can use actor model which doesn't suffer from beginner multithreading bugs. You know there's a reason why most applications are designed as I proposed, but feel free to argue about it further.
Tomáš Zato - Reinstate Monica

4

Even Space Invaders managed dozens of interacting objects. Whereas decoding one frame of HD H264 video involves hundreds of millions of arithmetic operations. You have a lot of processing power available.

That said, you can still make it slow if you waste it. The problem is not so much the number of objects as the number of collision tests performed; the simple approach of checking each object against each other object squares the number of calculations required. Testing 1001 objects for collisions this way would require a million comparisons. Often this is addressed by e.g. not checking projectiles for collision with each other.


2
I'm not sure Space Invaders is the best comparison to make. The reason it starts out slow and speeds up as you kill enemies isn't because it was designed that way, but because the hardware couldn't handle rendering that many enemies at once. en.wikipedia.org/wiki/Space_Invaders#Hardware
Mike Kellogg

What about, each object maintains a list of all objects that are close enough that it might collide with them in the next second, updated once a second or each time they change direction?
Random832

Depends on what you're modelling. Space partitioning solutions are another common approach: partition the world into regions (e.g. BSP which you may have to do anyway for rendering purposes, or quadtree), then you can only collide with objects in the same region.
pjc50

3

I am going to disagree with some of the other answers here. Separate logic threads are not only a good idea, but hugely beneficial to processing speed - if your logic is easily separable.

Your question is a good example of logic that is probably separable if you can add some additional logic on top of it. For example, you could run several hit detection threads either by locking the threads to specific regions of space, or mutexing the objects involved.

You probably do NOT want one thread for every possible collision, just because that is likely to bog down the scheduler; there is also a cost associated with creating and destroying threads. Better to make some number of threads around the system's cores (or utilize a metric like the old #cores * 2 + 4), then reuse them when their process finishes.

Not all logic is easily separable, though. Sometimes your operations can reach across all game data at once, which would make threading useless (in fact, harmful, because you would need to add checks to avoid threading issues). Further, if multiple stages of logic are highly dependent on each other occurring in specific orders, you will have to control the execution of threads in such a way as to ensure that does not give order-dependent results. However, that issue isn't eliminated by not using threads, threads just exacerbate it.

Most games don't do this simply because it is more complex than the average game developer is willing/able to handle for what is usually not the bottleneck in the first place. The vast majority of games are GPU-limited, not CPU-limited. While improving the CPU speed can help overall, it's usually not the focus.

That said, physics engines often employ multiple threads, and I can name several games I think that would have benefited from multiple logic threads (the Paradox RTS games like HOI3 and such, for example).

I do agree with other posts that you probably would have no need to employ threads in this specific example, even if it could be beneficial. Threading should be reserved to cases where you have excessive CPU load that cannot be optimized down via other methods. It is a huge undertaking and will affect the fundamental structure of an engine; it isn't something you can tack on after the fact.


2

I think the other answers miss an important part of the question by focusing too much on the threading part of the question.

A computer doesn't handle all objects in a game at once at all. It handles them in sequence.

A computer game progresses in discrete time-steps. Depending on the game and the speed of the PC, these steps are usually either 30 or 60 steps per second, or as many/few steps as the PC can calculate.

In one such step, a computer calculates what each of the game objects will do during that step and updates them accordingly, one after another. It could even do so in parallel, using threads to be faster, but as we'll soon see speed is not a concern at all.

An average CPU should be 2 GHz or faster, that means 109 clock cycles per second. If we calculate 60 timesteps per second, that leaves 109 / 60 clock cycles = 16,666,666 clock cycles per time step. With 70 units, we still have about 2,400,000 clock cycles per unit left. If we had to optimize, we might be able to update each unit in as little as 240 cycles, depending on the complexity of the game logic. As you can see, our computer is about 10,000 times faster than it needs to be for this task.


0

Disclaimer: My all time favourite type of game is text-based and I write this as a long time programmer of an old MUD.

I think an important question you need to ask yourself is this: Do you even need threads? I understand that a graphical game probably has more use of MTs but I think it also depends on the mechanics of the game. (It might also be worth considering that with GPUs, CPUs and all the other resources we have today are far more powerful which makes your concerns of resources as problematic as it might seem to you; indeed 100 objects is virtually zero). It also depends on how you define 'all characters at once'. Do you mean at the exact same time? You won't have that as Peter rightfully points out so all at once is irrelevant in the literal sense; it only appears this way.

Assuming you will go with threads: You definitely should not consider 100 threads (and I am not even going to get into whether it is too much for your CPU or not; I refer only to the complications and the practicality of it).

But remember this: multiple-threading is not easy (as Philipp points out) and has many problems. Others have much more experience (by a lot) than I do with MT but I would say they too would suggest the same thing (even though they would be more capable than I would be - especially without practise on my part).

Some argue that they disagree that threads aren't beneficial and some argue that each object should have a thread. But (and again this is all text but even if you consider more than one thread you need not - and should not - consider it for each object) as Philipp points out games tend to iterate through the lists. But yet it isn't only (as he suggests although I realise he is only responding to your parameters of so few objects) for so few objects. In the MUD I am a programmer for we have the following (and this isn't all the activity that happens in real-time so keep that in mind too):

(The number of instances do vary of course - higher and lower)

Mobiles (NPC i.e. non player character): 2614; prototypes: 1360 Objects: 4457; prototypes: 2281 Rooms: 7983; prototypes: 7983. Each room has its own instance usually but we also have dynamic rooms which is to say rooms within a room; or rooms inside a mobile e.g. the stomach of a dragon; or rooms in objects e.g. you enter a magical object). Keep in mind that these dynamic rooms exist per object/room/mobile that actually has them defined. Yes this is very much like World of Warcraft's (I don't play it but a friend had me play it when I had a Windows machine, for a while) idea of instances except we had it long before World of Warcraft even existed.

Scripts: 868 (currently) (oddly enough our statistics command doesn't show how many prototypes we have so I will be adding that). All of these are held in areas/zones and we have 103 of those. We also have special procedures that proc at different times. We also have other events. Then we also have connected sockets. Mobiles move around, do different activities (besides combat), have interactions with players, and so on. (So do other types of entities).

How do we handle all this without any delay?

  • sockets: select(), queues (input, output, events, other things), buffers (input, output, other things), etc. These are polled 10 times a second.

  • characters, objects, rooms, combat, everything: all in a central loop on different pulses.

We also (my implementation based on discussion between the founder/other programmer and myself) have extensive linked list tracking and pointer validity testing and we have more than enough free resources should we actually have a need for it. All of this (except we have expanded the world) existed years ago when there was less RAM, CPU power, hard disk space, etc. And indeed even then we had no problems. In the loops described (scripts cause this as do area resets/repopulations as do other things) monsters, objects (items), and other things are being created, freed, and so on. Connections are also accepted, polled, and everything else you would expect.

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